Roo/bootstrap/TriggerField.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         
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                    
224                 }
225             }
226            
227             
228                
229             // if object has flexy:if - then it may or may not be rendered.
230             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
231                 // skip a flexy if element.
232                 Roo.log('skipping render');
233              } else {
234                  
235                 // actually if flexy:foreach is found, we really want to create 
236                 // multiple copies here...
237                 //Roo.log('render');
238                 //Roo.log(this[cntr]());
239                 cn.render(this[cntr](true));
240              }
241             // then add the element..
242         }
243         
244         
245         // handle the kids..
246         
247         var nitems = [];
248         /*
249         if (typeof (tree.menu) != 'undefined') {
250             tree.menu.parentType = cn.xtype;
251             tree.menu.triggerEl = cn.el;
252             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
253             
254         }
255         */
256         if (!tree.items || !tree.items.length) {
257             cn.items = nitems;
258             return cn;
259         }
260         var items = tree.items;
261         delete tree.items;
262         
263         //Roo.log(items.length);
264             // add the items..
265         for(var i =0;i < items.length;i++) {
266             nitems.push(cn.addxtype(Roo.apply({}, items[i])));
267         }
268         
269         cn.items = nitems;
270         
271         return cn;
272     }
273     
274     
275     
276     
277 });
278
279  /*
280  * - LGPL
281  *
282  * Body
283  * 
284  */
285
286 /**
287  * @class Roo.bootstrap.Body
288  * @extends Roo.bootstrap.Component
289  * Bootstrap Body class
290  * 
291  * @constructor
292  * Create a new body
293  * @param {Object} config The config object
294  */
295
296 Roo.bootstrap.Body = function(config){
297     Roo.bootstrap.Body.superclass.constructor.call(this, config);
298     this.el = Roo.get(document.body);
299     if (this.cls && this.cls.length) {
300         Roo.get(document.body).addClass(this.cls);
301     }
302 };
303
304 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
305       
306         autoCreate : {
307         cls: 'container'
308     },
309     onRender : function(ct, position)
310     {
311        /* Roo.log("Roo.bootstrap.Body - onRender");
312         if (this.cls && this.cls.length) {
313             Roo.get(document.body).addClass(this.cls);
314         }
315         // style??? xttr???
316         */
317     }
318     
319     
320  
321    
322 });
323
324  /*
325  * - LGPL
326  *
327  * button group
328  * 
329  */
330
331
332 /**
333  * @class Roo.bootstrap.ButtonGroup
334  * @extends Roo.bootstrap.Component
335  * Bootstrap ButtonGroup class
336  * @cfg {String} size lg | sm | xs (default empty normal)
337  * @cfg {String} align vertical | justified  (default none)
338  * @cfg {String} direction up | down (default down)
339  * @cfg {Boolean} toolbar false | true
340  * @cfg {Boolean} btn true | false
341  * 
342  * 
343  * @constructor
344  * Create a new Input
345  * @param {Object} config The config object
346  */
347
348 Roo.bootstrap.ButtonGroup = function(config){
349     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
350 };
351
352 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
353     
354     size: '',
355     align: '',
356     direction: '',
357     toolbar: false,
358     btn: true,
359
360     getAutoCreate : function(){
361         var cfg = {
362             cls: 'btn-group',
363             html : null
364         }
365         
366         cfg.html = this.html || cfg.html;
367         
368         if (this.toolbar) {
369             cfg = {
370                 cls: 'btn-toolbar',
371                 html: null
372             }
373             
374             return cfg;
375         }
376         
377         if (['vertical','justified'].indexOf(this.align)!==-1) {
378             cfg.cls = 'btn-group-' + this.align;
379             
380             if (this.align == 'justified') {
381                 console.log(this.items);
382             }
383         }
384         
385         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
386             cfg.cls += ' btn-group-' + this.size;
387         }
388         
389         if (this.direction == 'up') {
390             cfg.cls += ' dropup' ;
391         }
392         
393         return cfg;
394     }
395    
396 });
397
398  /*
399  * - LGPL
400  *
401  * button
402  * 
403  */
404
405 /**
406  * @class Roo.bootstrap.Button
407  * @extends Roo.bootstrap.Component
408  * Bootstrap Button class
409  * @cfg {String} html The button content
410  * @cfg {String} weight default (or empty) | primary | success | info | warning | danger | link
411  * @cfg {String} size empty | lg | sm | xs
412  * @cfg {String} tag empty | a | input | submit
413  * @cfg {String} href empty or href
414  * @cfg {Boolean} disabled false | true
415  * @cfg {Boolean} isClose false | true
416  * @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
417  * @cfg {String} badge text for badge
418  * @cfg {String} theme default (or empty) | glow
419  * @cfg {Boolean} inverse false | true
420  * @cfg {Boolean} toggle false | true
421  * @cfg {String} ontext text for on toggle state
422  * @cfg {String} offtext text for off toggle state
423  * @cfg {Boolean} defaulton true | false
424  * @cfg {Boolean} preventDefault (true | false) default true
425  * @cfg {Boolean} removeClass true | false remove the standard class..
426  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
427  * 
428  * @constructor
429  * Create a new button
430  * @param {Object} config The config object
431  */
432
433
434 Roo.bootstrap.Button = function(config){
435     Roo.bootstrap.Button.superclass.constructor.call(this, config);
436     this.addEvents({
437         // raw events
438         /**
439          * @event click
440          * When a butotn is pressed
441          * @param {Roo.EventObject} e
442          */
443         "click" : true,
444          /**
445          * @event toggle
446          * After the button has been toggles
447          * @param {Roo.EventObject} e
448          * @param {boolean} pressed (also available as button.pressed)
449          */
450         "toggle" : true
451     });
452 };
453
454 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
455     html: false,
456     active: false,
457     weight: '',
458     size: '',
459     tag: 'button',
460     href: '',
461     disabled: false,
462     isClose: false,
463     glyphicon: '',
464     badge: '',
465     theme: 'default',
466     inverse: false,
467     
468     toggle: false,
469     ontext: 'ON',
470     offtext: 'OFF',
471     defaulton: true,
472     preventDefault: true,
473     removeClass: false,
474     name: false,
475     target: false,
476     
477     
478     pressed : null,
479      
480     
481     getAutoCreate : function(){
482         
483         var cfg = {
484             tag : 'button',
485             cls : 'roo-button',
486             html: ''
487         };
488         
489         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
490             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
491             this.tag = 'button';
492         } else {
493             cfg.tag = this.tag;
494         }
495         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
496         
497         if (this.toggle == true) {
498             cfg={
499                 tag: 'div',
500                 cls: 'slider-frame roo-button',
501                 cn: [
502                     {
503                         tag: 'span',
504                         'data-on-text':'ON',
505                         'data-off-text':'OFF',
506                         cls: 'slider-button',
507                         html: this.offtext
508                     }
509                 ]
510             };
511             
512             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
513                 cfg.cls += ' '+this.weight;
514             }
515             
516             return cfg;
517         }
518         
519         if (this.isClose) {
520             cfg.cls += ' close';
521             
522             cfg["aria-hidden"] = true;
523             
524             cfg.html = "&times;";
525             
526             return cfg;
527         }
528         
529          
530         if (this.theme==='default') {
531             cfg.cls = 'btn roo-button';
532             
533             //if (this.parentType != 'Navbar') {
534             this.weight = this.weight.length ?  this.weight : 'default';
535             //}
536             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
537                 
538                 cfg.cls += ' btn-' + this.weight;
539             }
540         } else if (this.theme==='glow') {
541             
542             cfg.tag = 'a';
543             cfg.cls = 'btn-glow roo-button';
544             
545             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
546                 
547                 cfg.cls += ' ' + this.weight;
548             }
549         }
550    
551         
552         if (this.inverse) {
553             this.cls += ' inverse';
554         }
555         
556         
557         if (this.active) {
558             cfg.cls += ' active';
559         }
560         
561         if (this.disabled) {
562             cfg.disabled = 'disabled';
563         }
564         
565         if (this.items) {
566             Roo.log('changing to ul' );
567             cfg.tag = 'ul';
568             this.glyphicon = 'caret';
569         }
570         
571         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
572          
573         //gsRoo.log(this.parentType);
574         if (this.parentType === 'Navbar' && !this.parent().bar) {
575             Roo.log('changing to li?');
576             
577             cfg.tag = 'li';
578             
579             cfg.cls = '';
580             cfg.cn =  [{
581                 tag : 'a',
582                 cls : 'roo-button',
583                 html : this.html,
584                 href : this.href || '#'
585             }];
586             if (this.menu) {
587                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
588                 cfg.cls += ' dropdown';
589             }   
590             
591             delete cfg.html;
592             
593         }
594         
595        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
596         
597         if (this.glyphicon) {
598             cfg.html = ' ' + cfg.html;
599             
600             cfg.cn = [
601                 {
602                     tag: 'span',
603                     cls: 'glyphicon glyphicon-' + this.glyphicon
604                 }
605             ];
606         }
607         
608         if (this.badge) {
609             cfg.html += ' ';
610             
611             cfg.tag = 'a';
612             
613 //            cfg.cls='btn roo-button';
614             
615             cfg.href=this.href;
616             
617             var value = cfg.html;
618             
619             if(this.glyphicon){
620                 value = {
621                             tag: 'span',
622                             cls: 'glyphicon glyphicon-' + this.glyphicon,
623                             html: this.html
624                         };
625                 
626             }
627             
628             cfg.cn = [
629                 value,
630                 {
631                     tag: 'span',
632                     cls: 'badge',
633                     html: this.badge
634                 }
635             ];
636             
637             cfg.html='';
638         }
639         
640         if (this.menu) {
641             cfg.cls += ' dropdown';
642             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
643         }
644         
645         if (cfg.tag !== 'a' && this.href !== '') {
646             throw "Tag must be a to set href.";
647         } else if (this.href.length > 0) {
648             cfg.href = this.href;
649         }
650         
651         if(this.removeClass){
652             cfg.cls = '';
653         }
654         
655         if(this.target){
656             cfg.target = this.target;
657         }
658         
659         return cfg;
660     },
661     initEvents: function() {
662        // Roo.log('init events?');
663 //        Roo.log(this.el.dom);
664         // add the menu...
665         
666         if (typeof (this.menu) != 'undefined') {
667             this.menu.parentType = this.xtype;
668             this.menu.triggerEl = this.el;
669             this.addxtype(Roo.apply({}, this.menu));
670         }
671
672
673        if (this.el.hasClass('roo-button')) {
674             this.el.on('click', this.onClick, this);
675        } else {
676             this.el.select('.roo-button').on('click', this.onClick, this);
677        }
678        
679        if(this.removeClass){
680            this.el.on('click', this.onClick, this);
681        }
682        
683        this.el.enableDisplayMode();
684         
685     },
686     onClick : function(e)
687     {
688         if (this.disabled) {
689             return;
690         }
691         
692         Roo.log('button on click ');
693         if(this.preventDefault){
694             e.preventDefault();
695         }
696         if (this.pressed === true || this.pressed === false) {
697             this.pressed = !this.pressed;
698             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
699             this.fireEvent('toggle', this, e, this.pressed);
700         }
701         
702         
703         this.fireEvent('click', this, e);
704     },
705     
706     /**
707      * Enables this button
708      */
709     enable : function()
710     {
711         this.disabled = false;
712         this.el.removeClass('disabled');
713     },
714     
715     /**
716      * Disable this button
717      */
718     disable : function()
719     {
720         this.disabled = true;
721         this.el.addClass('disabled');
722     },
723      /**
724      * sets the active state on/off, 
725      * @param {Boolean} state (optional) Force a particular state
726      */
727     setActive : function(v) {
728         
729         this.el[v ? 'addClass' : 'removeClass']('active');
730     },
731      /**
732      * toggles the current active state 
733      */
734     toggleActive : function()
735     {
736        var active = this.el.hasClass('active');
737        this.setActive(!active);
738        
739         
740     },
741     setText : function(str)
742     {
743         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
744     },
745     getText : function()
746     {
747         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
748     },
749     hide: function() {
750        
751      
752         this.el.hide();   
753     },
754     show: function() {
755        
756         this.el.show();   
757     }
758     
759     
760 });
761
762  /*
763  * - LGPL
764  *
765  * column
766  * 
767  */
768
769 /**
770  * @class Roo.bootstrap.Column
771  * @extends Roo.bootstrap.Component
772  * Bootstrap Column class
773  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
774  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
775  * @cfg {Number} md colspan out of 12 for computer-sized screens
776  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
777  * @cfg {String} html content of column.
778  * 
779  * @constructor
780  * Create a new Column
781  * @param {Object} config The config object
782  */
783
784 Roo.bootstrap.Column = function(config){
785     Roo.bootstrap.Column.superclass.constructor.call(this, config);
786 };
787
788 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
789     
790     xs: null,
791     sm: null,
792     md: null,
793     lg: null,
794     html: '',
795     offset: 0,
796     
797     getAutoCreate : function(){
798         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
799         
800         cfg = {
801             tag: 'div',
802             cls: 'column'
803         };
804         
805         var settings=this;
806         ['xs','sm','md','lg'].map(function(size){
807             if (settings[size]) {
808                 cfg.cls += ' col-' + size + '-' + settings[size];
809             }
810         });
811         if (this.html.length) {
812             cfg.html = this.html;
813         }
814         
815         return cfg;
816     }
817    
818 });
819
820  
821
822  /*
823  * - LGPL
824  *
825  * page container.
826  * 
827  */
828
829
830 /**
831  * @class Roo.bootstrap.Container
832  * @extends Roo.bootstrap.Component
833  * Bootstrap Container class
834  * @cfg {Boolean} jumbotron is it a jumbotron element
835  * @cfg {String} html content of element
836  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
837  * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
838  * @cfg {String} header content of header (for panel)
839  * @cfg {String} footer content of footer (for panel)
840  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
841  * @cfg {String} tag (header|aside|section) type of HTML tag.
842
843  *     
844  * @constructor
845  * Create a new Container
846  * @param {Object} config The config object
847  */
848
849 Roo.bootstrap.Container = function(config){
850     Roo.bootstrap.Container.superclass.constructor.call(this, config);
851 };
852
853 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
854     
855     jumbotron : false,
856     well: '',
857     panel : '',
858     header: '',
859     footer : '',
860     sticky: '',
861     tag : false,
862   
863      
864     getChildContainer : function() {
865         
866         if(!this.el){
867             return false;
868         }
869         
870         if (this.panel.length) {
871             return this.el.select('.panel-body',true).first();
872         }
873         
874         return this.el;
875     },
876     
877     
878     getAutoCreate : function(){
879         
880         var cfg = {
881             tag : this.tag || 'div',
882             html : '',
883             cls : ''
884         };
885         if (this.jumbotron) {
886             cfg.cls = 'jumbotron';
887         }
888         // - this is applied by the parent..
889         //if (this.cls) {
890         //    cfg.cls = this.cls + '';
891         //}
892         
893         if (this.sticky.length) {
894             
895             var bd = Roo.get(document.body);
896             if (!bd.hasClass('bootstrap-sticky')) {
897                 bd.addClass('bootstrap-sticky');
898                 Roo.select('html',true).setStyle('height', '100%');
899             }
900              
901             cfg.cls += 'bootstrap-sticky-' + this.sticky;
902         }
903         
904         
905         if (this.well.length) {
906             switch (this.well) {
907                 case 'lg':
908                 case 'sm':
909                     cfg.cls +=' well well-' +this.well;
910                     break;
911                 default:
912                     cfg.cls +=' well';
913                     break;
914             }
915         }
916         
917         var body = cfg;
918         
919         if (this.panel.length) {
920             cfg.cls += ' panel panel-' + this.panel;
921             cfg.cn = [];
922             if (this.header.length) {
923                 cfg.cn.push({
924                     
925                     cls : 'panel-heading',
926                     cn : [{
927                         tag: 'h3',
928                         cls : 'panel-title',
929                         html : this.header
930                     }]
931                     
932                 });
933             }
934             body = false;
935             cfg.cn.push({
936                 cls : 'panel-body',
937                 html : this.html
938             });
939             
940             
941             if (this.footer.length) {
942                 cfg.cn.push({
943                     cls : 'panel-footer',
944                     html : this.footer
945                     
946                 });
947             }
948             
949         }
950         
951         if (body) {
952             body.html = this.html || cfg.html;
953         }
954         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
955             cfg.cls =  'container';
956         }
957         
958         return cfg;
959     },
960     
961     titleEl : function()
962     {
963         if(!this.el || !this.panel.length || !this.header.length){
964             return;
965         }
966         
967         return this.el.select('.panel-title',true).first();
968     },
969     
970     setTitle : function(v)
971     {
972         var titleEl = this.titleEl();
973         
974         if(!titleEl){
975             return;
976         }
977         
978         titleEl.dom.innerHTML = v;
979     },
980     
981     getTitle : function()
982     {
983         
984         var titleEl = this.titleEl();
985         
986         if(!titleEl){
987             return '';
988         }
989         
990         return titleEl.dom.innerHTML;
991     }
992    
993 });
994
995  /*
996  * - LGPL
997  *
998  * image
999  * 
1000  */
1001
1002
1003 /**
1004  * @class Roo.bootstrap.Img
1005  * @extends Roo.bootstrap.Component
1006  * Bootstrap Img class
1007  * @cfg {Boolean} imgResponsive false | true
1008  * @cfg {String} border rounded | circle | thumbnail
1009  * @cfg {String} src image source
1010  * @cfg {String} alt image alternative text
1011  * @cfg {String} href a tag href
1012  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1013  * 
1014  * @constructor
1015  * Create a new Input
1016  * @param {Object} config The config object
1017  */
1018
1019 Roo.bootstrap.Img = function(config){
1020     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1021     
1022     this.addEvents({
1023         // img events
1024         /**
1025          * @event click
1026          * The img click event for the img.
1027          * @param {Roo.EventObject} e
1028          */
1029         "click" : true
1030     });
1031 };
1032
1033 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1034     
1035     imgResponsive: true,
1036     border: '',
1037     src: '',
1038     href: false,
1039     target: false,
1040
1041     getAutoCreate : function(){
1042         
1043         var cfg = {
1044             tag: 'img',
1045             cls: (this.imgResponsive) ? 'img-responsive' : '',
1046             html : null
1047         }
1048         
1049         cfg.html = this.html || cfg.html;
1050         
1051         cfg.src = this.src || cfg.src;
1052         
1053         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1054             cfg.cls += ' img-' + this.border;
1055         }
1056         
1057         if(this.alt){
1058             cfg.alt = this.alt;
1059         }
1060         
1061         if(this.href){
1062             var a = {
1063                 tag: 'a',
1064                 href: this.href,
1065                 cn: [
1066                     cfg
1067                 ]
1068             }
1069             
1070             if(this.target){
1071                 a.target = this.target;
1072             }
1073             
1074         }
1075         
1076         
1077         return (this.href) ? a : cfg;
1078     },
1079     
1080     initEvents: function() {
1081         
1082         if(!this.href){
1083             this.el.on('click', this.onClick, this);
1084         }
1085     },
1086     
1087     onClick : function(e)
1088     {
1089         Roo.log('img onclick');
1090         this.fireEvent('click', this, e);
1091     }
1092    
1093 });
1094
1095  /*
1096  * - LGPL
1097  *
1098  * image
1099  * 
1100  */
1101
1102
1103 /**
1104  * @class Roo.bootstrap.Link
1105  * @extends Roo.bootstrap.Component
1106  * Bootstrap Link Class
1107  * @cfg {String} alt image alternative text
1108  * @cfg {String} href a tag href
1109  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1110  * @cfg {String} html the content of the link.
1111  * @cfg {Boolean} preventDefault (true | false) default false
1112
1113  * 
1114  * @constructor
1115  * Create a new Input
1116  * @param {Object} config The config object
1117  */
1118
1119 Roo.bootstrap.Link = function(config){
1120     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1121     
1122     this.addEvents({
1123         // img events
1124         /**
1125          * @event click
1126          * The img click event for the img.
1127          * @param {Roo.EventObject} e
1128          */
1129         "click" : true
1130     });
1131 };
1132
1133 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1134     
1135     href: false,
1136     target: false,
1137     preventDefault: false,
1138
1139     getAutoCreate : function(){
1140         
1141         var cfg = {
1142             tag: 'a',
1143             html : this.html || 'html-missing'
1144         }
1145         
1146         
1147         if(this.alt){
1148             cfg.alt = this.alt;
1149         }
1150         cfg.href = this.href || '#';
1151         if(this.target){
1152             cfg.target = this.target;
1153         }
1154         
1155         return cfg;
1156     },
1157     
1158     initEvents: function() {
1159         
1160         if(!this.href || this.preventDefault){
1161             this.el.on('click', this.onClick, this);
1162         }
1163     },
1164     
1165     onClick : function(e)
1166     {
1167         if(this.preventDefault){
1168             e.preventDefault();
1169         }
1170         //Roo.log('img onclick');
1171         this.fireEvent('click', this, e);
1172     }
1173    
1174 });
1175
1176  /*
1177  * - LGPL
1178  *
1179  * header
1180  * 
1181  */
1182
1183 /**
1184  * @class Roo.bootstrap.Header
1185  * @extends Roo.bootstrap.Component
1186  * Bootstrap Header class
1187  * @cfg {String} html content of header
1188  * @cfg {Number} level (1|2|3|4|5|6) default 1
1189  * 
1190  * @constructor
1191  * Create a new Header
1192  * @param {Object} config The config object
1193  */
1194
1195
1196 Roo.bootstrap.Header  = function(config){
1197     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1198 };
1199
1200 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1201     
1202     //href : false,
1203     html : false,
1204     level : 1,
1205     
1206     
1207     
1208     getAutoCreate : function(){
1209         
1210         var cfg = {
1211             tag: 'h' + (1 *this.level),
1212             html: this.html || 'fill in html'
1213         } ;
1214         
1215         return cfg;
1216     }
1217    
1218 });
1219
1220  
1221
1222  /*
1223  * Based on:
1224  * Ext JS Library 1.1.1
1225  * Copyright(c) 2006-2007, Ext JS, LLC.
1226  *
1227  * Originally Released Under LGPL - original licence link has changed is not relivant.
1228  *
1229  * Fork - LGPL
1230  * <script type="text/javascript">
1231  */
1232  
1233 /**
1234  * @class Roo.bootstrap.MenuMgr
1235  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1236  * @singleton
1237  */
1238 Roo.bootstrap.MenuMgr = function(){
1239    var menus, active, groups = {}, attached = false, lastShow = new Date();
1240
1241    // private - called when first menu is created
1242    function init(){
1243        menus = {};
1244        active = new Roo.util.MixedCollection();
1245        Roo.get(document).addKeyListener(27, function(){
1246            if(active.length > 0){
1247                hideAll();
1248            }
1249        });
1250    }
1251
1252    // private
1253    function hideAll(){
1254        if(active && active.length > 0){
1255            var c = active.clone();
1256            c.each(function(m){
1257                m.hide();
1258            });
1259        }
1260    }
1261
1262    // private
1263    function onHide(m){
1264        active.remove(m);
1265        if(active.length < 1){
1266            Roo.get(document).un("mouseup", onMouseDown);
1267             
1268            attached = false;
1269        }
1270    }
1271
1272    // private
1273    function onShow(m){
1274        var last = active.last();
1275        lastShow = new Date();
1276        active.add(m);
1277        if(!attached){
1278           Roo.get(document).on("mouseup", onMouseDown);
1279            
1280            attached = true;
1281        }
1282        if(m.parentMenu){
1283           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1284           m.parentMenu.activeChild = m;
1285        }else if(last && last.isVisible()){
1286           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1287        }
1288    }
1289
1290    // private
1291    function onBeforeHide(m){
1292        if(m.activeChild){
1293            m.activeChild.hide();
1294        }
1295        if(m.autoHideTimer){
1296            clearTimeout(m.autoHideTimer);
1297            delete m.autoHideTimer;
1298        }
1299    }
1300
1301    // private
1302    function onBeforeShow(m){
1303        var pm = m.parentMenu;
1304        if(!pm && !m.allowOtherMenus){
1305            hideAll();
1306        }else if(pm && pm.activeChild && active != m){
1307            pm.activeChild.hide();
1308        }
1309    }
1310
1311    // private
1312    function onMouseDown(e){
1313         Roo.log("on MouseDown");
1314         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
1315            hideAll();
1316         }
1317         
1318         
1319    }
1320
1321    // private
1322    function onBeforeCheck(mi, state){
1323        if(state){
1324            var g = groups[mi.group];
1325            for(var i = 0, l = g.length; i < l; i++){
1326                if(g[i] != mi){
1327                    g[i].setChecked(false);
1328                }
1329            }
1330        }
1331    }
1332
1333    return {
1334
1335        /**
1336         * Hides all menus that are currently visible
1337         */
1338        hideAll : function(){
1339             hideAll();  
1340        },
1341
1342        // private
1343        register : function(menu){
1344            if(!menus){
1345                init();
1346            }
1347            menus[menu.id] = menu;
1348            menu.on("beforehide", onBeforeHide);
1349            menu.on("hide", onHide);
1350            menu.on("beforeshow", onBeforeShow);
1351            menu.on("show", onShow);
1352            var g = menu.group;
1353            if(g && menu.events["checkchange"]){
1354                if(!groups[g]){
1355                    groups[g] = [];
1356                }
1357                groups[g].push(menu);
1358                menu.on("checkchange", onCheck);
1359            }
1360        },
1361
1362         /**
1363          * Returns a {@link Roo.menu.Menu} object
1364          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1365          * be used to generate and return a new Menu instance.
1366          */
1367        get : function(menu){
1368            if(typeof menu == "string"){ // menu id
1369                return menus[menu];
1370            }else if(menu.events){  // menu instance
1371                return menu;
1372            }
1373            /*else if(typeof menu.length == 'number'){ // array of menu items?
1374                return new Roo.bootstrap.Menu({items:menu});
1375            }else{ // otherwise, must be a config
1376                return new Roo.bootstrap.Menu(menu);
1377            }
1378            */
1379            return false;
1380        },
1381
1382        // private
1383        unregister : function(menu){
1384            delete menus[menu.id];
1385            menu.un("beforehide", onBeforeHide);
1386            menu.un("hide", onHide);
1387            menu.un("beforeshow", onBeforeShow);
1388            menu.un("show", onShow);
1389            var g = menu.group;
1390            if(g && menu.events["checkchange"]){
1391                groups[g].remove(menu);
1392                menu.un("checkchange", onCheck);
1393            }
1394        },
1395
1396        // private
1397        registerCheckable : function(menuItem){
1398            var g = menuItem.group;
1399            if(g){
1400                if(!groups[g]){
1401                    groups[g] = [];
1402                }
1403                groups[g].push(menuItem);
1404                menuItem.on("beforecheckchange", onBeforeCheck);
1405            }
1406        },
1407
1408        // private
1409        unregisterCheckable : function(menuItem){
1410            var g = menuItem.group;
1411            if(g){
1412                groups[g].remove(menuItem);
1413                menuItem.un("beforecheckchange", onBeforeCheck);
1414            }
1415        }
1416    };
1417 }();/*
1418  * - LGPL
1419  *
1420  * menu
1421  * 
1422  */
1423
1424 /**
1425  * @class Roo.bootstrap.Menu
1426  * @extends Roo.bootstrap.Component
1427  * Bootstrap Menu class - container for MenuItems
1428  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1429  * 
1430  * @constructor
1431  * Create a new Menu
1432  * @param {Object} config The config object
1433  */
1434
1435
1436 Roo.bootstrap.Menu = function(config){
1437     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1438     if (this.registerMenu) {
1439         Roo.bootstrap.MenuMgr.register(this);
1440     }
1441     this.addEvents({
1442         /**
1443          * @event beforeshow
1444          * Fires before this menu is displayed
1445          * @param {Roo.menu.Menu} this
1446          */
1447         beforeshow : true,
1448         /**
1449          * @event beforehide
1450          * Fires before this menu is hidden
1451          * @param {Roo.menu.Menu} this
1452          */
1453         beforehide : true,
1454         /**
1455          * @event show
1456          * Fires after this menu is displayed
1457          * @param {Roo.menu.Menu} this
1458          */
1459         show : true,
1460         /**
1461          * @event hide
1462          * Fires after this menu is hidden
1463          * @param {Roo.menu.Menu} this
1464          */
1465         hide : true,
1466         /**
1467          * @event click
1468          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1469          * @param {Roo.menu.Menu} this
1470          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1471          * @param {Roo.EventObject} e
1472          */
1473         click : true,
1474         /**
1475          * @event mouseover
1476          * Fires when the mouse is hovering over this menu
1477          * @param {Roo.menu.Menu} this
1478          * @param {Roo.EventObject} e
1479          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1480          */
1481         mouseover : true,
1482         /**
1483          * @event mouseout
1484          * Fires when the mouse exits this menu
1485          * @param {Roo.menu.Menu} this
1486          * @param {Roo.EventObject} e
1487          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1488          */
1489         mouseout : true,
1490         /**
1491          * @event itemclick
1492          * Fires when a menu item contained in this menu is clicked
1493          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1494          * @param {Roo.EventObject} e
1495          */
1496         itemclick: true
1497     });
1498     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1499 };
1500
1501 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1502     
1503    /// html : false,
1504     //align : '',
1505     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1506     type: false,
1507     /**
1508      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1509      */
1510     registerMenu : true,
1511     
1512     menuItems :false, // stores the menu items..
1513     
1514     hidden:true,
1515     
1516     parentMenu : false,
1517     
1518     getChildContainer : function() {
1519         return this.el;  
1520     },
1521     
1522     getAutoCreate : function(){
1523          
1524         //if (['right'].indexOf(this.align)!==-1) {
1525         //    cfg.cn[1].cls += ' pull-right'
1526         //}
1527         
1528         
1529         var cfg = {
1530             tag : 'ul',
1531             cls : 'dropdown-menu' ,
1532             style : 'z-index:1000'
1533             
1534         }
1535         
1536         if (this.type === 'submenu') {
1537             cfg.cls = 'submenu active';
1538         }
1539         if (this.type === 'treeview') {
1540             cfg.cls = 'treeview-menu';
1541         }
1542         
1543         return cfg;
1544     },
1545     initEvents : function() {
1546         
1547        // Roo.log("ADD event");
1548        // Roo.log(this.triggerEl.dom);
1549         this.triggerEl.on('click', this.onTriggerPress, this);
1550         this.triggerEl.addClass('dropdown-toggle');
1551         this.el.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
1552
1553         this.el.on("mouseover", this.onMouseOver, this);
1554         this.el.on("mouseout", this.onMouseOut, this);
1555         
1556         
1557     },
1558     findTargetItem : function(e){
1559         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
1560         if(!t){
1561             return false;
1562         }
1563         //Roo.log(t);         Roo.log(t.id);
1564         if(t && t.id){
1565             //Roo.log(this.menuitems);
1566             return this.menuitems.get(t.id);
1567             
1568             //return this.items.get(t.menuItemId);
1569         }
1570         
1571         return false;
1572     },
1573     onClick : function(e){
1574         Roo.log("menu.onClick");
1575         var t = this.findTargetItem(e);
1576         if(!t){
1577             return;
1578         }
1579         Roo.log(e);
1580         /*
1581         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
1582             if(t == this.activeItem && t.shouldDeactivate(e)){
1583                 this.activeItem.deactivate();
1584                 delete this.activeItem;
1585                 return;
1586             }
1587             if(t.canActivate){
1588                 this.setActiveItem(t, true);
1589             }
1590             return;
1591             
1592             
1593         }
1594         */
1595         Roo.log('pass click event');
1596         
1597         t.onClick(e);
1598         
1599         this.fireEvent("click", this, t, e);
1600         
1601         this.hide();
1602     },
1603      onMouseOver : function(e){
1604         var t  = this.findTargetItem(e);
1605         //Roo.log(t);
1606         //if(t){
1607         //    if(t.canActivate && !t.disabled){
1608         //        this.setActiveItem(t, true);
1609         //    }
1610         //}
1611         
1612         this.fireEvent("mouseover", this, e, t);
1613     },
1614     isVisible : function(){
1615         return !this.hidden;
1616     },
1617      onMouseOut : function(e){
1618         var t  = this.findTargetItem(e);
1619         
1620         //if(t ){
1621         //    if(t == this.activeItem && t.shouldDeactivate(e)){
1622         //        this.activeItem.deactivate();
1623         //        delete this.activeItem;
1624         //    }
1625         //}
1626         this.fireEvent("mouseout", this, e, t);
1627     },
1628     
1629     
1630     /**
1631      * Displays this menu relative to another element
1632      * @param {String/HTMLElement/Roo.Element} element The element to align to
1633      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
1634      * the element (defaults to this.defaultAlign)
1635      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1636      */
1637     show : function(el, pos, parentMenu){
1638         this.parentMenu = parentMenu;
1639         if(!this.el){
1640             this.render();
1641         }
1642         this.fireEvent("beforeshow", this);
1643         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
1644     },
1645      /**
1646      * Displays this menu at a specific xy position
1647      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
1648      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1649      */
1650     showAt : function(xy, parentMenu, /* private: */_e){
1651         this.parentMenu = parentMenu;
1652         if(!this.el){
1653             this.render();
1654         }
1655         if(_e !== false){
1656             this.fireEvent("beforeshow", this);
1657             
1658             //xy = this.el.adjustForConstraints(xy);
1659         }
1660         //this.el.setXY(xy);
1661         //this.el.show();
1662         this.hideMenuItems();
1663         this.hidden = false;
1664         this.triggerEl.addClass('open');
1665         this.focus();
1666         this.fireEvent("show", this);
1667     },
1668     
1669     focus : function(){
1670         return;
1671         if(!this.hidden){
1672             this.doFocus.defer(50, this);
1673         }
1674     },
1675
1676     doFocus : function(){
1677         if(!this.hidden){
1678             this.focusEl.focus();
1679         }
1680     },
1681
1682     /**
1683      * Hides this menu and optionally all parent menus
1684      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
1685      */
1686     hide : function(deep){
1687         
1688         this.hideMenuItems();
1689         if(this.el && this.isVisible()){
1690             this.fireEvent("beforehide", this);
1691             if(this.activeItem){
1692                 this.activeItem.deactivate();
1693                 this.activeItem = null;
1694             }
1695             this.triggerEl.removeClass('open');;
1696             this.hidden = true;
1697             this.fireEvent("hide", this);
1698         }
1699         if(deep === true && this.parentMenu){
1700             this.parentMenu.hide(true);
1701         }
1702     },
1703     
1704     onTriggerPress  : function(e)
1705     {
1706         
1707         Roo.log('trigger press');
1708         //Roo.log(e.getTarget());
1709        // Roo.log(this.triggerEl.dom);
1710         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
1711             return;
1712         }
1713         if (this.isVisible()) {
1714             Roo.log('hide');
1715             this.hide();
1716         } else {
1717             this.show(this.triggerEl, false, false);
1718         }
1719         
1720         
1721     },
1722     
1723          
1724        
1725     
1726     hideMenuItems : function()
1727     {
1728         //$(backdrop).remove()
1729         Roo.select('.open',true).each(function(aa) {
1730             
1731             aa.removeClass('open');
1732           //var parent = getParent($(this))
1733           //var relatedTarget = { relatedTarget: this }
1734           
1735            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
1736           //if (e.isDefaultPrevented()) return
1737            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
1738         })
1739     },
1740     addxtypeChild : function (tree, cntr) {
1741         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
1742           
1743         this.menuitems.add(comp);
1744         return comp;
1745
1746     },
1747     getEl : function()
1748     {
1749         Roo.log(this.el);
1750         return this.el;
1751     }
1752 });
1753
1754  
1755  /*
1756  * - LGPL
1757  *
1758  * menu item
1759  * 
1760  */
1761
1762
1763 /**
1764  * @class Roo.bootstrap.MenuItem
1765  * @extends Roo.bootstrap.Component
1766  * Bootstrap MenuItem class
1767  * @cfg {String} html the menu label
1768  * @cfg {String} href the link
1769  * @cfg {Boolean} preventDefault (true | false) default true
1770  * 
1771  * 
1772  * @constructor
1773  * Create a new MenuItem
1774  * @param {Object} config The config object
1775  */
1776
1777
1778 Roo.bootstrap.MenuItem = function(config){
1779     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
1780     this.addEvents({
1781         // raw events
1782         /**
1783          * @event click
1784          * The raw click event for the entire grid.
1785          * @param {Roo.EventObject} e
1786          */
1787         "click" : true
1788     });
1789 };
1790
1791 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
1792     
1793     href : false,
1794     html : false,
1795     preventDefault: true,
1796     
1797     getAutoCreate : function(){
1798         var cfg= {
1799             tag: 'li',
1800             cls: 'dropdown-menu-item',
1801             cn: [
1802                     {
1803                         tag : 'a',
1804                         href : '#',
1805                         html : 'Link'
1806                     }
1807                 ]
1808         };
1809         if (this.parent().type == 'treeview') {
1810             cfg.cls = 'treeview-menu';
1811         }
1812         
1813         cfg.cn[0].href = this.href || cfg.cn[0].href ;
1814         cfg.cn[0].html = this.html || cfg.cn[0].html ;
1815         return cfg;
1816     },
1817     
1818     initEvents: function() {
1819         
1820         //this.el.select('a').on('click', this.onClick, this);
1821         
1822     },
1823     onClick : function(e)
1824     {
1825         Roo.log('item on click ');
1826         //if(this.preventDefault){
1827         //    e.preventDefault();
1828         //}
1829         //this.parent().hideMenuItems();
1830         
1831         this.fireEvent('click', this, e);
1832     },
1833     getEl : function()
1834     {
1835         return this.el;
1836     }
1837 });
1838
1839  
1840
1841  /*
1842  * - LGPL
1843  *
1844  * menu separator
1845  * 
1846  */
1847
1848
1849 /**
1850  * @class Roo.bootstrap.MenuSeparator
1851  * @extends Roo.bootstrap.Component
1852  * Bootstrap MenuSeparator class
1853  * 
1854  * @constructor
1855  * Create a new MenuItem
1856  * @param {Object} config The config object
1857  */
1858
1859
1860 Roo.bootstrap.MenuSeparator = function(config){
1861     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
1862 };
1863
1864 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
1865     
1866     getAutoCreate : function(){
1867         var cfg = {
1868             cls: 'divider',
1869             tag : 'li'
1870         };
1871         
1872         return cfg;
1873     }
1874    
1875 });
1876
1877  
1878
1879  
1880 /*
1881 <div class="modal fade">
1882   <div class="modal-dialog">
1883     <div class="modal-content">
1884       <div class="modal-header">
1885         <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
1886         <h4 class="modal-title">Modal title</h4>
1887       </div>
1888       <div class="modal-body">
1889         <p>One fine body&hellip;</p>
1890       </div>
1891       <div class="modal-footer">
1892         <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
1893         <button type="button" class="btn btn-primary">Save changes</button>
1894       </div>
1895     </div><!-- /.modal-content -->
1896   </div><!-- /.modal-dialog -->
1897 </div><!-- /.modal -->
1898 */
1899 /*
1900  * - LGPL
1901  *
1902  * page contgainer.
1903  * 
1904  */
1905
1906 /**
1907  * @class Roo.bootstrap.Modal
1908  * @extends Roo.bootstrap.Component
1909  * Bootstrap Modal class
1910  * @cfg {String} title Title of dialog
1911  * @cfg {Boolean} specificTitle (true|false) default false
1912  * @cfg {Array} buttons Array of buttons or standard button set..
1913  * @cfg {String} buttonPosition (left|right|center) default right
1914  * 
1915  * @constructor
1916  * Create a new Modal Dialog
1917  * @param {Object} config The config object
1918  */
1919
1920 Roo.bootstrap.Modal = function(config){
1921     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
1922     this.addEvents({
1923         // raw events
1924         /**
1925          * @event btnclick
1926          * The raw btnclick event for the button
1927          * @param {Roo.EventObject} e
1928          */
1929         "btnclick" : true
1930     });
1931     this.buttons = this.buttons || [];
1932 };
1933
1934 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
1935     
1936     title : 'test dialog',
1937    
1938     buttons : false,
1939     
1940     // set on load...
1941     body:  false,
1942     
1943     specificTitle: false,
1944     
1945     buttonPosition: 'right',
1946     
1947     onRender : function(ct, position)
1948     {
1949         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
1950      
1951         if(!this.el){
1952             var cfg = Roo.apply({},  this.getAutoCreate());
1953             cfg.id = Roo.id();
1954             //if(!cfg.name){
1955             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
1956             //}
1957             //if (!cfg.name.length) {
1958             //    delete cfg.name;
1959            // }
1960             if (this.cls) {
1961                 cfg.cls += ' ' + this.cls;
1962             }
1963             if (this.style) {
1964                 cfg.style = this.style;
1965             }
1966             this.el = Roo.get(document.body).createChild(cfg, position);
1967         }
1968         //var type = this.el.dom.type;
1969         
1970         if(this.tabIndex !== undefined){
1971             this.el.dom.setAttribute('tabIndex', this.tabIndex);
1972         }
1973         
1974         
1975         
1976         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
1977         this.maskEl.enableDisplayMode("block");
1978         this.maskEl.hide();
1979         //this.el.addClass("x-dlg-modal");
1980     
1981         if (this.buttons.length) {
1982             Roo.each(this.buttons, function(bb) {
1983                 b = Roo.apply({}, bb);
1984                 b.xns = b.xns || Roo.bootstrap;
1985                 b.xtype = b.xtype || 'Button';
1986                 if (typeof(b.listeners) == 'undefined') {
1987                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
1988                 }
1989                 
1990                 var btn = Roo.factory(b);
1991                 
1992                 btn.onRender(this.el.select('.modal-footer div').first());
1993                 
1994             },this);
1995         }
1996         // render the children.
1997         var nitems = [];
1998         
1999         if(typeof(this.items) != 'undefined'){
2000             var items = this.items;
2001             delete this.items;
2002
2003             for(var i =0;i < items.length;i++) {
2004                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2005             }
2006         }
2007         
2008         this.items = nitems;
2009         
2010         this.body = this.el.select('.modal-body',true).first();
2011         this.close = this.el.select('.modal-header .close', true).first();
2012         this.footer = this.el.select('.modal-footer',true).first();
2013         this.initEvents();
2014         //this.el.addClass([this.fieldClass, this.cls]);
2015         
2016     },
2017     getAutoCreate : function(){
2018         
2019         
2020         var bdy = {
2021                 cls : 'modal-body',
2022                 html : this.html || ''
2023         };
2024         
2025         var title = {
2026             tag: 'h4',
2027             cls : 'modal-title',
2028             html : this.title
2029         };
2030         
2031         if(this.specificTitle){
2032             title = this.title;
2033         };
2034         
2035         return modal = {
2036             cls: "modal fade",
2037             style : 'display: none',
2038             cn : [
2039                 {
2040                     cls: "modal-dialog",
2041                     cn : [
2042                         {
2043                             cls : "modal-content",
2044                             cn : [
2045                                 {
2046                                     cls : 'modal-header',
2047                                     cn : [
2048                                         {
2049                                             tag: 'button',
2050                                             cls : 'close',
2051                                             html : '&times'
2052                                         },
2053                                         title
2054                                     ]
2055                                 },
2056                                 bdy,
2057                                 {
2058                                     cls : 'modal-footer',
2059                                     cn : [
2060                                         {
2061                                             tag: 'div',
2062                                             cls: 'btn-' + this.buttonPosition
2063                                         }
2064                                     ]
2065                                     
2066                                 }
2067                                 
2068                                 
2069                             ]
2070                             
2071                         }
2072                     ]
2073                         
2074                 }
2075             ]
2076             
2077             
2078         };
2079           
2080     },
2081     getChildContainer : function() {
2082          
2083          return this.el.select('.modal-body',true).first();
2084         
2085     },
2086     getButtonContainer : function() {
2087          return this.el.select('.modal-footer div',true).first();
2088         
2089     },
2090     initEvents : function()
2091     {
2092         this.el.select('.modal-header .close').on('click', this.hide, this);
2093 //        
2094 //        this.addxtype(this);
2095     },
2096     show : function() {
2097         
2098         if (!this.rendered) {
2099             this.render();
2100         }
2101        
2102         this.el.addClass('on');
2103         this.el.removeClass('fade');
2104         this.el.setStyle('display', 'block');
2105         Roo.get(document.body).addClass("x-body-masked");
2106         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2107         this.maskEl.show();
2108         this.el.setStyle('zIndex', '10001');
2109         this.fireEvent('show', this);
2110         
2111         
2112     },
2113     hide : function()
2114     {
2115         Roo.log('Modal hide?!');
2116         this.maskEl.hide();
2117         Roo.get(document.body).removeClass("x-body-masked");
2118         this.el.removeClass('on');
2119         this.el.addClass('fade');
2120         this.el.setStyle('display', 'none');
2121         this.fireEvent('hide', this);
2122     },
2123     
2124     addButton : function(str, cb)
2125     {
2126          
2127         
2128         var b = Roo.apply({}, { html : str } );
2129         b.xns = b.xns || Roo.bootstrap;
2130         b.xtype = b.xtype || 'Button';
2131         if (typeof(b.listeners) == 'undefined') {
2132             b.listeners = { click : cb.createDelegate(this)  };
2133         }
2134         
2135         var btn = Roo.factory(b);
2136            
2137         btn.onRender(this.el.select('.modal-footer div').first());
2138         
2139         return btn;   
2140        
2141     },
2142     
2143     setDefaultButton : function(btn)
2144     {
2145         //this.el.select('.modal-footer').()
2146     },
2147     resizeTo: function(w,h)
2148     {
2149         // skip..
2150     },
2151     setContentSize  : function(w, h)
2152     {
2153         
2154     },
2155     onButtonClick: function(btn,e)
2156     {
2157         //Roo.log([a,b,c]);
2158         this.fireEvent('btnclick', btn.name, e);
2159     },
2160     setTitle: function(str) {
2161         this.el.select('.modal-title',true).first().dom.innerHTML = str;
2162         
2163     }
2164 });
2165
2166
2167 Roo.apply(Roo.bootstrap.Modal,  {
2168     /**
2169          * Button config that displays a single OK button
2170          * @type Object
2171          */
2172         OK :  [{
2173             name : 'ok',
2174             weight : 'primary',
2175             html : 'OK'
2176         }], 
2177         /**
2178          * Button config that displays Yes and No buttons
2179          * @type Object
2180          */
2181         YESNO : [
2182             {
2183                 name  : 'no',
2184                 html : 'No'
2185             },
2186             {
2187                 name  :'yes',
2188                 weight : 'primary',
2189                 html : 'Yes'
2190             }
2191         ],
2192         
2193         /**
2194          * Button config that displays OK and Cancel buttons
2195          * @type Object
2196          */
2197         OKCANCEL : [
2198             {
2199                name : 'cancel',
2200                 html : 'Cancel'
2201             },
2202             {
2203                 name : 'ok',
2204                 weight : 'primary',
2205                 html : 'OK'
2206             }
2207         ],
2208         /**
2209          * Button config that displays Yes, No and Cancel buttons
2210          * @type Object
2211          */
2212         YESNOCANCEL : [
2213             {
2214                 name : 'yes',
2215                 weight : 'primary',
2216                 html : 'Yes'
2217             },
2218             {
2219                 name : 'no',
2220                 html : 'No'
2221             },
2222             {
2223                 name : 'cancel',
2224                 html : 'Cancel'
2225             }
2226         ]
2227 });
2228  /*
2229  * - LGPL
2230  *
2231  * messagebox - can be used as a replace
2232  * 
2233  */
2234 /**
2235  * @class Roo.MessageBox
2236  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2237  * Example usage:
2238  *<pre><code>
2239 // Basic alert:
2240 Roo.Msg.alert('Status', 'Changes saved successfully.');
2241
2242 // Prompt for user data:
2243 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2244     if (btn == 'ok'){
2245         // process text value...
2246     }
2247 });
2248
2249 // Show a dialog using config options:
2250 Roo.Msg.show({
2251    title:'Save Changes?',
2252    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2253    buttons: Roo.Msg.YESNOCANCEL,
2254    fn: processResult,
2255    animEl: 'elId'
2256 });
2257 </code></pre>
2258  * @singleton
2259  */
2260 Roo.bootstrap.MessageBox = function(){
2261     var dlg, opt, mask, waitTimer;
2262     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2263     var buttons, activeTextEl, bwidth;
2264
2265     
2266     // private
2267     var handleButton = function(button){
2268         dlg.hide();
2269         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2270     };
2271
2272     // private
2273     var handleHide = function(){
2274         if(opt && opt.cls){
2275             dlg.el.removeClass(opt.cls);
2276         }
2277         //if(waitTimer){
2278         //    Roo.TaskMgr.stop(waitTimer);
2279         //    waitTimer = null;
2280         //}
2281     };
2282
2283     // private
2284     var updateButtons = function(b){
2285         var width = 0;
2286         if(!b){
2287             buttons["ok"].hide();
2288             buttons["cancel"].hide();
2289             buttons["yes"].hide();
2290             buttons["no"].hide();
2291             //dlg.footer.dom.style.display = 'none';
2292             return width;
2293         }
2294         dlg.footer.dom.style.display = '';
2295         for(var k in buttons){
2296             if(typeof buttons[k] != "function"){
2297                 if(b[k]){
2298                     buttons[k].show();
2299                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2300                     width += buttons[k].el.getWidth()+15;
2301                 }else{
2302                     buttons[k].hide();
2303                 }
2304             }
2305         }
2306         return width;
2307     };
2308
2309     // private
2310     var handleEsc = function(d, k, e){
2311         if(opt && opt.closable !== false){
2312             dlg.hide();
2313         }
2314         if(e){
2315             e.stopEvent();
2316         }
2317     };
2318
2319     return {
2320         /**
2321          * Returns a reference to the underlying {@link Roo.BasicDialog} element
2322          * @return {Roo.BasicDialog} The BasicDialog element
2323          */
2324         getDialog : function(){
2325            if(!dlg){
2326                 dlg = new Roo.bootstrap.Modal( {
2327                     //draggable: true,
2328                     //resizable:false,
2329                     //constraintoviewport:false,
2330                     //fixedcenter:true,
2331                     //collapsible : false,
2332                     //shim:true,
2333                     //modal: true,
2334                   //  width:400,
2335                   //  height:100,
2336                     //buttonAlign:"center",
2337                     closeClick : function(){
2338                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2339                             handleButton("no");
2340                         }else{
2341                             handleButton("cancel");
2342                         }
2343                     }
2344                 });
2345                 dlg.render();
2346                 dlg.on("hide", handleHide);
2347                 mask = dlg.mask;
2348                 //dlg.addKeyListener(27, handleEsc);
2349                 buttons = {};
2350                 this.buttons = buttons;
2351                 var bt = this.buttonText;
2352                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2353                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2354                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2355                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2356                 Roo.log(buttons)
2357                 bodyEl = dlg.body.createChild({
2358
2359                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2360                         '<textarea class="roo-mb-textarea"></textarea>' +
2361                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
2362                 });
2363                 msgEl = bodyEl.dom.firstChild;
2364                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2365                 textboxEl.enableDisplayMode();
2366                 textboxEl.addKeyListener([10,13], function(){
2367                     if(dlg.isVisible() && opt && opt.buttons){
2368                         if(opt.buttons.ok){
2369                             handleButton("ok");
2370                         }else if(opt.buttons.yes){
2371                             handleButton("yes");
2372                         }
2373                     }
2374                 });
2375                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2376                 textareaEl.enableDisplayMode();
2377                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2378                 progressEl.enableDisplayMode();
2379                 var pf = progressEl.dom.firstChild;
2380                 if (pf) {
2381                     pp = Roo.get(pf.firstChild);
2382                     pp.setHeight(pf.offsetHeight);
2383                 }
2384                 
2385             }
2386             return dlg;
2387         },
2388
2389         /**
2390          * Updates the message box body text
2391          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2392          * the XHTML-compliant non-breaking space character '&amp;#160;')
2393          * @return {Roo.MessageBox} This message box
2394          */
2395         updateText : function(text){
2396             if(!dlg.isVisible() && !opt.width){
2397                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2398             }
2399             msgEl.innerHTML = text || '&#160;';
2400       
2401             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2402             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2403             var w = Math.max(
2404                     Math.min(opt.width || cw , this.maxWidth), 
2405                     Math.max(opt.minWidth || this.minWidth, bwidth)
2406             );
2407             if(opt.prompt){
2408                 activeTextEl.setWidth(w);
2409             }
2410             if(dlg.isVisible()){
2411                 dlg.fixedcenter = false;
2412             }
2413             // to big, make it scroll. = But as usual stupid IE does not support
2414             // !important..
2415             
2416             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2417                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2418                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2419             } else {
2420                 bodyEl.dom.style.height = '';
2421                 bodyEl.dom.style.overflowY = '';
2422             }
2423             if (cw > w) {
2424                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2425             } else {
2426                 bodyEl.dom.style.overflowX = '';
2427             }
2428             
2429             dlg.setContentSize(w, bodyEl.getHeight());
2430             if(dlg.isVisible()){
2431                 dlg.fixedcenter = true;
2432             }
2433             return this;
2434         },
2435
2436         /**
2437          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
2438          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2439          * @param {Number} value Any number between 0 and 1 (e.g., .5)
2440          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2441          * @return {Roo.MessageBox} This message box
2442          */
2443         updateProgress : function(value, text){
2444             if(text){
2445                 this.updateText(text);
2446             }
2447             if (pp) { // weird bug on my firefox - for some reason this is not defined
2448                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2449             }
2450             return this;
2451         },        
2452
2453         /**
2454          * Returns true if the message box is currently displayed
2455          * @return {Boolean} True if the message box is visible, else false
2456          */
2457         isVisible : function(){
2458             return dlg && dlg.isVisible();  
2459         },
2460
2461         /**
2462          * Hides the message box if it is displayed
2463          */
2464         hide : function(){
2465             if(this.isVisible()){
2466                 dlg.hide();
2467             }  
2468         },
2469
2470         /**
2471          * Displays a new message box, or reinitializes an existing message box, based on the config options
2472          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2473          * The following config object properties are supported:
2474          * <pre>
2475 Property    Type             Description
2476 ----------  ---------------  ------------------------------------------------------------------------------------
2477 animEl            String/Element   An id or Element from which the message box should animate as it opens and
2478                                    closes (defaults to undefined)
2479 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
2480                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
2481 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
2482                                    progress and wait dialogs will ignore this property and always hide the
2483                                    close button as they can only be closed programmatically.
2484 cls               String           A custom CSS class to apply to the message box element
2485 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
2486                                    displayed (defaults to 75)
2487 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
2488                                    function will be btn (the name of the button that was clicked, if applicable,
2489                                    e.g. "ok"), and text (the value of the active text field, if applicable).
2490                                    Progress and wait dialogs will ignore this option since they do not respond to
2491                                    user actions and can only be closed programmatically, so any required function
2492                                    should be called by the same code after it closes the dialog.
2493 icon              String           A CSS class that provides a background image to be used as an icon for
2494                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
2495 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
2496 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
2497 modal             Boolean          False to allow user interaction with the page while the message box is
2498                                    displayed (defaults to true)
2499 msg               String           A string that will replace the existing message box body text (defaults
2500                                    to the XHTML-compliant non-breaking space character '&#160;')
2501 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
2502 progress          Boolean          True to display a progress bar (defaults to false)
2503 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
2504 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
2505 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
2506 title             String           The title text
2507 value             String           The string value to set into the active textbox element if displayed
2508 wait              Boolean          True to display a progress bar (defaults to false)
2509 width             Number           The width of the dialog in pixels
2510 </pre>
2511          *
2512          * Example usage:
2513          * <pre><code>
2514 Roo.Msg.show({
2515    title: 'Address',
2516    msg: 'Please enter your address:',
2517    width: 300,
2518    buttons: Roo.MessageBox.OKCANCEL,
2519    multiline: true,
2520    fn: saveAddress,
2521    animEl: 'addAddressBtn'
2522 });
2523 </code></pre>
2524          * @param {Object} config Configuration options
2525          * @return {Roo.MessageBox} This message box
2526          */
2527         show : function(options)
2528         {
2529             
2530             // this causes nightmares if you show one dialog after another
2531             // especially on callbacks..
2532              
2533             if(this.isVisible()){
2534                 
2535                 this.hide();
2536                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
2537                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
2538                 Roo.log("New Dialog Message:" +  options.msg )
2539                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
2540                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
2541                 
2542             }
2543             var d = this.getDialog();
2544             opt = options;
2545             d.setTitle(opt.title || "&#160;");
2546             d.close.setDisplayed(opt.closable !== false);
2547             activeTextEl = textboxEl;
2548             opt.prompt = opt.prompt || (opt.multiline ? true : false);
2549             if(opt.prompt){
2550                 if(opt.multiline){
2551                     textboxEl.hide();
2552                     textareaEl.show();
2553                     textareaEl.setHeight(typeof opt.multiline == "number" ?
2554                         opt.multiline : this.defaultTextHeight);
2555                     activeTextEl = textareaEl;
2556                 }else{
2557                     textboxEl.show();
2558                     textareaEl.hide();
2559                 }
2560             }else{
2561                 textboxEl.hide();
2562                 textareaEl.hide();
2563             }
2564             progressEl.setDisplayed(opt.progress === true);
2565             this.updateProgress(0);
2566             activeTextEl.dom.value = opt.value || "";
2567             if(opt.prompt){
2568                 dlg.setDefaultButton(activeTextEl);
2569             }else{
2570                 var bs = opt.buttons;
2571                 var db = null;
2572                 if(bs && bs.ok){
2573                     db = buttons["ok"];
2574                 }else if(bs && bs.yes){
2575                     db = buttons["yes"];
2576                 }
2577                 dlg.setDefaultButton(db);
2578             }
2579             bwidth = updateButtons(opt.buttons);
2580             this.updateText(opt.msg);
2581             if(opt.cls){
2582                 d.el.addClass(opt.cls);
2583             }
2584             d.proxyDrag = opt.proxyDrag === true;
2585             d.modal = opt.modal !== false;
2586             d.mask = opt.modal !== false ? mask : false;
2587             if(!d.isVisible()){
2588                 // force it to the end of the z-index stack so it gets a cursor in FF
2589                 document.body.appendChild(dlg.el.dom);
2590                 d.animateTarget = null;
2591                 d.show(options.animEl);
2592             }
2593             return this;
2594         },
2595
2596         /**
2597          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
2598          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
2599          * and closing the message box when the process is complete.
2600          * @param {String} title The title bar text
2601          * @param {String} msg The message box body text
2602          * @return {Roo.MessageBox} This message box
2603          */
2604         progress : function(title, msg){
2605             this.show({
2606                 title : title,
2607                 msg : msg,
2608                 buttons: false,
2609                 progress:true,
2610                 closable:false,
2611                 minWidth: this.minProgressWidth,
2612                 modal : true
2613             });
2614             return this;
2615         },
2616
2617         /**
2618          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
2619          * If a callback function is passed it will be called after the user clicks the button, and the
2620          * id of the button that was clicked will be passed as the only parameter to the callback
2621          * (could also be the top-right close button).
2622          * @param {String} title The title bar text
2623          * @param {String} msg The message box body text
2624          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2625          * @param {Object} scope (optional) The scope of the callback function
2626          * @return {Roo.MessageBox} This message box
2627          */
2628         alert : function(title, msg, fn, scope){
2629             this.show({
2630                 title : title,
2631                 msg : msg,
2632                 buttons: this.OK,
2633                 fn: fn,
2634                 scope : scope,
2635                 modal : true
2636             });
2637             return this;
2638         },
2639
2640         /**
2641          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
2642          * interaction while waiting for a long-running process to complete that does not have defined intervals.
2643          * You are responsible for closing the message box when the process is complete.
2644          * @param {String} msg The message box body text
2645          * @param {String} title (optional) The title bar text
2646          * @return {Roo.MessageBox} This message box
2647          */
2648         wait : function(msg, title){
2649             this.show({
2650                 title : title,
2651                 msg : msg,
2652                 buttons: false,
2653                 closable:false,
2654                 progress:true,
2655                 modal:true,
2656                 width:300,
2657                 wait:true
2658             });
2659             waitTimer = Roo.TaskMgr.start({
2660                 run: function(i){
2661                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
2662                 },
2663                 interval: 1000
2664             });
2665             return this;
2666         },
2667
2668         /**
2669          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
2670          * If a callback function is passed it will be called after the user clicks either button, and the id of the
2671          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
2672          * @param {String} title The title bar text
2673          * @param {String} msg The message box body text
2674          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2675          * @param {Object} scope (optional) The scope of the callback function
2676          * @return {Roo.MessageBox} This message box
2677          */
2678         confirm : function(title, msg, fn, scope){
2679             this.show({
2680                 title : title,
2681                 msg : msg,
2682                 buttons: this.YESNO,
2683                 fn: fn,
2684                 scope : scope,
2685                 modal : true
2686             });
2687             return this;
2688         },
2689
2690         /**
2691          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
2692          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
2693          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
2694          * (could also be the top-right close button) and the text that was entered will be passed as the two
2695          * parameters to the callback.
2696          * @param {String} title The title bar text
2697          * @param {String} msg The message box body text
2698          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2699          * @param {Object} scope (optional) The scope of the callback function
2700          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
2701          * property, or the height in pixels to create the textbox (defaults to false / single-line)
2702          * @return {Roo.MessageBox} This message box
2703          */
2704         prompt : function(title, msg, fn, scope, multiline){
2705             this.show({
2706                 title : title,
2707                 msg : msg,
2708                 buttons: this.OKCANCEL,
2709                 fn: fn,
2710                 minWidth:250,
2711                 scope : scope,
2712                 prompt:true,
2713                 multiline: multiline,
2714                 modal : true
2715             });
2716             return this;
2717         },
2718
2719         /**
2720          * Button config that displays a single OK button
2721          * @type Object
2722          */
2723         OK : {ok:true},
2724         /**
2725          * Button config that displays Yes and No buttons
2726          * @type Object
2727          */
2728         YESNO : {yes:true, no:true},
2729         /**
2730          * Button config that displays OK and Cancel buttons
2731          * @type Object
2732          */
2733         OKCANCEL : {ok:true, cancel:true},
2734         /**
2735          * Button config that displays Yes, No and Cancel buttons
2736          * @type Object
2737          */
2738         YESNOCANCEL : {yes:true, no:true, cancel:true},
2739
2740         /**
2741          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
2742          * @type Number
2743          */
2744         defaultTextHeight : 75,
2745         /**
2746          * The maximum width in pixels of the message box (defaults to 600)
2747          * @type Number
2748          */
2749         maxWidth : 600,
2750         /**
2751          * The minimum width in pixels of the message box (defaults to 100)
2752          * @type Number
2753          */
2754         minWidth : 100,
2755         /**
2756          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
2757          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
2758          * @type Number
2759          */
2760         minProgressWidth : 250,
2761         /**
2762          * An object containing the default button text strings that can be overriden for localized language support.
2763          * Supported properties are: ok, cancel, yes and no.
2764          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
2765          * @type Object
2766          */
2767         buttonText : {
2768             ok : "OK",
2769             cancel : "Cancel",
2770             yes : "Yes",
2771             no : "No"
2772         }
2773     };
2774 }();
2775
2776 /**
2777  * Shorthand for {@link Roo.MessageBox}
2778  */
2779 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox 
2780 Roo.Msg = Roo.Msg || Roo.MessageBox;
2781 /*
2782  * - LGPL
2783  *
2784  * navbar
2785  * 
2786  */
2787
2788 /**
2789  * @class Roo.bootstrap.Navbar
2790  * @extends Roo.bootstrap.Component
2791  * Bootstrap Navbar class
2792
2793  * @constructor
2794  * Create a new Navbar
2795  * @param {Object} config The config object
2796  */
2797
2798
2799 Roo.bootstrap.Navbar = function(config){
2800     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
2801     
2802 };
2803
2804 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
2805     
2806     
2807    
2808     // private
2809     navItems : false,
2810     loadMask : false,
2811     
2812     
2813     getAutoCreate : function(){
2814         
2815         
2816         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
2817         
2818     },
2819     
2820     initEvents :function ()
2821     {
2822         //Roo.log(this.el.select('.navbar-toggle',true));
2823         this.el.select('.navbar-toggle',true).on('click', function() {
2824            // Roo.log('click');
2825             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
2826         }, this);
2827         
2828         var mark = {
2829             tag: "div",
2830             cls:"x-dlg-mask"
2831         }
2832         
2833         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
2834         
2835         var size = this.el.getSize();
2836         this.maskEl.setSize(size.width, size.height);
2837         this.maskEl.enableDisplayMode("block");
2838         this.maskEl.hide();
2839         
2840         if(this.loadMask){
2841             this.maskEl.show();
2842         }
2843     },
2844     
2845     
2846     getChildContainer : function()
2847     {
2848         if (this.el.select('.collapse').getCount()) {
2849             return this.el.select('.collapse',true).first();
2850         }
2851         
2852         return this.el;
2853     },
2854     
2855     mask : function()
2856     {
2857         this.maskEl.show();
2858     },
2859     
2860     unmask : function()
2861     {
2862         this.maskEl.hide();
2863     } 
2864     
2865     
2866     
2867     
2868 });
2869
2870
2871
2872  
2873
2874  /*
2875  * - LGPL
2876  *
2877  * navbar
2878  * 
2879  */
2880
2881 /**
2882  * @class Roo.bootstrap.NavSimplebar
2883  * @extends Roo.bootstrap.Navbar
2884  * Bootstrap Sidebar class
2885  *
2886  * @cfg {Boolean} inverse is inverted color
2887  * 
2888  * @cfg {String} type (nav | pills | tabs)
2889  * @cfg {Boolean} arrangement stacked | justified
2890  * @cfg {String} align (left | right) alignment
2891  * 
2892  * @cfg {Boolean} main (true|false) main nav bar? default false
2893  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
2894  * 
2895  * @cfg {String} tag (header|footer|nav|div) default is nav 
2896
2897  * 
2898  * 
2899  * 
2900  * @constructor
2901  * Create a new Sidebar
2902  * @param {Object} config The config object
2903  */
2904
2905
2906 Roo.bootstrap.NavSimplebar = function(config){
2907     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
2908 };
2909
2910 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
2911     
2912     inverse: false,
2913     
2914     type: false,
2915     arrangement: '',
2916     align : false,
2917     
2918     
2919     
2920     main : false,
2921     
2922     
2923     tag : false,
2924     
2925     
2926     getAutoCreate : function(){
2927         
2928         
2929         var cfg = {
2930             tag : this.tag || 'div',
2931             cls : 'navbar'
2932         };
2933           
2934         
2935         cfg.cn = [
2936             {
2937                 cls: 'nav',
2938                 tag : 'ul'
2939             }
2940         ];
2941         
2942          
2943         this.type = this.type || 'nav';
2944         if (['tabs','pills'].indexOf(this.type)!==-1) {
2945             cfg.cn[0].cls += ' nav-' + this.type
2946         
2947         
2948         } else {
2949             if (this.type!=='nav') {
2950                 Roo.log('nav type must be nav/tabs/pills')
2951             }
2952             cfg.cn[0].cls += ' navbar-nav'
2953         }
2954         
2955         
2956         
2957         
2958         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
2959             cfg.cn[0].cls += ' nav-' + this.arrangement;
2960         }
2961         
2962         
2963         if (this.align === 'right') {
2964             cfg.cn[0].cls += ' navbar-right';
2965         }
2966         
2967         if (this.inverse) {
2968             cfg.cls += ' navbar-inverse';
2969             
2970         }
2971         
2972         
2973         return cfg;
2974     
2975         
2976     }
2977     
2978     
2979     
2980 });
2981
2982
2983
2984  
2985
2986  
2987        /*
2988  * - LGPL
2989  *
2990  * navbar
2991  * 
2992  */
2993
2994 /**
2995  * @class Roo.bootstrap.NavHeaderbar
2996  * @extends Roo.bootstrap.NavSimplebar
2997  * Bootstrap Sidebar class
2998  *
2999  * @cfg {String} brand what is brand
3000  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3001  * @cfg {String} brand_href href of the brand
3002  * @cfg {Boolean} srButton generate the sr-only button (true | false) default true
3003  * 
3004  * @constructor
3005  * Create a new Sidebar
3006  * @param {Object} config The config object
3007  */
3008
3009
3010 Roo.bootstrap.NavHeaderbar = function(config){
3011     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3012 };
3013
3014 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3015     
3016     position: '',
3017     brand: '',
3018     brand_href: false,
3019     srButton : true,
3020     
3021     
3022     getAutoCreate : function(){
3023         
3024         var   cfg = {
3025             tag: this.nav || 'nav',
3026             cls: 'navbar',
3027             role: 'navigation',
3028             cn: []
3029         };
3030         
3031         if(this.srButton){
3032             cfg.cn.push({
3033                 tag: 'div',
3034                 cls: 'navbar-header',
3035                 cn: [
3036                     {
3037                         tag: 'button',
3038                         type: 'button',
3039                         cls: 'navbar-toggle',
3040                         'data-toggle': 'collapse',
3041                         cn: [
3042                             {
3043                                 tag: 'span',
3044                                 cls: 'sr-only',
3045                                 html: 'Toggle navigation'
3046                             },
3047                             {
3048                                 tag: 'span',
3049                                 cls: 'icon-bar'
3050                             },
3051                             {
3052                                 tag: 'span',
3053                                 cls: 'icon-bar'
3054                             },
3055                             {
3056                                 tag: 'span',
3057                                 cls: 'icon-bar'
3058                             }
3059                         ]
3060                     }
3061                 ]
3062             });
3063         }
3064         
3065         cfg.cn.push({
3066             tag: 'div',
3067             cls: 'collapse navbar-collapse',
3068             cn : []
3069         });
3070         
3071         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3072         
3073         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3074             cfg.cls += ' navbar-' + this.position;
3075             
3076             // tag can override this..
3077             
3078             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3079         }
3080         
3081         if (this.brand !== '') {
3082             cfg.cn[0].cn.push({
3083                 tag: 'a',
3084                 href: this.brand_href ? this.brand_href : '#',
3085                 cls: 'navbar-brand',
3086                 cn: [
3087                 this.brand
3088                 ]
3089             });
3090         }
3091         
3092         if(this.main){
3093             cfg.cls += ' main-nav';
3094         }
3095         
3096         
3097         return cfg;
3098
3099         
3100     }
3101     
3102     
3103     
3104 });
3105
3106
3107
3108  
3109
3110  /*
3111  * - LGPL
3112  *
3113  * navbar
3114  * 
3115  */
3116
3117 /**
3118  * @class Roo.bootstrap.NavSidebar
3119  * @extends Roo.bootstrap.Navbar
3120  * Bootstrap Sidebar class
3121  * 
3122  * @constructor
3123  * Create a new Sidebar
3124  * @param {Object} config The config object
3125  */
3126
3127
3128 Roo.bootstrap.NavSidebar = function(config){
3129     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3130 };
3131
3132 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3133     
3134     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3135     
3136     getAutoCreate : function(){
3137         
3138         
3139         return  {
3140             tag: 'div',
3141             cls: 'sidebar sidebar-nav'
3142         };
3143     
3144         
3145     }
3146     
3147     
3148     
3149 });
3150
3151
3152
3153  
3154
3155  /*
3156  * - LGPL
3157  *
3158  * nav group
3159  * 
3160  */
3161
3162 /**
3163  * @class Roo.bootstrap.NavGroup
3164  * @extends Roo.bootstrap.Component
3165  * Bootstrap NavGroup class
3166  * @cfg {String} align left | right
3167  * @cfg {Boolean} inverse false | true
3168  * @cfg {String} type (nav|pills|tab) default nav
3169  * @cfg {String} navId - reference Id for navbar.
3170
3171  * 
3172  * @constructor
3173  * Create a new nav group
3174  * @param {Object} config The config object
3175  */
3176
3177 Roo.bootstrap.NavGroup = function(config){
3178     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3179     this.navItems = [];
3180    
3181     Roo.bootstrap.NavGroup.register(this);
3182      this.addEvents({
3183         /**
3184              * @event changed
3185              * Fires when the active item changes
3186              * @param {Roo.bootstrap.NavGroup} this
3187              * @param {Roo.bootstrap.Navbar.Item} item The item selected
3188              * @param {Roo.bootstrap.Navbar.Item} item The previously selected item 
3189          */
3190         'changed': true
3191      });
3192     
3193 };
3194
3195 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3196     
3197     align: '',
3198     inverse: false,
3199     form: false,
3200     type: 'nav',
3201     navId : '',
3202     // private
3203     
3204     navItems : false, 
3205     
3206     getAutoCreate : function()
3207     {
3208         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3209         
3210         cfg = {
3211             tag : 'ul',
3212             cls: 'nav' 
3213         }
3214         
3215         if (['tabs','pills'].indexOf(this.type)!==-1) {
3216             cfg.cls += ' nav-' + this.type
3217         } else {
3218             if (this.type!=='nav') {
3219                 Roo.log('nav type must be nav/tabs/pills')
3220             }
3221             cfg.cls += ' navbar-nav'
3222         }
3223         
3224         if (this.parent().sidebar) {
3225             cfg = {
3226                 tag: 'ul',
3227                 cls: 'dashboard-menu sidebar-menu'
3228             }
3229             
3230             return cfg;
3231         }
3232         
3233         if (this.form === true) {
3234             cfg = {
3235                 tag: 'form',
3236                 cls: 'navbar-form'
3237             }
3238             
3239             if (this.align === 'right') {
3240                 cfg.cls += ' navbar-right';
3241             } else {
3242                 cfg.cls += ' navbar-left';
3243             }
3244         }
3245         
3246         if (this.align === 'right') {
3247             cfg.cls += ' navbar-right';
3248         }
3249         
3250         if (this.inverse) {
3251             cfg.cls += ' navbar-inverse';
3252             
3253         }
3254         
3255         
3256         return cfg;
3257     },
3258     /**
3259     * sets the active Navigation item
3260     * @param {Roo.bootstrap.NavItem} the new current navitem
3261     */
3262     setActiveItem : function(item)
3263     {
3264         var prev = false;
3265         Roo.each(this.navItems, function(v){
3266             if (v == item) {
3267                 return ;
3268             }
3269             if (v.isActive()) {
3270                 v.setActive(false, true);
3271                 prev = v;
3272                 
3273             }
3274             
3275         });
3276
3277         item.setActive(true, true);
3278         this.fireEvent('changed', this, item, prev);
3279         
3280         
3281     },
3282     /**
3283     * gets the active Navigation item
3284     * @return {Roo.bootstrap.NavItem} the current navitem
3285     */
3286     getActive : function()
3287     {
3288         
3289         var prev = false;
3290         Roo.each(this.navItems, function(v){
3291             
3292             if (v.isActive()) {
3293                 prev = v;
3294                 
3295             }
3296             
3297         });
3298         return prev;
3299     },
3300     
3301     indexOfNav : function()
3302     {
3303         
3304         var prev = false;
3305         Roo.each(this.navItems, function(v,i){
3306             
3307             if (v.isActive()) {
3308                 prev = i;
3309                 
3310             }
3311             
3312         });
3313         return prev;
3314     },
3315     /**
3316     * adds a Navigation item
3317     * @param {Roo.bootstrap.NavItem} the navitem to add
3318     */
3319     addItem : function(cfg)
3320     {
3321         var cn = new Roo.bootstrap.NavItem(cfg);
3322         this.register(cn);
3323         cn.parentId = this.id;
3324         cn.onRender(this.el, null);
3325         return cn;
3326     },
3327     /**
3328     * register a Navigation item
3329     * @param {Roo.bootstrap.NavItem} the navitem to add
3330     */
3331     register : function(item)
3332     {
3333         this.navItems.push( item);
3334         item.navId = this.navId;
3335     
3336     },
3337   
3338     
3339     getNavItem: function(tabId)
3340     {
3341         var ret = false;
3342         Roo.each(this.navItems, function(e) {
3343             if (e.tabId == tabId) {
3344                ret =  e;
3345                return false;
3346             }
3347             return true;
3348             
3349         });
3350         return ret;
3351     },
3352     
3353     setActiveNext : function()
3354     {
3355         var i = this.indexOfNav(this.getActive());
3356         if (i > this.navItems.length) {
3357             return;
3358         }
3359         this.setActiveItem(this.navItems[i+1]);
3360     },
3361     setActivePrev : function()
3362     {
3363         var i = this.indexOfNav(this.getActive());
3364         if (i  < 1) {
3365             return;
3366         }
3367         this.setActiveItem(this.navItems[i-1]);
3368     },
3369     clearWasActive : function(except) {
3370         Roo.each(this.navItems, function(e) {
3371             if (e.tabId != except.tabId && e.was_active) {
3372                e.was_active = false;
3373                return false;
3374             }
3375             return true;
3376             
3377         });
3378     },
3379     getWasActive : function ()
3380     {
3381         var r = false;
3382         Roo.each(this.navItems, function(e) {
3383             if (e.was_active) {
3384                r = e;
3385                return false;
3386             }
3387             return true;
3388             
3389         });
3390         return r;
3391     }
3392     
3393     
3394 });
3395
3396  
3397 Roo.apply(Roo.bootstrap.NavGroup, {
3398     
3399     groups: {},
3400      /**
3401     * register a Navigation Group
3402     * @param {Roo.bootstrap.NavGroup} the navgroup to add
3403     */
3404     register : function(navgrp)
3405     {
3406         this.groups[navgrp.navId] = navgrp;
3407         
3408     },
3409     /**
3410     * fetch a Navigation Group based on the navigation ID
3411     * @param {string} the navgroup to add
3412     * @returns {Roo.bootstrap.NavGroup} the navgroup 
3413     */
3414     get: function(navId) {
3415         if (typeof(this.groups[navId]) == 'undefined') {
3416             return false;
3417             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
3418         }
3419         return this.groups[navId] ;
3420     }
3421     
3422     
3423     
3424 });
3425
3426  /*
3427  * - LGPL
3428  *
3429  * row
3430  * 
3431  */
3432
3433 /**
3434  * @class Roo.bootstrap.NavItem
3435  * @extends Roo.bootstrap.Component
3436  * Bootstrap Navbar.NavItem class
3437  * @cfg {String} href  link to
3438  * @cfg {String} html content of button
3439  * @cfg {String} badge text inside badge
3440  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
3441  * @cfg {String} glyphicon name of glyphicon
3442  * @cfg {String} icon name of font awesome icon
3443  * @cfg {Boolean} active Is item active
3444  * @cfg {Boolean} disabled Is item disabled
3445  
3446  * @cfg {Boolean} preventDefault (true | false) default false
3447  * @cfg {String} tabId the tab that this item activates.
3448  * @cfg {String} tagtype (a|span) render as a href or span?
3449   
3450  * @constructor
3451  * Create a new Navbar Item
3452  * @param {Object} config The config object
3453  */
3454 Roo.bootstrap.NavItem = function(config){
3455     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
3456     this.addEvents({
3457         // raw events
3458         /**
3459          * @event click
3460          * The raw click event for the entire grid.
3461          * @param {Roo.EventObject} e
3462          */
3463         "click" : true,
3464          /**
3465             * @event changed
3466             * Fires when the active item active state changes
3467             * @param {Roo.bootstrap.NavItem} this
3468             * @param {boolean} state the new state
3469              
3470          */
3471         'changed': true
3472     });
3473    
3474 };
3475
3476 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
3477     
3478     href: false,
3479     html: '',
3480     badge: '',
3481     icon: false,
3482     glyphicon: false,
3483     active: false,
3484     preventDefault : false,
3485     tabId : false,
3486     tagtype : 'a',
3487     disabled : false,
3488     
3489     was_active : false,
3490     
3491     getAutoCreate : function(){
3492          
3493         var cfg = {
3494             tag: 'li',
3495             cls: 'nav-item'
3496             
3497         }
3498         if (this.active) {
3499             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
3500         }
3501         if (this.disabled) {
3502             cfg.cls += ' disabled';
3503         }
3504         
3505         if (this.href || this.html || this.glyphicon || this.icon) {
3506             cfg.cn = [
3507                 {
3508                     tag: this.tagtype,
3509                     href : this.href || "#",
3510                     html: this.html || ''
3511                 }
3512             ];
3513             
3514             if (this.icon) {
3515                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
3516             }
3517
3518             if(this.glyphicon) {
3519                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
3520             }
3521             
3522             if (this.menu) {
3523                 
3524                 cfg.cn[0].html += " <span class='caret'></span>";
3525              
3526             }
3527             
3528             if (this.badge !== '') {
3529                  
3530                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
3531             }
3532         }
3533         
3534         
3535         
3536         return cfg;
3537     },
3538     initEvents: function() {
3539        // Roo.log('init events?');
3540        // Roo.log(this.el.dom);
3541         if (typeof (this.menu) != 'undefined') {
3542             this.menu.parentType = this.xtype;
3543             this.menu.triggerEl = this.el;
3544             this.addxtype(Roo.apply({}, this.menu));
3545         }
3546
3547        
3548         this.el.select('a',true).on('click', this.onClick, this);
3549         // at this point parent should be available..
3550         this.parent().register(this);
3551     },
3552     
3553     onClick : function(e)
3554     {
3555          
3556         if(this.preventDefault){
3557             e.preventDefault();
3558         }
3559         if (this.disabled) {
3560             return;
3561         }
3562         
3563         var tg = Roo.bootstrap.TabGroup.get(this.navId);
3564         if (tg && tg.transition) {
3565             Roo.log("waiting for the transitionend");
3566             return;
3567         }
3568         
3569         Roo.log("fire event clicked");
3570         if(this.fireEvent('click', this, e) === false){
3571             return;
3572         };
3573         
3574         if (['tabs','pills'].indexOf(this.parent().type)!==-1) {
3575             if (typeof(this.parent().setActiveItem) !== 'undefined') {
3576                 this.parent().setActiveItem(this);
3577             }
3578         } 
3579     },
3580     
3581     isActive: function () {
3582         return this.active
3583     },
3584     setActive : function(state, fire, is_was_active)
3585     {
3586         if (this.active && !state & this.navId) {
3587             this.was_active = true;
3588             var nv = Roo.bootstrap.NavGroup.get(this.navId);
3589             if (nv) {
3590                 nv.clearWasActive(this);
3591             }
3592             
3593         }
3594         this.active = state;
3595         
3596         if (!state ) {
3597             this.el.removeClass('active');
3598         } else if (!this.el.hasClass('active')) {
3599             this.el.addClass('active');
3600         }
3601         if (fire) {
3602             this.fireEvent('changed', this, state);
3603         }
3604         
3605         // show a panel if it's registered and related..
3606         
3607         if (!this.navId || !this.tabId || !state || is_was_active) {
3608             return;
3609         }
3610         
3611         var tg = Roo.bootstrap.TabGroup.get(this.navId);
3612         if (!tg) {
3613             return;
3614         }
3615         var pan = tg.getPanelByName(this.tabId);
3616         if (!pan) {
3617             return;
3618         }
3619         // if we can not flip to new panel - go back to old nav highlight..
3620         if (false == tg.showPanel(pan)) {
3621             var nv = Roo.bootstrap.NavGroup.get(this.navId);
3622             if (nv) {
3623                 var onav = nv.getWasActive();
3624                 if (onav) {
3625                     onav.setActive(true, false, true);
3626                 }
3627             }
3628             
3629         }
3630         
3631         
3632         
3633     },
3634      // this should not be here...
3635     setDisabled : function(state)
3636     {
3637         this.disabled = state;
3638         if (!state ) {
3639             this.el.removeClass('disabled');
3640         } else if (!this.el.hasClass('disabled')) {
3641             this.el.addClass('disabled');
3642         }
3643         
3644     }
3645 });
3646  
3647
3648  /*
3649  * - LGPL
3650  *
3651  * sidebar item
3652  *
3653  *  li
3654  *    <span> icon </span>
3655  *    <span> text </span>
3656  *    <span>badge </span>
3657  */
3658
3659 /**
3660  * @class Roo.bootstrap.NavSidebarItem
3661  * @extends Roo.bootstrap.NavItem
3662  * Bootstrap Navbar.NavSidebarItem class
3663  * @constructor
3664  * Create a new Navbar Button
3665  * @param {Object} config The config object
3666  */
3667 Roo.bootstrap.NavSidebarItem = function(config){
3668     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
3669     this.addEvents({
3670         // raw events
3671         /**
3672          * @event click
3673          * The raw click event for the entire grid.
3674          * @param {Roo.EventObject} e
3675          */
3676         "click" : true,
3677          /**
3678             * @event changed
3679             * Fires when the active item active state changes
3680             * @param {Roo.bootstrap.NavSidebarItem} this
3681             * @param {boolean} state the new state
3682              
3683          */
3684         'changed': true
3685     });
3686    
3687 };
3688
3689 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
3690     
3691     
3692     getAutoCreate : function(){
3693         
3694         
3695         var a = {
3696                 tag: 'a',
3697                 href : this.href || '#',
3698                 cls: '',
3699                 html : '',
3700                 cn : []
3701         };
3702         var cfg = {
3703             tag: 'li',
3704             cls: '',
3705             cn: [ a ]
3706         }
3707         var span = {
3708             tag: 'span',
3709             html : this.html || ''
3710         }
3711         
3712         
3713         if (this.active) {
3714             cfg.cls += ' active';
3715         }
3716         
3717         // left icon..
3718         if (this.glyphicon || this.icon) {
3719             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
3720             a.cn.push({ tag : 'i', cls : c }) ;
3721         }
3722         // html..
3723         a.cn.push(span);
3724         // then badge..
3725         if (this.badge !== '') {
3726             a.cn.push({ tag: 'span',  cls : 'badge pull-right ' + (this.badgecls || ''), html: this.badge }); 
3727         }
3728         // fi
3729         if (this.menu) {
3730             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
3731             a.cls += 'dropdown-toggle treeview' ;
3732             
3733         }
3734         
3735         
3736         
3737         return cfg;
3738          
3739            
3740     }
3741    
3742      
3743  
3744 });
3745  
3746
3747  /*
3748  * - LGPL
3749  *
3750  * row
3751  * 
3752  */
3753
3754 /**
3755  * @class Roo.bootstrap.Row
3756  * @extends Roo.bootstrap.Component
3757  * Bootstrap Row class (contains columns...)
3758  * 
3759  * @constructor
3760  * Create a new Row
3761  * @param {Object} config The config object
3762  */
3763
3764 Roo.bootstrap.Row = function(config){
3765     Roo.bootstrap.Row.superclass.constructor.call(this, config);
3766 };
3767
3768 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
3769     
3770     getAutoCreate : function(){
3771        return {
3772             cls: 'row clearfix'
3773        };
3774     }
3775     
3776     
3777 });
3778
3779  
3780
3781  /*
3782  * - LGPL
3783  *
3784  * element
3785  * 
3786  */
3787
3788 /**
3789  * @class Roo.bootstrap.Element
3790  * @extends Roo.bootstrap.Component
3791  * Bootstrap Element class
3792  * @cfg {String} html contents of the element
3793  * @cfg {String} tag tag of the element
3794  * @cfg {String} cls class of the element
3795  * 
3796  * @constructor
3797  * Create a new Element
3798  * @param {Object} config The config object
3799  */
3800
3801 Roo.bootstrap.Element = function(config){
3802     Roo.bootstrap.Element.superclass.constructor.call(this, config);
3803 };
3804
3805 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
3806     
3807     tag: 'div',
3808     cls: '',
3809     html: '',
3810      
3811     
3812     getAutoCreate : function(){
3813         
3814         var cfg = {
3815             tag: this.tag,
3816             cls: this.cls,
3817             html: this.html
3818         }
3819         
3820         
3821         
3822         return cfg;
3823     }
3824    
3825 });
3826
3827  
3828
3829  /*
3830  * - LGPL
3831  *
3832  * pagination
3833  * 
3834  */
3835
3836 /**
3837  * @class Roo.bootstrap.Pagination
3838  * @extends Roo.bootstrap.Component
3839  * Bootstrap Pagination class
3840  * @cfg {String} size xs | sm | md | lg
3841  * @cfg {Boolean} inverse false | true
3842  * 
3843  * @constructor
3844  * Create a new Pagination
3845  * @param {Object} config The config object
3846  */
3847
3848 Roo.bootstrap.Pagination = function(config){
3849     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
3850 };
3851
3852 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
3853     
3854     cls: false,
3855     size: false,
3856     inverse: false,
3857     
3858     getAutoCreate : function(){
3859         var cfg = {
3860             tag: 'ul',
3861                 cls: 'pagination'
3862         };
3863         if (this.inverse) {
3864             cfg.cls += ' inverse';
3865         }
3866         if (this.html) {
3867             cfg.html=this.html;
3868         }
3869         if (this.cls) {
3870             cfg.cls += " " + this.cls;
3871         }
3872         return cfg;
3873     }
3874    
3875 });
3876
3877  
3878
3879  /*
3880  * - LGPL
3881  *
3882  * Pagination item
3883  * 
3884  */
3885
3886
3887 /**
3888  * @class Roo.bootstrap.PaginationItem
3889  * @extends Roo.bootstrap.Component
3890  * Bootstrap PaginationItem class
3891  * @cfg {String} html text
3892  * @cfg {String} href the link
3893  * @cfg {Boolean} preventDefault (true | false) default true
3894  * @cfg {Boolean} active (true | false) default false
3895  * 
3896  * 
3897  * @constructor
3898  * Create a new PaginationItem
3899  * @param {Object} config The config object
3900  */
3901
3902
3903 Roo.bootstrap.PaginationItem = function(config){
3904     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
3905     this.addEvents({
3906         // raw events
3907         /**
3908          * @event click
3909          * The raw click event for the entire grid.
3910          * @param {Roo.EventObject} e
3911          */
3912         "click" : true
3913     });
3914 };
3915
3916 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
3917     
3918     href : false,
3919     html : false,
3920     preventDefault: true,
3921     active : false,
3922     cls : false,
3923     
3924     getAutoCreate : function(){
3925         var cfg= {
3926             tag: 'li',
3927             cn: [
3928                 {
3929                     tag : 'a',
3930                     href : this.href ? this.href : '#',
3931                     html : this.html ? this.html : ''
3932                 }
3933             ]
3934         };
3935         
3936         if(this.cls){
3937             cfg.cls = this.cls;
3938         }
3939         
3940         if(this.active){
3941             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
3942         }
3943         
3944         return cfg;
3945     },
3946     
3947     initEvents: function() {
3948         
3949         this.el.on('click', this.onClick, this);
3950         
3951     },
3952     onClick : function(e)
3953     {
3954         Roo.log('PaginationItem on click ');
3955         if(this.preventDefault){
3956             e.preventDefault();
3957         }
3958         
3959         this.fireEvent('click', this, e);
3960     }
3961    
3962 });
3963
3964  
3965
3966  /*
3967  * - LGPL
3968  *
3969  * slider
3970  * 
3971  */
3972
3973
3974 /**
3975  * @class Roo.bootstrap.Slider
3976  * @extends Roo.bootstrap.Component
3977  * Bootstrap Slider class
3978  *    
3979  * @constructor
3980  * Create a new Slider
3981  * @param {Object} config The config object
3982  */
3983
3984 Roo.bootstrap.Slider = function(config){
3985     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
3986 };
3987
3988 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
3989     
3990     getAutoCreate : function(){
3991         
3992         var cfg = {
3993             tag: 'div',
3994             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
3995             cn: [
3996                 {
3997                     tag: 'a',
3998                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
3999                 }
4000             ]
4001         }
4002         
4003         return cfg;
4004     }
4005    
4006 });
4007
4008  /*
4009  * Based on:
4010  * Ext JS Library 1.1.1
4011  * Copyright(c) 2006-2007, Ext JS, LLC.
4012  *
4013  * Originally Released Under LGPL - original licence link has changed is not relivant.
4014  *
4015  * Fork - LGPL
4016  * <script type="text/javascript">
4017  */
4018  
4019
4020 /**
4021  * @class Roo.grid.ColumnModel
4022  * @extends Roo.util.Observable
4023  * This is the default implementation of a ColumnModel used by the Grid. It defines
4024  * the columns in the grid.
4025  * <br>Usage:<br>
4026  <pre><code>
4027  var colModel = new Roo.grid.ColumnModel([
4028         {header: "Ticker", width: 60, sortable: true, locked: true},
4029         {header: "Company Name", width: 150, sortable: true},
4030         {header: "Market Cap.", width: 100, sortable: true},
4031         {header: "$ Sales", width: 100, sortable: true, renderer: money},
4032         {header: "Employees", width: 100, sortable: true, resizable: false}
4033  ]);
4034  </code></pre>
4035  * <p>
4036  
4037  * The config options listed for this class are options which may appear in each
4038  * individual column definition.
4039  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4040  * @constructor
4041  * @param {Object} config An Array of column config objects. See this class's
4042  * config objects for details.
4043 */
4044 Roo.grid.ColumnModel = function(config){
4045         /**
4046      * The config passed into the constructor
4047      */
4048     this.config = config;
4049     this.lookup = {};
4050
4051     // if no id, create one
4052     // if the column does not have a dataIndex mapping,
4053     // map it to the order it is in the config
4054     for(var i = 0, len = config.length; i < len; i++){
4055         var c = config[i];
4056         if(typeof c.dataIndex == "undefined"){
4057             c.dataIndex = i;
4058         }
4059         if(typeof c.renderer == "string"){
4060             c.renderer = Roo.util.Format[c.renderer];
4061         }
4062         if(typeof c.id == "undefined"){
4063             c.id = Roo.id();
4064         }
4065         if(c.editor && c.editor.xtype){
4066             c.editor  = Roo.factory(c.editor, Roo.grid);
4067         }
4068         if(c.editor && c.editor.isFormField){
4069             c.editor = new Roo.grid.GridEditor(c.editor);
4070         }
4071         this.lookup[c.id] = c;
4072     }
4073
4074     /**
4075      * The width of columns which have no width specified (defaults to 100)
4076      * @type Number
4077      */
4078     this.defaultWidth = 100;
4079
4080     /**
4081      * Default sortable of columns which have no sortable specified (defaults to false)
4082      * @type Boolean
4083      */
4084     this.defaultSortable = false;
4085
4086     this.addEvents({
4087         /**
4088              * @event widthchange
4089              * Fires when the width of a column changes.
4090              * @param {ColumnModel} this
4091              * @param {Number} columnIndex The column index
4092              * @param {Number} newWidth The new width
4093              */
4094             "widthchange": true,
4095         /**
4096              * @event headerchange
4097              * Fires when the text of a header changes.
4098              * @param {ColumnModel} this
4099              * @param {Number} columnIndex The column index
4100              * @param {Number} newText The new header text
4101              */
4102             "headerchange": true,
4103         /**
4104              * @event hiddenchange
4105              * Fires when a column is hidden or "unhidden".
4106              * @param {ColumnModel} this
4107              * @param {Number} columnIndex The column index
4108              * @param {Boolean} hidden true if hidden, false otherwise
4109              */
4110             "hiddenchange": true,
4111             /**
4112          * @event columnmoved
4113          * Fires when a column is moved.
4114          * @param {ColumnModel} this
4115          * @param {Number} oldIndex
4116          * @param {Number} newIndex
4117          */
4118         "columnmoved" : true,
4119         /**
4120          * @event columlockchange
4121          * Fires when a column's locked state is changed
4122          * @param {ColumnModel} this
4123          * @param {Number} colIndex
4124          * @param {Boolean} locked true if locked
4125          */
4126         "columnlockchange" : true
4127     });
4128     Roo.grid.ColumnModel.superclass.constructor.call(this);
4129 };
4130 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4131     /**
4132      * @cfg {String} header The header text to display in the Grid view.
4133      */
4134     /**
4135      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4136      * {@link Roo.data.Record} definition from which to draw the column's value. If not
4137      * specified, the column's index is used as an index into the Record's data Array.
4138      */
4139     /**
4140      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4141      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4142      */
4143     /**
4144      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4145      * Defaults to the value of the {@link #defaultSortable} property.
4146      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4147      */
4148     /**
4149      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
4150      */
4151     /**
4152      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
4153      */
4154     /**
4155      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4156      */
4157     /**
4158      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4159      */
4160     /**
4161      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4162      * given the cell's data value. See {@link #setRenderer}. If not specified, the
4163      * default renderer uses the raw data value. If an object is returned (bootstrap only)
4164      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4165      */
4166        /**
4167      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
4168      */
4169     /**
4170      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
4171      */
4172
4173     /**
4174      * Returns the id of the column at the specified index.
4175      * @param {Number} index The column index
4176      * @return {String} the id
4177      */
4178     getColumnId : function(index){
4179         return this.config[index].id;
4180     },
4181
4182     /**
4183      * Returns the column for a specified id.
4184      * @param {String} id The column id
4185      * @return {Object} the column
4186      */
4187     getColumnById : function(id){
4188         return this.lookup[id];
4189     },
4190
4191     
4192     /**
4193      * Returns the column for a specified dataIndex.
4194      * @param {String} dataIndex The column dataIndex
4195      * @return {Object|Boolean} the column or false if not found
4196      */
4197     getColumnByDataIndex: function(dataIndex){
4198         var index = this.findColumnIndex(dataIndex);
4199         return index > -1 ? this.config[index] : false;
4200     },
4201     
4202     /**
4203      * Returns the index for a specified column id.
4204      * @param {String} id The column id
4205      * @return {Number} the index, or -1 if not found
4206      */
4207     getIndexById : function(id){
4208         for(var i = 0, len = this.config.length; i < len; i++){
4209             if(this.config[i].id == id){
4210                 return i;
4211             }
4212         }
4213         return -1;
4214     },
4215     
4216     /**
4217      * Returns the index for a specified column dataIndex.
4218      * @param {String} dataIndex The column dataIndex
4219      * @return {Number} the index, or -1 if not found
4220      */
4221     
4222     findColumnIndex : function(dataIndex){
4223         for(var i = 0, len = this.config.length; i < len; i++){
4224             if(this.config[i].dataIndex == dataIndex){
4225                 return i;
4226             }
4227         }
4228         return -1;
4229     },
4230     
4231     
4232     moveColumn : function(oldIndex, newIndex){
4233         var c = this.config[oldIndex];
4234         this.config.splice(oldIndex, 1);
4235         this.config.splice(newIndex, 0, c);
4236         this.dataMap = null;
4237         this.fireEvent("columnmoved", this, oldIndex, newIndex);
4238     },
4239
4240     isLocked : function(colIndex){
4241         return this.config[colIndex].locked === true;
4242     },
4243
4244     setLocked : function(colIndex, value, suppressEvent){
4245         if(this.isLocked(colIndex) == value){
4246             return;
4247         }
4248         this.config[colIndex].locked = value;
4249         if(!suppressEvent){
4250             this.fireEvent("columnlockchange", this, colIndex, value);
4251         }
4252     },
4253
4254     getTotalLockedWidth : function(){
4255         var totalWidth = 0;
4256         for(var i = 0; i < this.config.length; i++){
4257             if(this.isLocked(i) && !this.isHidden(i)){
4258                 this.totalWidth += this.getColumnWidth(i);
4259             }
4260         }
4261         return totalWidth;
4262     },
4263
4264     getLockedCount : function(){
4265         for(var i = 0, len = this.config.length; i < len; i++){
4266             if(!this.isLocked(i)){
4267                 return i;
4268             }
4269         }
4270     },
4271
4272     /**
4273      * Returns the number of columns.
4274      * @return {Number}
4275      */
4276     getColumnCount : function(visibleOnly){
4277         if(visibleOnly === true){
4278             var c = 0;
4279             for(var i = 0, len = this.config.length; i < len; i++){
4280                 if(!this.isHidden(i)){
4281                     c++;
4282                 }
4283             }
4284             return c;
4285         }
4286         return this.config.length;
4287     },
4288
4289     /**
4290      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
4291      * @param {Function} fn
4292      * @param {Object} scope (optional)
4293      * @return {Array} result
4294      */
4295     getColumnsBy : function(fn, scope){
4296         var r = [];
4297         for(var i = 0, len = this.config.length; i < len; i++){
4298             var c = this.config[i];
4299             if(fn.call(scope||this, c, i) === true){
4300                 r[r.length] = c;
4301             }
4302         }
4303         return r;
4304     },
4305
4306     /**
4307      * Returns true if the specified column is sortable.
4308      * @param {Number} col The column index
4309      * @return {Boolean}
4310      */
4311     isSortable : function(col){
4312         if(typeof this.config[col].sortable == "undefined"){
4313             return this.defaultSortable;
4314         }
4315         return this.config[col].sortable;
4316     },
4317
4318     /**
4319      * Returns the rendering (formatting) function defined for the column.
4320      * @param {Number} col The column index.
4321      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
4322      */
4323     getRenderer : function(col){
4324         if(!this.config[col].renderer){
4325             return Roo.grid.ColumnModel.defaultRenderer;
4326         }
4327         return this.config[col].renderer;
4328     },
4329
4330     /**
4331      * Sets the rendering (formatting) function for a column.
4332      * @param {Number} col The column index
4333      * @param {Function} fn The function to use to process the cell's raw data
4334      * to return HTML markup for the grid view. The render function is called with
4335      * the following parameters:<ul>
4336      * <li>Data value.</li>
4337      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
4338      * <li>css A CSS style string to apply to the table cell.</li>
4339      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
4340      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
4341      * <li>Row index</li>
4342      * <li>Column index</li>
4343      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
4344      */
4345     setRenderer : function(col, fn){
4346         this.config[col].renderer = fn;
4347     },
4348
4349     /**
4350      * Returns the width for the specified column.
4351      * @param {Number} col The column index
4352      * @return {Number}
4353      */
4354     getColumnWidth : function(col){
4355         return this.config[col].width * 1 || this.defaultWidth;
4356     },
4357
4358     /**
4359      * Sets the width for a column.
4360      * @param {Number} col The column index
4361      * @param {Number} width The new width
4362      */
4363     setColumnWidth : function(col, width, suppressEvent){
4364         this.config[col].width = width;
4365         this.totalWidth = null;
4366         if(!suppressEvent){
4367              this.fireEvent("widthchange", this, col, width);
4368         }
4369     },
4370
4371     /**
4372      * Returns the total width of all columns.
4373      * @param {Boolean} includeHidden True to include hidden column widths
4374      * @return {Number}
4375      */
4376     getTotalWidth : function(includeHidden){
4377         if(!this.totalWidth){
4378             this.totalWidth = 0;
4379             for(var i = 0, len = this.config.length; i < len; i++){
4380                 if(includeHidden || !this.isHidden(i)){
4381                     this.totalWidth += this.getColumnWidth(i);
4382                 }
4383             }
4384         }
4385         return this.totalWidth;
4386     },
4387
4388     /**
4389      * Returns the header for the specified column.
4390      * @param {Number} col The column index
4391      * @return {String}
4392      */
4393     getColumnHeader : function(col){
4394         return this.config[col].header;
4395     },
4396
4397     /**
4398      * Sets the header for a column.
4399      * @param {Number} col The column index
4400      * @param {String} header The new header
4401      */
4402     setColumnHeader : function(col, header){
4403         this.config[col].header = header;
4404         this.fireEvent("headerchange", this, col, header);
4405     },
4406
4407     /**
4408      * Returns the tooltip for the specified column.
4409      * @param {Number} col The column index
4410      * @return {String}
4411      */
4412     getColumnTooltip : function(col){
4413             return this.config[col].tooltip;
4414     },
4415     /**
4416      * Sets the tooltip for a column.
4417      * @param {Number} col The column index
4418      * @param {String} tooltip The new tooltip
4419      */
4420     setColumnTooltip : function(col, tooltip){
4421             this.config[col].tooltip = tooltip;
4422     },
4423
4424     /**
4425      * Returns the dataIndex for the specified column.
4426      * @param {Number} col The column index
4427      * @return {Number}
4428      */
4429     getDataIndex : function(col){
4430         return this.config[col].dataIndex;
4431     },
4432
4433     /**
4434      * Sets the dataIndex for a column.
4435      * @param {Number} col The column index
4436      * @param {Number} dataIndex The new dataIndex
4437      */
4438     setDataIndex : function(col, dataIndex){
4439         this.config[col].dataIndex = dataIndex;
4440     },
4441
4442     
4443     
4444     /**
4445      * Returns true if the cell is editable.
4446      * @param {Number} colIndex The column index
4447      * @param {Number} rowIndex The row index
4448      * @return {Boolean}
4449      */
4450     isCellEditable : function(colIndex, rowIndex){
4451         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
4452     },
4453
4454     /**
4455      * Returns the editor defined for the cell/column.
4456      * return false or null to disable editing.
4457      * @param {Number} colIndex The column index
4458      * @param {Number} rowIndex The row index
4459      * @return {Object}
4460      */
4461     getCellEditor : function(colIndex, rowIndex){
4462         return this.config[colIndex].editor;
4463     },
4464
4465     /**
4466      * Sets if a column is editable.
4467      * @param {Number} col The column index
4468      * @param {Boolean} editable True if the column is editable
4469      */
4470     setEditable : function(col, editable){
4471         this.config[col].editable = editable;
4472     },
4473
4474
4475     /**
4476      * Returns true if the column is hidden.
4477      * @param {Number} colIndex The column index
4478      * @return {Boolean}
4479      */
4480     isHidden : function(colIndex){
4481         return this.config[colIndex].hidden;
4482     },
4483
4484
4485     /**
4486      * Returns true if the column width cannot be changed
4487      */
4488     isFixed : function(colIndex){
4489         return this.config[colIndex].fixed;
4490     },
4491
4492     /**
4493      * Returns true if the column can be resized
4494      * @return {Boolean}
4495      */
4496     isResizable : function(colIndex){
4497         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
4498     },
4499     /**
4500      * Sets if a column is hidden.
4501      * @param {Number} colIndex The column index
4502      * @param {Boolean} hidden True if the column is hidden
4503      */
4504     setHidden : function(colIndex, hidden){
4505         this.config[colIndex].hidden = hidden;
4506         this.totalWidth = null;
4507         this.fireEvent("hiddenchange", this, colIndex, hidden);
4508     },
4509
4510     /**
4511      * Sets the editor for a column.
4512      * @param {Number} col The column index
4513      * @param {Object} editor The editor object
4514      */
4515     setEditor : function(col, editor){
4516         this.config[col].editor = editor;
4517     }
4518 });
4519
4520 Roo.grid.ColumnModel.defaultRenderer = function(value){
4521         if(typeof value == "string" && value.length < 1){
4522             return "&#160;";
4523         }
4524         return value;
4525 };
4526
4527 // Alias for backwards compatibility
4528 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
4529 /*
4530  * Based on:
4531  * Ext JS Library 1.1.1
4532  * Copyright(c) 2006-2007, Ext JS, LLC.
4533  *
4534  * Originally Released Under LGPL - original licence link has changed is not relivant.
4535  *
4536  * Fork - LGPL
4537  * <script type="text/javascript">
4538  */
4539  
4540 /**
4541  * @class Roo.LoadMask
4542  * A simple utility class for generically masking elements while loading data.  If the element being masked has
4543  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
4544  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
4545  * element's UpdateManager load indicator and will be destroyed after the initial load.
4546  * @constructor
4547  * Create a new LoadMask
4548  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
4549  * @param {Object} config The config object
4550  */
4551 Roo.LoadMask = function(el, config){
4552     this.el = Roo.get(el);
4553     Roo.apply(this, config);
4554     if(this.store){
4555         this.store.on('beforeload', this.onBeforeLoad, this);
4556         this.store.on('load', this.onLoad, this);
4557         this.store.on('loadexception', this.onLoadException, this);
4558         this.removeMask = false;
4559     }else{
4560         var um = this.el.getUpdateManager();
4561         um.showLoadIndicator = false; // disable the default indicator
4562         um.on('beforeupdate', this.onBeforeLoad, this);
4563         um.on('update', this.onLoad, this);
4564         um.on('failure', this.onLoad, this);
4565         this.removeMask = true;
4566     }
4567 };
4568
4569 Roo.LoadMask.prototype = {
4570     /**
4571      * @cfg {Boolean} removeMask
4572      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
4573      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
4574      */
4575     /**
4576      * @cfg {String} msg
4577      * The text to display in a centered loading message box (defaults to 'Loading...')
4578      */
4579     msg : 'Loading...',
4580     /**
4581      * @cfg {String} msgCls
4582      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
4583      */
4584     msgCls : 'x-mask-loading',
4585
4586     /**
4587      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
4588      * @type Boolean
4589      */
4590     disabled: false,
4591
4592     /**
4593      * Disables the mask to prevent it from being displayed
4594      */
4595     disable : function(){
4596        this.disabled = true;
4597     },
4598
4599     /**
4600      * Enables the mask so that it can be displayed
4601      */
4602     enable : function(){
4603         this.disabled = false;
4604     },
4605     
4606     onLoadException : function()
4607     {
4608         Roo.log(arguments);
4609         
4610         if (typeof(arguments[3]) != 'undefined') {
4611             Roo.MessageBox.alert("Error loading",arguments[3]);
4612         } 
4613         /*
4614         try {
4615             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
4616                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
4617             }   
4618         } catch(e) {
4619             
4620         }
4621         */
4622     
4623         
4624         
4625         this.el.unmask(this.removeMask);
4626     },
4627     // private
4628     onLoad : function()
4629     {
4630         this.el.unmask(this.removeMask);
4631     },
4632
4633     // private
4634     onBeforeLoad : function(){
4635         if(!this.disabled){
4636             this.el.mask(this.msg, this.msgCls);
4637         }
4638     },
4639
4640     // private
4641     destroy : function(){
4642         if(this.store){
4643             this.store.un('beforeload', this.onBeforeLoad, this);
4644             this.store.un('load', this.onLoad, this);
4645             this.store.un('loadexception', this.onLoadException, this);
4646         }else{
4647             var um = this.el.getUpdateManager();
4648             um.un('beforeupdate', this.onBeforeLoad, this);
4649             um.un('update', this.onLoad, this);
4650             um.un('failure', this.onLoad, this);
4651         }
4652     }
4653 };/*
4654  * - LGPL
4655  *
4656  * table
4657  * 
4658  */
4659
4660 /**
4661  * @class Roo.bootstrap.Table
4662  * @extends Roo.bootstrap.Component
4663  * Bootstrap Table class
4664  * @cfg {String} cls table class
4665  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
4666  * @cfg {String} bgcolor Specifies the background color for a table
4667  * @cfg {Number} border Specifies whether the table cells should have borders or not
4668  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
4669  * @cfg {Number} cellspacing Specifies the space between cells
4670  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
4671  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
4672  * @cfg {String} sortable Specifies that the table should be sortable
4673  * @cfg {String} summary Specifies a summary of the content of a table
4674  * @cfg {Number} width Specifies the width of a table
4675  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
4676  * 
4677  * @cfg {boolean} striped Should the rows be alternative striped
4678  * @cfg {boolean} bordered Add borders to the table
4679  * @cfg {boolean} hover Add hover highlighting
4680  * @cfg {boolean} condensed Format condensed
4681  * @cfg {boolean} responsive Format condensed
4682  * @cfg {Boolean} loadMask (true|false) default false
4683  * @cfg {Boolean} tfoot (true|false) generate tfoot, default true
4684  * @cfg {Boolean} thead (true|false) generate thead, default true
4685  * @cfg {Boolean} RowSelection (true|false) default false
4686  * @cfg {Boolean} CellSelection (true|false) default false
4687  *
4688  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
4689  
4690  * 
4691  * @constructor
4692  * Create a new Table
4693  * @param {Object} config The config object
4694  */
4695
4696 Roo.bootstrap.Table = function(config){
4697     Roo.bootstrap.Table.superclass.constructor.call(this, config);
4698     
4699     if (this.sm) {
4700         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
4701         this.sm = this.selModel;
4702         this.sm.xmodule = this.xmodule || false;
4703     }
4704     if (this.cm && typeof(this.cm.config) == 'undefined') {
4705         this.colModel = new Roo.grid.ColumnModel(this.cm);
4706         this.cm = this.colModel;
4707         this.cm.xmodule = this.xmodule || false;
4708     }
4709     if (this.store) {
4710         this.store= Roo.factory(this.store, Roo.data);
4711         this.ds = this.store;
4712         this.ds.xmodule = this.xmodule || false;
4713          
4714     }
4715     if (this.footer && this.store) {
4716         this.footer.dataSource = this.ds;
4717         this.footer = Roo.factory(this.footer);
4718     }
4719     
4720     /** @private */
4721     this.addEvents({
4722         /**
4723          * @event cellclick
4724          * Fires when a cell is clicked
4725          * @param {Roo.bootstrap.Table} this
4726          * @param {Roo.Element} el
4727          * @param {Number} rowIndex
4728          * @param {Number} columnIndex
4729          * @param {Roo.EventObject} e
4730          */
4731         "cellclick" : true,
4732         /**
4733          * @event celldblclick
4734          * Fires when a cell is double clicked
4735          * @param {Roo.bootstrap.Table} this
4736          * @param {Roo.Element} el
4737          * @param {Number} rowIndex
4738          * @param {Number} columnIndex
4739          * @param {Roo.EventObject} e
4740          */
4741         "celldblclick" : true,
4742         /**
4743          * @event rowclick
4744          * Fires when a row is clicked
4745          * @param {Roo.bootstrap.Table} this
4746          * @param {Roo.Element} el
4747          * @param {Number} rowIndex
4748          * @param {Roo.EventObject} e
4749          */
4750         "rowclick" : true,
4751         /**
4752          * @event rowdblclick
4753          * Fires when a row is double clicked
4754          * @param {Roo.bootstrap.Table} this
4755          * @param {Roo.Element} el
4756          * @param {Number} rowIndex
4757          * @param {Roo.EventObject} e
4758          */
4759         "rowdblclick" : true,
4760         /**
4761          * @event mouseover
4762          * Fires when a mouseover occur
4763          * @param {Roo.bootstrap.Table} this
4764          * @param {Roo.Element} el
4765          * @param {Number} rowIndex
4766          * @param {Number} columnIndex
4767          * @param {Roo.EventObject} e
4768          */
4769         "mouseover" : true,
4770         /**
4771          * @event mouseout
4772          * Fires when a mouseout occur
4773          * @param {Roo.bootstrap.Table} this
4774          * @param {Roo.Element} el
4775          * @param {Number} rowIndex
4776          * @param {Number} columnIndex
4777          * @param {Roo.EventObject} e
4778          */
4779         "mouseout" : true,
4780         /**
4781          * @event rowclass
4782          * Fires when a row is rendered, so you can change add a style to it.
4783          * @param {Roo.bootstrap.Table} this
4784          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
4785          */
4786         'rowclass' : true
4787         
4788     });
4789 };
4790
4791 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
4792     
4793     cls: false,
4794     align: false,
4795     bgcolor: false,
4796     border: false,
4797     cellpadding: false,
4798     cellspacing: false,
4799     frame: false,
4800     rules: false,
4801     sortable: false,
4802     summary: false,
4803     width: false,
4804     striped : false,
4805     bordered: false,
4806     hover:  false,
4807     condensed : false,
4808     responsive : false,
4809     sm : false,
4810     cm : false,
4811     store : false,
4812     loadMask : false,
4813     tfoot : true,
4814     thead : true,
4815     RowSelection : false,
4816     CellSelection : false,
4817     layout : false,
4818     
4819     // Roo.Element - the tbody
4820     mainBody: false, 
4821     
4822     getAutoCreate : function(){
4823         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
4824         
4825         cfg = {
4826             tag: 'table',
4827             cls : 'table',
4828             cn : []
4829         }
4830             
4831         if (this.striped) {
4832             cfg.cls += ' table-striped';
4833         }
4834         
4835         if (this.hover) {
4836             cfg.cls += ' table-hover';
4837         }
4838         if (this.bordered) {
4839             cfg.cls += ' table-bordered';
4840         }
4841         if (this.condensed) {
4842             cfg.cls += ' table-condensed';
4843         }
4844         if (this.responsive) {
4845             cfg.cls += ' table-responsive';
4846         }
4847         
4848         if (this.cls) {
4849             cfg.cls+=  ' ' +this.cls;
4850         }
4851         
4852         // this lot should be simplifed...
4853         
4854         if (this.align) {
4855             cfg.align=this.align;
4856         }
4857         if (this.bgcolor) {
4858             cfg.bgcolor=this.bgcolor;
4859         }
4860         if (this.border) {
4861             cfg.border=this.border;
4862         }
4863         if (this.cellpadding) {
4864             cfg.cellpadding=this.cellpadding;
4865         }
4866         if (this.cellspacing) {
4867             cfg.cellspacing=this.cellspacing;
4868         }
4869         if (this.frame) {
4870             cfg.frame=this.frame;
4871         }
4872         if (this.rules) {
4873             cfg.rules=this.rules;
4874         }
4875         if (this.sortable) {
4876             cfg.sortable=this.sortable;
4877         }
4878         if (this.summary) {
4879             cfg.summary=this.summary;
4880         }
4881         if (this.width) {
4882             cfg.width=this.width;
4883         }
4884         if (this.layout) {
4885             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
4886         }
4887         
4888         if(this.store || this.cm){
4889             if(this.thead){
4890                 cfg.cn.push(this.renderHeader());
4891             }
4892             
4893             cfg.cn.push(this.renderBody());
4894             
4895             if(this.tfoot){
4896                 cfg.cn.push(this.renderFooter());
4897             }
4898             
4899             cfg.cls+=  ' TableGrid';
4900         }
4901         
4902         return { cn : [ cfg ] };
4903     },
4904     
4905     initEvents : function()
4906     {   
4907         if(!this.store || !this.cm){
4908             return;
4909         }
4910         
4911         //Roo.log('initEvents with ds!!!!');
4912         
4913         this.mainBody = this.el.select('tbody', true).first();
4914         
4915         
4916         var _this = this;
4917         
4918         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
4919             e.on('click', _this.sort, _this);
4920         });
4921         
4922         this.el.on("click", this.onClick, this);
4923         this.el.on("dblclick", this.onDblClick, this);
4924         
4925         this.parent().el.setStyle('position', 'relative');
4926         if (this.footer) {
4927             this.footer.parentId = this.id;
4928             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
4929         }
4930         
4931         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
4932         
4933         this.store.on('load', this.onLoad, this);
4934         this.store.on('beforeload', this.onBeforeLoad, this);
4935         this.store.on('update', this.onUpdate, this);
4936         
4937     },
4938     
4939     onMouseover : function(e, el)
4940     {
4941         var cell = Roo.get(el);
4942         
4943         if(!cell){
4944             return;
4945         }
4946         
4947         if(e.getTarget().nodeName.toLowerCase() != 'td'){
4948             cell = cell.findParent('td', false, true);
4949         }
4950         
4951         var row = cell.findParent('tr', false, true);
4952         var cellIndex = cell.dom.cellIndex;
4953         var rowIndex = row.dom.rowIndex - 1; // start from 0
4954         
4955         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
4956         
4957     },
4958     
4959     onMouseout : function(e, el)
4960     {
4961         var cell = Roo.get(el);
4962         
4963         if(!cell){
4964             return;
4965         }
4966         
4967         if(e.getTarget().nodeName.toLowerCase() != 'td'){
4968             cell = cell.findParent('td', false, true);
4969         }
4970         
4971         var row = cell.findParent('tr', false, true);
4972         var cellIndex = cell.dom.cellIndex;
4973         var rowIndex = row.dom.rowIndex - 1; // start from 0
4974         
4975         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
4976         
4977     },
4978     
4979     onClick : function(e, el)
4980     {
4981         var cell = Roo.get(el);
4982         
4983         if(!cell || (!this.CellSelection && !this.RowSelection)){
4984             return;
4985         }
4986         
4987         
4988         if(e.getTarget().nodeName.toLowerCase() != 'td'){
4989             cell = cell.findParent('td', false, true);
4990         }
4991         
4992         var row = cell.findParent('tr', false, true);
4993         var cellIndex = cell.dom.cellIndex;
4994         var rowIndex = row.dom.rowIndex - 1;
4995         
4996         if(this.CellSelection){
4997             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
4998         }
4999         
5000         if(this.RowSelection){
5001             this.fireEvent('rowclick', this, row, rowIndex, e);
5002         }
5003         
5004         
5005     },
5006     
5007     onDblClick : function(e,el)
5008     {
5009         var cell = Roo.get(el);
5010         
5011         if(!cell || (!this.CellSelection && !this.RowSelection)){
5012             return;
5013         }
5014         
5015         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5016             cell = cell.findParent('td', false, true);
5017         }
5018         
5019         var row = cell.findParent('tr', false, true);
5020         var cellIndex = cell.dom.cellIndex;
5021         var rowIndex = row.dom.rowIndex - 1;
5022         
5023         if(this.CellSelection){
5024             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5025         }
5026         
5027         if(this.RowSelection){
5028             this.fireEvent('rowdblclick', this, row, rowIndex, e);
5029         }
5030     },
5031     
5032     sort : function(e,el)
5033     {
5034         var col = Roo.get(el)
5035         
5036         if(!col.hasClass('sortable')){
5037             return;
5038         }
5039         
5040         var sort = col.attr('sort');
5041         var dir = 'ASC';
5042         
5043         if(col.hasClass('glyphicon-arrow-up')){
5044             dir = 'DESC';
5045         }
5046         
5047         this.store.sortInfo = {field : sort, direction : dir};
5048         
5049         if (this.footer) {
5050             Roo.log("calling footer first");
5051             this.footer.onClick('first');
5052         } else {
5053         
5054             this.store.load({ params : { start : 0 } });
5055         }
5056     },
5057     
5058     renderHeader : function()
5059     {
5060         var header = {
5061             tag: 'thead',
5062             cn : []
5063         };
5064         
5065         var cm = this.cm;
5066         
5067         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5068             
5069             var config = cm.config[i];
5070                     
5071             var c = {
5072                 tag: 'th',
5073                 style : '',
5074                 html: cm.getColumnHeader(i)
5075             };
5076             
5077             if(typeof(config.hidden) != 'undefined' && config.hidden){
5078                 c.style += ' display:none;';
5079             }
5080             
5081             if(typeof(config.dataIndex) != 'undefined'){
5082                 c.sort = config.dataIndex;
5083             }
5084             
5085             if(typeof(config.sortable) != 'undefined' && config.sortable){
5086                 c.cls = 'sortable';
5087             }
5088             
5089             if(typeof(config.align) != 'undefined' && config.align.length){
5090                 c.style += ' text-align:' + config.align + ';';
5091             }
5092             
5093             if(typeof(config.width) != 'undefined'){
5094                 c.style += ' width:' + config.width + 'px;';
5095             }
5096             
5097             header.cn.push(c)
5098         }
5099         
5100         return header;
5101     },
5102     
5103     renderBody : function()
5104     {
5105         var body = {
5106             tag: 'tbody',
5107             cn : [
5108                 {
5109                     tag: 'tr',
5110                     cn : [
5111                         {
5112                             tag : 'td',
5113                             colspan :  this.cm.getColumnCount()
5114                         }
5115                     ]
5116                 }
5117             ]
5118         };
5119         
5120         return body;
5121     },
5122     
5123     renderFooter : function()
5124     {
5125         var footer = {
5126             tag: 'tfoot',
5127             cn : [
5128                 {
5129                     tag: 'tr',
5130                     cn : [
5131                         {
5132                             tag : 'td',
5133                             colspan :  this.cm.getColumnCount()
5134                         }
5135                     ]
5136                 }
5137             ]
5138         };
5139         
5140         return footer;
5141     },
5142     
5143     
5144     
5145     onLoad : function()
5146     {
5147         Roo.log('ds onload');
5148         this.clear();
5149         
5150         var _this = this;
5151         var cm = this.cm;
5152         var ds = this.store;
5153         
5154         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5155             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
5156             
5157             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
5158                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
5159             }
5160             
5161             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
5162                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
5163             }
5164         });
5165         
5166         var tbody =  this.mainBody;
5167               
5168         if(ds.getCount() > 0){
5169             ds.data.each(function(d,rowIndex){
5170                 var row =  this.renderRow(cm, ds, rowIndex);
5171                 
5172                 tbody.createChild(row);
5173                 
5174                 var _this = this;
5175                 
5176                 if(row.cellObjects.length){
5177                     Roo.each(row.cellObjects, function(r){
5178                         _this.renderCellObject(r);
5179                     })
5180                 }
5181                 
5182             }, this);
5183         }
5184         
5185         Roo.each(this.el.select('tbody td', true).elements, function(e){
5186             e.on('mouseover', _this.onMouseover, _this);
5187         });
5188         
5189         Roo.each(this.el.select('tbody td', true).elements, function(e){
5190             e.on('mouseout', _this.onMouseout, _this);
5191         });
5192
5193         //if(this.loadMask){
5194         //    this.maskEl.hide();
5195         //}
5196     },
5197     
5198     
5199     onUpdate : function(ds,record)
5200     {
5201         this.refreshRow(record);
5202     },
5203     onRemove : function(ds, record, index, isUpdate){
5204         if(isUpdate !== true){
5205             this.fireEvent("beforerowremoved", this, index, record);
5206         }
5207         var bt = this.mainBody.dom;
5208         if(bt.rows[index]){
5209             bt.removeChild(bt.rows[index]);
5210         }
5211         
5212         if(isUpdate !== true){
5213             //this.stripeRows(index);
5214             //this.syncRowHeights(index, index);
5215             //this.layout();
5216             this.fireEvent("rowremoved", this, index, record);
5217         }
5218     },
5219     
5220     
5221     refreshRow : function(record){
5222         var ds = this.store, index;
5223         if(typeof record == 'number'){
5224             index = record;
5225             record = ds.getAt(index);
5226         }else{
5227             index = ds.indexOf(record);
5228         }
5229         this.insertRow(ds, index, true);
5230         this.onRemove(ds, record, index+1, true);
5231         //this.syncRowHeights(index, index);
5232         //this.layout();
5233         this.fireEvent("rowupdated", this, index, record);
5234     },
5235     
5236     insertRow : function(dm, rowIndex, isUpdate){
5237         
5238         if(!isUpdate){
5239             this.fireEvent("beforerowsinserted", this, rowIndex);
5240         }
5241             //var s = this.getScrollState();
5242         var row = this.renderRow(this.cm, this.store, rowIndex);
5243         // insert before rowIndex..
5244         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
5245         
5246         var _this = this;
5247                 
5248         if(row.cellObjects.length){
5249             Roo.each(row.cellObjects, function(r){
5250                 _this.renderCellObject(r);
5251             })
5252         }
5253             
5254         if(!isUpdate){
5255             this.fireEvent("rowsinserted", this, rowIndex);
5256             //this.syncRowHeights(firstRow, lastRow);
5257             //this.stripeRows(firstRow);
5258             //this.layout();
5259         }
5260         
5261     },
5262     
5263     
5264     getRowDom : function(rowIndex)
5265     {
5266         // not sure if I need to check this.. but let's do it anyway..
5267         return (this.mainBody.dom.rows && (rowIndex-1) < this.mainBody.dom.rows.length ) ?
5268                 this.mainBody.dom.rows[rowIndex] : false
5269     },
5270     // returns the object tree for a tr..
5271   
5272     
5273     renderRow : function(cm, ds, rowIndex) {
5274         
5275         var d = ds.getAt(rowIndex);
5276         
5277         var row = {
5278             tag : 'tr',
5279             cn : []
5280         };
5281             
5282         var cellObjects = [];
5283         
5284         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5285             var config = cm.config[i];
5286             
5287             var renderer = cm.getRenderer(i);
5288             var value = '';
5289             var id = false;
5290             
5291             if(typeof(renderer) !== 'undefined'){
5292                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
5293             }
5294             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
5295             // and are rendered into the cells after the row is rendered - using the id for the element.
5296             
5297             if(typeof(value) === 'object'){
5298                 id = Roo.id();
5299                 cellObjects.push({
5300                     container : id,
5301                     cfg : value 
5302                 })
5303             }
5304             
5305             var rowcfg = {
5306                 record: d,
5307                 rowIndex : rowIndex,
5308                 colIndex : i,
5309                 rowClass : ''
5310             }
5311
5312             this.fireEvent('rowclass', this, rowcfg);
5313             
5314             var td = {
5315                 tag: 'td',
5316                 cls : rowcfg.rowClass,
5317                 style: '',
5318                 html: (typeof(value) === 'object') ? '' : value
5319             };
5320             
5321             if (id) {
5322                 td.id = id;
5323             }
5324             
5325             if(typeof(config.hidden) != 'undefined' && config.hidden){
5326                 td.style += ' display:none;';
5327             }
5328             
5329             if(typeof(config.align) != 'undefined' && config.align.length){
5330                 td.style += ' text-align:' + config.align + ';';
5331             }
5332             
5333             if(typeof(config.width) != 'undefined'){
5334                 td.style += ' width:' +  config.width + 'px;';
5335             }
5336              
5337             row.cn.push(td);
5338            
5339         }
5340         
5341         row.cellObjects = cellObjects;
5342         
5343         return row;
5344           
5345     },
5346     
5347     
5348     
5349     onBeforeLoad : function()
5350     {
5351         //Roo.log('ds onBeforeLoad');
5352         
5353         //this.clear();
5354         
5355         //if(this.loadMask){
5356         //    this.maskEl.show();
5357         //}
5358     },
5359     
5360     clear : function()
5361     {
5362         this.el.select('tbody', true).first().dom.innerHTML = '';
5363     },
5364     
5365     getSelectionModel : function(){
5366         if(!this.selModel){
5367             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
5368         }
5369         return this.selModel;
5370     },
5371     /*
5372      * Render the Roo.bootstrap object from renderder
5373      */
5374     renderCellObject : function(r)
5375     {
5376         var _this = this;
5377         
5378         var t = r.cfg.render(r.container);
5379         
5380         if(r.cfg.cn){
5381             Roo.each(r.cfg.cn, function(c){
5382                 var child = {
5383                     container: t.getChildContainer(),
5384                     cfg: c
5385                 }
5386                 _this.renderCellObject(child);
5387             })
5388         }
5389     }
5390    
5391 });
5392
5393  
5394
5395  /*
5396  * - LGPL
5397  *
5398  * table cell
5399  * 
5400  */
5401
5402 /**
5403  * @class Roo.bootstrap.TableCell
5404  * @extends Roo.bootstrap.Component
5405  * Bootstrap TableCell class
5406  * @cfg {String} html cell contain text
5407  * @cfg {String} cls cell class
5408  * @cfg {String} tag cell tag (td|th) default td
5409  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
5410  * @cfg {String} align Aligns the content in a cell
5411  * @cfg {String} axis Categorizes cells
5412  * @cfg {String} bgcolor Specifies the background color of a cell
5413  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5414  * @cfg {Number} colspan Specifies the number of columns a cell should span
5415  * @cfg {String} headers Specifies one or more header cells a cell is related to
5416  * @cfg {Number} height Sets the height of a cell
5417  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
5418  * @cfg {Number} rowspan Sets the number of rows a cell should span
5419  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
5420  * @cfg {String} valign Vertical aligns the content in a cell
5421  * @cfg {Number} width Specifies the width of a cell
5422  * 
5423  * @constructor
5424  * Create a new TableCell
5425  * @param {Object} config The config object
5426  */
5427
5428 Roo.bootstrap.TableCell = function(config){
5429     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
5430 };
5431
5432 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
5433     
5434     html: false,
5435     cls: false,
5436     tag: false,
5437     abbr: false,
5438     align: false,
5439     axis: false,
5440     bgcolor: false,
5441     charoff: false,
5442     colspan: false,
5443     headers: false,
5444     height: false,
5445     nowrap: false,
5446     rowspan: false,
5447     scope: false,
5448     valign: false,
5449     width: false,
5450     
5451     
5452     getAutoCreate : function(){
5453         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
5454         
5455         cfg = {
5456             tag: 'td'
5457         }
5458         
5459         if(this.tag){
5460             cfg.tag = this.tag;
5461         }
5462         
5463         if (this.html) {
5464             cfg.html=this.html
5465         }
5466         if (this.cls) {
5467             cfg.cls=this.cls
5468         }
5469         if (this.abbr) {
5470             cfg.abbr=this.abbr
5471         }
5472         if (this.align) {
5473             cfg.align=this.align
5474         }
5475         if (this.axis) {
5476             cfg.axis=this.axis
5477         }
5478         if (this.bgcolor) {
5479             cfg.bgcolor=this.bgcolor
5480         }
5481         if (this.charoff) {
5482             cfg.charoff=this.charoff
5483         }
5484         if (this.colspan) {
5485             cfg.colspan=this.colspan
5486         }
5487         if (this.headers) {
5488             cfg.headers=this.headers
5489         }
5490         if (this.height) {
5491             cfg.height=this.height
5492         }
5493         if (this.nowrap) {
5494             cfg.nowrap=this.nowrap
5495         }
5496         if (this.rowspan) {
5497             cfg.rowspan=this.rowspan
5498         }
5499         if (this.scope) {
5500             cfg.scope=this.scope
5501         }
5502         if (this.valign) {
5503             cfg.valign=this.valign
5504         }
5505         if (this.width) {
5506             cfg.width=this.width
5507         }
5508         
5509         
5510         return cfg;
5511     }
5512    
5513 });
5514
5515  
5516
5517  /*
5518  * - LGPL
5519  *
5520  * table row
5521  * 
5522  */
5523
5524 /**
5525  * @class Roo.bootstrap.TableRow
5526  * @extends Roo.bootstrap.Component
5527  * Bootstrap TableRow class
5528  * @cfg {String} cls row class
5529  * @cfg {String} align Aligns the content in a table row
5530  * @cfg {String} bgcolor Specifies a background color for a table row
5531  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5532  * @cfg {String} valign Vertical aligns the content in a table row
5533  * 
5534  * @constructor
5535  * Create a new TableRow
5536  * @param {Object} config The config object
5537  */
5538
5539 Roo.bootstrap.TableRow = function(config){
5540     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
5541 };
5542
5543 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
5544     
5545     cls: false,
5546     align: false,
5547     bgcolor: false,
5548     charoff: false,
5549     valign: false,
5550     
5551     getAutoCreate : function(){
5552         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
5553         
5554         cfg = {
5555             tag: 'tr'
5556         }
5557             
5558         if(this.cls){
5559             cfg.cls = this.cls;
5560         }
5561         if(this.align){
5562             cfg.align = this.align;
5563         }
5564         if(this.bgcolor){
5565             cfg.bgcolor = this.bgcolor;
5566         }
5567         if(this.charoff){
5568             cfg.charoff = this.charoff;
5569         }
5570         if(this.valign){
5571             cfg.valign = this.valign;
5572         }
5573         
5574         return cfg;
5575     }
5576    
5577 });
5578
5579  
5580
5581  /*
5582  * - LGPL
5583  *
5584  * table body
5585  * 
5586  */
5587
5588 /**
5589  * @class Roo.bootstrap.TableBody
5590  * @extends Roo.bootstrap.Component
5591  * Bootstrap TableBody class
5592  * @cfg {String} cls element class
5593  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
5594  * @cfg {String} align Aligns the content inside the element
5595  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
5596  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
5597  * 
5598  * @constructor
5599  * Create a new TableBody
5600  * @param {Object} config The config object
5601  */
5602
5603 Roo.bootstrap.TableBody = function(config){
5604     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
5605 };
5606
5607 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
5608     
5609     cls: false,
5610     tag: false,
5611     align: false,
5612     charoff: false,
5613     valign: false,
5614     
5615     getAutoCreate : function(){
5616         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
5617         
5618         cfg = {
5619             tag: 'tbody'
5620         }
5621             
5622         if (this.cls) {
5623             cfg.cls=this.cls
5624         }
5625         if(this.tag){
5626             cfg.tag = this.tag;
5627         }
5628         
5629         if(this.align){
5630             cfg.align = this.align;
5631         }
5632         if(this.charoff){
5633             cfg.charoff = this.charoff;
5634         }
5635         if(this.valign){
5636             cfg.valign = this.valign;
5637         }
5638         
5639         return cfg;
5640     }
5641     
5642     
5643 //    initEvents : function()
5644 //    {
5645 //        
5646 //        if(!this.store){
5647 //            return;
5648 //        }
5649 //        
5650 //        this.store = Roo.factory(this.store, Roo.data);
5651 //        this.store.on('load', this.onLoad, this);
5652 //        
5653 //        this.store.load();
5654 //        
5655 //    },
5656 //    
5657 //    onLoad: function () 
5658 //    {   
5659 //        this.fireEvent('load', this);
5660 //    }
5661 //    
5662 //   
5663 });
5664
5665  
5666
5667  /*
5668  * Based on:
5669  * Ext JS Library 1.1.1
5670  * Copyright(c) 2006-2007, Ext JS, LLC.
5671  *
5672  * Originally Released Under LGPL - original licence link has changed is not relivant.
5673  *
5674  * Fork - LGPL
5675  * <script type="text/javascript">
5676  */
5677
5678 // as we use this in bootstrap.
5679 Roo.namespace('Roo.form');
5680  /**
5681  * @class Roo.form.Action
5682  * Internal Class used to handle form actions
5683  * @constructor
5684  * @param {Roo.form.BasicForm} el The form element or its id
5685  * @param {Object} config Configuration options
5686  */
5687
5688  
5689  
5690 // define the action interface
5691 Roo.form.Action = function(form, options){
5692     this.form = form;
5693     this.options = options || {};
5694 };
5695 /**
5696  * Client Validation Failed
5697  * @const 
5698  */
5699 Roo.form.Action.CLIENT_INVALID = 'client';
5700 /**
5701  * Server Validation Failed
5702  * @const 
5703  */
5704 Roo.form.Action.SERVER_INVALID = 'server';
5705  /**
5706  * Connect to Server Failed
5707  * @const 
5708  */
5709 Roo.form.Action.CONNECT_FAILURE = 'connect';
5710 /**
5711  * Reading Data from Server Failed
5712  * @const 
5713  */
5714 Roo.form.Action.LOAD_FAILURE = 'load';
5715
5716 Roo.form.Action.prototype = {
5717     type : 'default',
5718     failureType : undefined,
5719     response : undefined,
5720     result : undefined,
5721
5722     // interface method
5723     run : function(options){
5724
5725     },
5726
5727     // interface method
5728     success : function(response){
5729
5730     },
5731
5732     // interface method
5733     handleResponse : function(response){
5734
5735     },
5736
5737     // default connection failure
5738     failure : function(response){
5739         
5740         this.response = response;
5741         this.failureType = Roo.form.Action.CONNECT_FAILURE;
5742         this.form.afterAction(this, false);
5743     },
5744
5745     processResponse : function(response){
5746         this.response = response;
5747         if(!response.responseText){
5748             return true;
5749         }
5750         this.result = this.handleResponse(response);
5751         return this.result;
5752     },
5753
5754     // utility functions used internally
5755     getUrl : function(appendParams){
5756         var url = this.options.url || this.form.url || this.form.el.dom.action;
5757         if(appendParams){
5758             var p = this.getParams();
5759             if(p){
5760                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
5761             }
5762         }
5763         return url;
5764     },
5765
5766     getMethod : function(){
5767         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
5768     },
5769
5770     getParams : function(){
5771         var bp = this.form.baseParams;
5772         var p = this.options.params;
5773         if(p){
5774             if(typeof p == "object"){
5775                 p = Roo.urlEncode(Roo.applyIf(p, bp));
5776             }else if(typeof p == 'string' && bp){
5777                 p += '&' + Roo.urlEncode(bp);
5778             }
5779         }else if(bp){
5780             p = Roo.urlEncode(bp);
5781         }
5782         return p;
5783     },
5784
5785     createCallback : function(){
5786         return {
5787             success: this.success,
5788             failure: this.failure,
5789             scope: this,
5790             timeout: (this.form.timeout*1000),
5791             upload: this.form.fileUpload ? this.success : undefined
5792         };
5793     }
5794 };
5795
5796 Roo.form.Action.Submit = function(form, options){
5797     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
5798 };
5799
5800 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
5801     type : 'submit',
5802
5803     haveProgress : false,
5804     uploadComplete : false,
5805     
5806     // uploadProgress indicator.
5807     uploadProgress : function()
5808     {
5809         if (!this.form.progressUrl) {
5810             return;
5811         }
5812         
5813         if (!this.haveProgress) {
5814             Roo.MessageBox.progress("Uploading", "Uploading");
5815         }
5816         if (this.uploadComplete) {
5817            Roo.MessageBox.hide();
5818            return;
5819         }
5820         
5821         this.haveProgress = true;
5822    
5823         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
5824         
5825         var c = new Roo.data.Connection();
5826         c.request({
5827             url : this.form.progressUrl,
5828             params: {
5829                 id : uid
5830             },
5831             method: 'GET',
5832             success : function(req){
5833                //console.log(data);
5834                 var rdata = false;
5835                 var edata;
5836                 try  {
5837                    rdata = Roo.decode(req.responseText)
5838                 } catch (e) {
5839                     Roo.log("Invalid data from server..");
5840                     Roo.log(edata);
5841                     return;
5842                 }
5843                 if (!rdata || !rdata.success) {
5844                     Roo.log(rdata);
5845                     Roo.MessageBox.alert(Roo.encode(rdata));
5846                     return;
5847                 }
5848                 var data = rdata.data;
5849                 
5850                 if (this.uploadComplete) {
5851                    Roo.MessageBox.hide();
5852                    return;
5853                 }
5854                    
5855                 if (data){
5856                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
5857                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
5858                     );
5859                 }
5860                 this.uploadProgress.defer(2000,this);
5861             },
5862        
5863             failure: function(data) {
5864                 Roo.log('progress url failed ');
5865                 Roo.log(data);
5866             },
5867             scope : this
5868         });
5869            
5870     },
5871     
5872     
5873     run : function()
5874     {
5875         // run get Values on the form, so it syncs any secondary forms.
5876         this.form.getValues();
5877         
5878         var o = this.options;
5879         var method = this.getMethod();
5880         var isPost = method == 'POST';
5881         if(o.clientValidation === false || this.form.isValid()){
5882             
5883             if (this.form.progressUrl) {
5884                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
5885                     (new Date() * 1) + '' + Math.random());
5886                     
5887             } 
5888             
5889             
5890             Roo.Ajax.request(Roo.apply(this.createCallback(), {
5891                 form:this.form.el.dom,
5892                 url:this.getUrl(!isPost),
5893                 method: method,
5894                 params:isPost ? this.getParams() : null,
5895                 isUpload: this.form.fileUpload
5896             }));
5897             
5898             this.uploadProgress();
5899
5900         }else if (o.clientValidation !== false){ // client validation failed
5901             this.failureType = Roo.form.Action.CLIENT_INVALID;
5902             this.form.afterAction(this, false);
5903         }
5904     },
5905
5906     success : function(response)
5907     {
5908         this.uploadComplete= true;
5909         if (this.haveProgress) {
5910             Roo.MessageBox.hide();
5911         }
5912         
5913         
5914         var result = this.processResponse(response);
5915         if(result === true || result.success){
5916             this.form.afterAction(this, true);
5917             return;
5918         }
5919         if(result.errors){
5920             this.form.markInvalid(result.errors);
5921             this.failureType = Roo.form.Action.SERVER_INVALID;
5922         }
5923         this.form.afterAction(this, false);
5924     },
5925     failure : function(response)
5926     {
5927         this.uploadComplete= true;
5928         if (this.haveProgress) {
5929             Roo.MessageBox.hide();
5930         }
5931         
5932         this.response = response;
5933         this.failureType = Roo.form.Action.CONNECT_FAILURE;
5934         this.form.afterAction(this, false);
5935     },
5936     
5937     handleResponse : function(response){
5938         if(this.form.errorReader){
5939             var rs = this.form.errorReader.read(response);
5940             var errors = [];
5941             if(rs.records){
5942                 for(var i = 0, len = rs.records.length; i < len; i++) {
5943                     var r = rs.records[i];
5944                     errors[i] = r.data;
5945                 }
5946             }
5947             if(errors.length < 1){
5948                 errors = null;
5949             }
5950             return {
5951                 success : rs.success,
5952                 errors : errors
5953             };
5954         }
5955         var ret = false;
5956         try {
5957             ret = Roo.decode(response.responseText);
5958         } catch (e) {
5959             ret = {
5960                 success: false,
5961                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
5962                 errors : []
5963             };
5964         }
5965         return ret;
5966         
5967     }
5968 });
5969
5970
5971 Roo.form.Action.Load = function(form, options){
5972     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
5973     this.reader = this.form.reader;
5974 };
5975
5976 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
5977     type : 'load',
5978
5979     run : function(){
5980         
5981         Roo.Ajax.request(Roo.apply(
5982                 this.createCallback(), {
5983                     method:this.getMethod(),
5984                     url:this.getUrl(false),
5985                     params:this.getParams()
5986         }));
5987     },
5988
5989     success : function(response){
5990         
5991         var result = this.processResponse(response);
5992         if(result === true || !result.success || !result.data){
5993             this.failureType = Roo.form.Action.LOAD_FAILURE;
5994             this.form.afterAction(this, false);
5995             return;
5996         }
5997         this.form.clearInvalid();
5998         this.form.setValues(result.data);
5999         this.form.afterAction(this, true);
6000     },
6001
6002     handleResponse : function(response){
6003         if(this.form.reader){
6004             var rs = this.form.reader.read(response);
6005             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
6006             return {
6007                 success : rs.success,
6008                 data : data
6009             };
6010         }
6011         return Roo.decode(response.responseText);
6012     }
6013 });
6014
6015 Roo.form.Action.ACTION_TYPES = {
6016     'load' : Roo.form.Action.Load,
6017     'submit' : Roo.form.Action.Submit
6018 };/*
6019  * - LGPL
6020  *
6021  * form
6022  * 
6023  */
6024
6025 /**
6026  * @class Roo.bootstrap.Form
6027  * @extends Roo.bootstrap.Component
6028  * Bootstrap Form class
6029  * @cfg {String} method  GET | POST (default POST)
6030  * @cfg {String} labelAlign top | left (default top)
6031   * @cfg {String} align left  | right - for navbars
6032
6033  * 
6034  * @constructor
6035  * Create a new Form
6036  * @param {Object} config The config object
6037  */
6038
6039
6040 Roo.bootstrap.Form = function(config){
6041     Roo.bootstrap.Form.superclass.constructor.call(this, config);
6042     this.addEvents({
6043         /**
6044          * @event clientvalidation
6045          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
6046          * @param {Form} this
6047          * @param {Boolean} valid true if the form has passed client-side validation
6048          */
6049         clientvalidation: true,
6050         /**
6051          * @event beforeaction
6052          * Fires before any action is performed. Return false to cancel the action.
6053          * @param {Form} this
6054          * @param {Action} action The action to be performed
6055          */
6056         beforeaction: true,
6057         /**
6058          * @event actionfailed
6059          * Fires when an action fails.
6060          * @param {Form} this
6061          * @param {Action} action The action that failed
6062          */
6063         actionfailed : true,
6064         /**
6065          * @event actioncomplete
6066          * Fires when an action is completed.
6067          * @param {Form} this
6068          * @param {Action} action The action that completed
6069          */
6070         actioncomplete : true
6071     });
6072     
6073 };
6074
6075 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
6076       
6077      /**
6078      * @cfg {String} method
6079      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
6080      */
6081     method : 'POST',
6082     /**
6083      * @cfg {String} url
6084      * The URL to use for form actions if one isn't supplied in the action options.
6085      */
6086     /**
6087      * @cfg {Boolean} fileUpload
6088      * Set to true if this form is a file upload.
6089      */
6090      
6091     /**
6092      * @cfg {Object} baseParams
6093      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
6094      */
6095       
6096     /**
6097      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
6098      */
6099     timeout: 30,
6100     /**
6101      * @cfg {Sting} align (left|right) for navbar forms
6102      */
6103     align : 'left',
6104
6105     // private
6106     activeAction : null,
6107  
6108     /**
6109      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
6110      * element by passing it or its id or mask the form itself by passing in true.
6111      * @type Mixed
6112      */
6113     waitMsgTarget : false,
6114     
6115      
6116     
6117     /**
6118      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
6119      * element by passing it or its id or mask the form itself by passing in true.
6120      * @type Mixed
6121      */
6122     
6123     getAutoCreate : function(){
6124         
6125         var cfg = {
6126             tag: 'form',
6127             method : this.method || 'POST',
6128             id : this.id || Roo.id(),
6129             cls : ''
6130         }
6131         if (this.parent().xtype.match(/^Nav/)) {
6132             cfg.cls = 'navbar-form navbar-' + this.align;
6133             
6134         }
6135         
6136         if (this.labelAlign == 'left' ) {
6137             cfg.cls += ' form-horizontal';
6138         }
6139         
6140         
6141         return cfg;
6142     },
6143     initEvents : function()
6144     {
6145         this.el.on('submit', this.onSubmit, this);
6146         // this was added as random key presses on the form where triggering form submit.
6147         this.el.on('keypress', function(e) {
6148             if (e.getCharCode() != 13) {
6149                 return true;
6150             }
6151             // we might need to allow it for textareas.. and some other items.
6152             // check e.getTarget().
6153             
6154             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
6155                 return true;
6156             }
6157         
6158             Roo.log("keypress blocked");
6159             
6160             e.preventDefault();
6161             return false;
6162         });
6163         
6164     },
6165     // private
6166     onSubmit : function(e){
6167         e.stopEvent();
6168     },
6169     
6170      /**
6171      * Returns true if client-side validation on the form is successful.
6172      * @return Boolean
6173      */
6174     isValid : function(){
6175         var items = this.getItems();
6176         var valid = true;
6177         items.each(function(f){
6178            if(!f.validate()){
6179                valid = false;
6180                
6181            }
6182         });
6183         return valid;
6184     },
6185     /**
6186      * Returns true if any fields in this form have changed since their original load.
6187      * @return Boolean
6188      */
6189     isDirty : function(){
6190         var dirty = false;
6191         var items = this.getItems();
6192         items.each(function(f){
6193            if(f.isDirty()){
6194                dirty = true;
6195                return false;
6196            }
6197            return true;
6198         });
6199         return dirty;
6200     },
6201      /**
6202      * Performs a predefined action (submit or load) or custom actions you define on this form.
6203      * @param {String} actionName The name of the action type
6204      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
6205      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
6206      * accept other config options):
6207      * <pre>
6208 Property          Type             Description
6209 ----------------  ---------------  ----------------------------------------------------------------------------------
6210 url               String           The url for the action (defaults to the form's url)
6211 method            String           The form method to use (defaults to the form's method, or POST if not defined)
6212 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
6213 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
6214                                    validate the form on the client (defaults to false)
6215      * </pre>
6216      * @return {BasicForm} this
6217      */
6218     doAction : function(action, options){
6219         if(typeof action == 'string'){
6220             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
6221         }
6222         if(this.fireEvent('beforeaction', this, action) !== false){
6223             this.beforeAction(action);
6224             action.run.defer(100, action);
6225         }
6226         return this;
6227     },
6228     
6229     // private
6230     beforeAction : function(action){
6231         var o = action.options;
6232         
6233         // not really supported yet.. ??
6234         
6235         //if(this.waitMsgTarget === true){
6236             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6237         //}else if(this.waitMsgTarget){
6238         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
6239         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
6240         //}else {
6241         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
6242        // }
6243          
6244     },
6245
6246     // private
6247     afterAction : function(action, success){
6248         this.activeAction = null;
6249         var o = action.options;
6250         
6251         //if(this.waitMsgTarget === true){
6252             this.el.unmask();
6253         //}else if(this.waitMsgTarget){
6254         //    this.waitMsgTarget.unmask();
6255         //}else{
6256         //    Roo.MessageBox.updateProgress(1);
6257         //    Roo.MessageBox.hide();
6258        // }
6259         // 
6260         if(success){
6261             if(o.reset){
6262                 this.reset();
6263             }
6264             Roo.callback(o.success, o.scope, [this, action]);
6265             this.fireEvent('actioncomplete', this, action);
6266             
6267         }else{
6268             
6269             // failure condition..
6270             // we have a scenario where updates need confirming.
6271             // eg. if a locking scenario exists..
6272             // we look for { errors : { needs_confirm : true }} in the response.
6273             if (
6274                 (typeof(action.result) != 'undefined')  &&
6275                 (typeof(action.result.errors) != 'undefined')  &&
6276                 (typeof(action.result.errors.needs_confirm) != 'undefined')
6277            ){
6278                 var _t = this;
6279                 Roo.log("not supported yet");
6280                  /*
6281                 
6282                 Roo.MessageBox.confirm(
6283                     "Change requires confirmation",
6284                     action.result.errorMsg,
6285                     function(r) {
6286                         if (r != 'yes') {
6287                             return;
6288                         }
6289                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
6290                     }
6291                     
6292                 );
6293                 */
6294                 
6295                 
6296                 return;
6297             }
6298             
6299             Roo.callback(o.failure, o.scope, [this, action]);
6300             // show an error message if no failed handler is set..
6301             if (!this.hasListener('actionfailed')) {
6302                 Roo.log("need to add dialog support");
6303                 /*
6304                 Roo.MessageBox.alert("Error",
6305                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
6306                         action.result.errorMsg :
6307                         "Saving Failed, please check your entries or try again"
6308                 );
6309                 */
6310             }
6311             
6312             this.fireEvent('actionfailed', this, action);
6313         }
6314         
6315     },
6316     /**
6317      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
6318      * @param {String} id The value to search for
6319      * @return Field
6320      */
6321     findField : function(id){
6322         var items = this.getItems();
6323         var field = items.get(id);
6324         if(!field){
6325              items.each(function(f){
6326                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
6327                     field = f;
6328                     return false;
6329                 }
6330                 return true;
6331             });
6332         }
6333         return field || null;
6334     },
6335      /**
6336      * Mark fields in this form invalid in bulk.
6337      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
6338      * @return {BasicForm} this
6339      */
6340     markInvalid : function(errors){
6341         if(errors instanceof Array){
6342             for(var i = 0, len = errors.length; i < len; i++){
6343                 var fieldError = errors[i];
6344                 var f = this.findField(fieldError.id);
6345                 if(f){
6346                     f.markInvalid(fieldError.msg);
6347                 }
6348             }
6349         }else{
6350             var field, id;
6351             for(id in errors){
6352                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
6353                     field.markInvalid(errors[id]);
6354                 }
6355             }
6356         }
6357         //Roo.each(this.childForms || [], function (f) {
6358         //    f.markInvalid(errors);
6359         //});
6360         
6361         return this;
6362     },
6363
6364     /**
6365      * Set values for fields in this form in bulk.
6366      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
6367      * @return {BasicForm} this
6368      */
6369     setValues : function(values){
6370         if(values instanceof Array){ // array of objects
6371             for(var i = 0, len = values.length; i < len; i++){
6372                 var v = values[i];
6373                 var f = this.findField(v.id);
6374                 if(f){
6375                     f.setValue(v.value);
6376                     if(this.trackResetOnLoad){
6377                         f.originalValue = f.getValue();
6378                     }
6379                 }
6380             }
6381         }else{ // object hash
6382             var field, id;
6383             for(id in values){
6384                 if(typeof values[id] != 'function' && (field = this.findField(id))){
6385                     
6386                     if (field.setFromData && 
6387                         field.valueField && 
6388                         field.displayField &&
6389                         // combos' with local stores can 
6390                         // be queried via setValue()
6391                         // to set their value..
6392                         (field.store && !field.store.isLocal)
6393                         ) {
6394                         // it's a combo
6395                         var sd = { };
6396                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
6397                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
6398                         field.setFromData(sd);
6399                         
6400                     } else {
6401                         field.setValue(values[id]);
6402                     }
6403                     
6404                     
6405                     if(this.trackResetOnLoad){
6406                         field.originalValue = field.getValue();
6407                     }
6408                 }
6409             }
6410         }
6411          
6412         //Roo.each(this.childForms || [], function (f) {
6413         //    f.setValues(values);
6414         //});
6415                 
6416         return this;
6417     },
6418
6419     /**
6420      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
6421      * they are returned as an array.
6422      * @param {Boolean} asString
6423      * @return {Object}
6424      */
6425     getValues : function(asString){
6426         //if (this.childForms) {
6427             // copy values from the child forms
6428         //    Roo.each(this.childForms, function (f) {
6429         //        this.setValues(f.getValues());
6430         //    }, this);
6431         //}
6432         
6433         
6434         
6435         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
6436         if(asString === true){
6437             return fs;
6438         }
6439         return Roo.urlDecode(fs);
6440     },
6441     
6442     /**
6443      * Returns the fields in this form as an object with key/value pairs. 
6444      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
6445      * @return {Object}
6446      */
6447     getFieldValues : function(with_hidden)
6448     {
6449         var items = this.getItems();
6450         var ret = {};
6451         items.each(function(f){
6452             if (!f.getName()) {
6453                 return;
6454             }
6455             var v = f.getValue();
6456             if (f.inputType =='radio') {
6457                 if (typeof(ret[f.getName()]) == 'undefined') {
6458                     ret[f.getName()] = ''; // empty..
6459                 }
6460                 
6461                 if (!f.el.dom.checked) {
6462                     return;
6463                     
6464                 }
6465                 v = f.el.dom.value;
6466                 
6467             }
6468             
6469             // not sure if this supported any more..
6470             if ((typeof(v) == 'object') && f.getRawValue) {
6471                 v = f.getRawValue() ; // dates..
6472             }
6473             // combo boxes where name != hiddenName...
6474             if (f.name != f.getName()) {
6475                 ret[f.name] = f.getRawValue();
6476             }
6477             ret[f.getName()] = v;
6478         });
6479         
6480         return ret;
6481     },
6482
6483     /**
6484      * Clears all invalid messages in this form.
6485      * @return {BasicForm} this
6486      */
6487     clearInvalid : function(){
6488         var items = this.getItems();
6489         
6490         items.each(function(f){
6491            f.clearInvalid();
6492         });
6493         
6494         
6495         
6496         return this;
6497     },
6498
6499     /**
6500      * Resets this form.
6501      * @return {BasicForm} this
6502      */
6503     reset : function(){
6504         var items = this.getItems();
6505         items.each(function(f){
6506             f.reset();
6507         });
6508         
6509         Roo.each(this.childForms || [], function (f) {
6510             f.reset();
6511         });
6512        
6513         
6514         return this;
6515     },
6516     getItems : function()
6517     {
6518         var r=new Roo.util.MixedCollection(false, function(o){
6519             return o.id || (o.id = Roo.id());
6520         });
6521         var iter = function(el) {
6522             if (el.inputEl) {
6523                 r.add(el);
6524             }
6525             if (!el.items) {
6526                 return;
6527             }
6528             Roo.each(el.items,function(e) {
6529                 iter(e);
6530             });
6531             
6532             
6533         };
6534         iter(this);
6535         return r;
6536         
6537         
6538         
6539         
6540     }
6541     
6542 });
6543
6544  
6545 /*
6546  * Based on:
6547  * Ext JS Library 1.1.1
6548  * Copyright(c) 2006-2007, Ext JS, LLC.
6549  *
6550  * Originally Released Under LGPL - original licence link has changed is not relivant.
6551  *
6552  * Fork - LGPL
6553  * <script type="text/javascript">
6554  */
6555 /**
6556  * @class Roo.form.VTypes
6557  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
6558  * @singleton
6559  */
6560 Roo.form.VTypes = function(){
6561     // closure these in so they are only created once.
6562     var alpha = /^[a-zA-Z_]+$/;
6563     var alphanum = /^[a-zA-Z0-9_]+$/;
6564     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
6565     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
6566
6567     // All these messages and functions are configurable
6568     return {
6569         /**
6570          * The function used to validate email addresses
6571          * @param {String} value The email address
6572          */
6573         'email' : function(v){
6574             return email.test(v);
6575         },
6576         /**
6577          * The error text to display when the email validation function returns false
6578          * @type String
6579          */
6580         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
6581         /**
6582          * The keystroke filter mask to be applied on email input
6583          * @type RegExp
6584          */
6585         'emailMask' : /[a-z0-9_\.\-@]/i,
6586
6587         /**
6588          * The function used to validate URLs
6589          * @param {String} value The URL
6590          */
6591         'url' : function(v){
6592             return url.test(v);
6593         },
6594         /**
6595          * The error text to display when the url validation function returns false
6596          * @type String
6597          */
6598         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
6599         
6600         /**
6601          * The function used to validate alpha values
6602          * @param {String} value The value
6603          */
6604         'alpha' : function(v){
6605             return alpha.test(v);
6606         },
6607         /**
6608          * The error text to display when the alpha validation function returns false
6609          * @type String
6610          */
6611         'alphaText' : 'This field should only contain letters and _',
6612         /**
6613          * The keystroke filter mask to be applied on alpha input
6614          * @type RegExp
6615          */
6616         'alphaMask' : /[a-z_]/i,
6617
6618         /**
6619          * The function used to validate alphanumeric values
6620          * @param {String} value The value
6621          */
6622         'alphanum' : function(v){
6623             return alphanum.test(v);
6624         },
6625         /**
6626          * The error text to display when the alphanumeric validation function returns false
6627          * @type String
6628          */
6629         'alphanumText' : 'This field should only contain letters, numbers and _',
6630         /**
6631          * The keystroke filter mask to be applied on alphanumeric input
6632          * @type RegExp
6633          */
6634         'alphanumMask' : /[a-z0-9_]/i
6635     };
6636 }();/*
6637  * - LGPL
6638  *
6639  * Input
6640  * 
6641  */
6642
6643 /**
6644  * @class Roo.bootstrap.Input
6645  * @extends Roo.bootstrap.Component
6646  * Bootstrap Input class
6647  * @cfg {Boolean} disabled is it disabled
6648  * @cfg {String} fieldLabel - the label associated
6649  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
6650  * @cfg {String} name name of the input
6651  * @cfg {string} fieldLabel - the label associated
6652  * @cfg {string}  inputType - input / file submit ...
6653  * @cfg {string} placeholder - placeholder to put in text.
6654  * @cfg {string}  before - input group add on before
6655  * @cfg {string} after - input group add on after
6656  * @cfg {string} size - (lg|sm) or leave empty..
6657  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
6658  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
6659  * @cfg {Number} md colspan out of 12 for computer-sized screens
6660  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
6661  * @cfg {string} value default value of the input
6662  * @cfg {Number} labelWidth set the width of label (0-12)
6663  * @cfg {String} labelAlign (top|left)
6664  * @cfg {Boolean} readOnly Specifies that the field should be read-only
6665  * @cfg {String} align (left|center|right) Default left
6666  * 
6667  * 
6668  * @constructor
6669  * Create a new Input
6670  * @param {Object} config The config object
6671  */
6672
6673 Roo.bootstrap.Input = function(config){
6674     Roo.bootstrap.Input.superclass.constructor.call(this, config);
6675    
6676         this.addEvents({
6677             /**
6678              * @event focus
6679              * Fires when this field receives input focus.
6680              * @param {Roo.form.Field} this
6681              */
6682             focus : true,
6683             /**
6684              * @event blur
6685              * Fires when this field loses input focus.
6686              * @param {Roo.form.Field} this
6687              */
6688             blur : true,
6689             /**
6690              * @event specialkey
6691              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
6692              * {@link Roo.EventObject#getKey} to determine which key was pressed.
6693              * @param {Roo.form.Field} this
6694              * @param {Roo.EventObject} e The event object
6695              */
6696             specialkey : true,
6697             /**
6698              * @event change
6699              * Fires just before the field blurs if the field value has changed.
6700              * @param {Roo.form.Field} this
6701              * @param {Mixed} newValue The new value
6702              * @param {Mixed} oldValue The original value
6703              */
6704             change : true,
6705             /**
6706              * @event invalid
6707              * Fires after the field has been marked as invalid.
6708              * @param {Roo.form.Field} this
6709              * @param {String} msg The validation message
6710              */
6711             invalid : true,
6712             /**
6713              * @event valid
6714              * Fires after the field has been validated with no errors.
6715              * @param {Roo.form.Field} this
6716              */
6717             valid : true,
6718              /**
6719              * @event keyup
6720              * Fires after the key up
6721              * @param {Roo.form.Field} this
6722              * @param {Roo.EventObject}  e The event Object
6723              */
6724             keyup : true
6725         });
6726 };
6727
6728 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
6729      /**
6730      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
6731       automatic validation (defaults to "keyup").
6732      */
6733     validationEvent : "keyup",
6734      /**
6735      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
6736      */
6737     validateOnBlur : true,
6738     /**
6739      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
6740      */
6741     validationDelay : 250,
6742      /**
6743      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
6744      */
6745     focusClass : "x-form-focus",  // not needed???
6746     
6747        
6748     /**
6749      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
6750      */
6751     invalidClass : "has-error",
6752     
6753     /**
6754      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
6755      */
6756     selectOnFocus : false,
6757     
6758      /**
6759      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
6760      */
6761     maskRe : null,
6762        /**
6763      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
6764      */
6765     vtype : null,
6766     
6767       /**
6768      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
6769      */
6770     disableKeyFilter : false,
6771     
6772        /**
6773      * @cfg {Boolean} disabled True to disable the field (defaults to false).
6774      */
6775     disabled : false,
6776      /**
6777      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
6778      */
6779     allowBlank : true,
6780     /**
6781      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
6782      */
6783     blankText : "This field is required",
6784     
6785      /**
6786      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
6787      */
6788     minLength : 0,
6789     /**
6790      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
6791      */
6792     maxLength : Number.MAX_VALUE,
6793     /**
6794      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
6795      */
6796     minLengthText : "The minimum length for this field is {0}",
6797     /**
6798      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
6799      */
6800     maxLengthText : "The maximum length for this field is {0}",
6801   
6802     
6803     /**
6804      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
6805      * If available, this function will be called only after the basic validators all return true, and will be passed the
6806      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
6807      */
6808     validator : null,
6809     /**
6810      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
6811      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
6812      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
6813      */
6814     regex : null,
6815     /**
6816      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
6817      */
6818     regexText : "",
6819     
6820     
6821     
6822     fieldLabel : '',
6823     inputType : 'text',
6824     
6825     name : false,
6826     placeholder: false,
6827     before : false,
6828     after : false,
6829     size : false,
6830     // private
6831     hasFocus : false,
6832     preventMark: false,
6833     isFormField : true,
6834     value : '',
6835     labelWidth : 2,
6836     labelAlign : false,
6837     readOnly : false,
6838     align : false,
6839     formatedValue : false,
6840     
6841     parentLabelAlign : function()
6842     {
6843         var parent = this;
6844         while (parent.parent()) {
6845             parent = parent.parent();
6846             if (typeof(parent.labelAlign) !='undefined') {
6847                 return parent.labelAlign;
6848             }
6849         }
6850         return 'left';
6851         
6852     },
6853     
6854     getAutoCreate : function(){
6855         
6856         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
6857         
6858         var id = Roo.id();
6859         
6860         var cfg = {};
6861         
6862         if(this.inputType != 'hidden'){
6863             cfg.cls = 'form-group' //input-group
6864         }
6865         
6866         var input =  {
6867             tag: 'input',
6868             id : id,
6869             type : this.inputType,
6870             value : this.value,
6871             cls : 'form-control',
6872             placeholder : this.placeholder || ''
6873             
6874         };
6875         
6876         if(this.align){
6877             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
6878         }
6879         
6880         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
6881             input.maxLength = this.maxLength;
6882         }
6883         
6884         if (this.disabled) {
6885             input.disabled=true;
6886         }
6887         
6888         if (this.readOnly) {
6889             input.readonly=true;
6890         }
6891         
6892         if (this.name) {
6893             input.name = this.name;
6894         }
6895         if (this.size) {
6896             input.cls += ' input-' + this.size;
6897         }
6898         var settings=this;
6899         ['xs','sm','md','lg'].map(function(size){
6900             if (settings[size]) {
6901                 cfg.cls += ' col-' + size + '-' + settings[size];
6902             }
6903         });
6904         
6905         var inputblock = input;
6906         
6907         if (this.before || this.after) {
6908             
6909             inputblock = {
6910                 cls : 'input-group',
6911                 cn :  [] 
6912             };
6913             if (this.before && typeof(this.before) == 'string') {
6914                 
6915                 inputblock.cn.push({
6916                     tag :'span',
6917                     cls : 'roo-input-before input-group-addon',
6918                     html : this.before
6919                 });
6920             }
6921             if (this.before && typeof(this.before) == 'object') {
6922                 this.before = Roo.factory(this.before);
6923                 Roo.log(this.before);
6924                 inputblock.cn.push({
6925                     tag :'span',
6926                     cls : 'roo-input-before input-group-' +
6927                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
6928                 });
6929             }
6930             
6931             inputblock.cn.push(input);
6932             
6933             if (this.after && typeof(this.after) == 'string') {
6934                 inputblock.cn.push({
6935                     tag :'span',
6936                     cls : 'roo-input-after input-group-addon',
6937                     html : this.after
6938                 });
6939             }
6940             if (this.after && typeof(this.after) == 'object') {
6941                 this.after = Roo.factory(this.after);
6942                 Roo.log(this.after);
6943                 inputblock.cn.push({
6944                     tag :'span',
6945                     cls : 'roo-input-after input-group-' +
6946                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
6947                 });
6948             }
6949         };
6950         
6951         if (align ==='left' && this.fieldLabel.length) {
6952                 Roo.log("left and has label");
6953                 cfg.cn = [
6954                     
6955                     {
6956                         tag: 'label',
6957                         'for' :  id,
6958                         cls : 'control-label col-sm-' + this.labelWidth,
6959                         html : this.fieldLabel
6960                         
6961                     },
6962                     {
6963                         cls : "col-sm-" + (12 - this.labelWidth), 
6964                         cn: [
6965                             inputblock
6966                         ]
6967                     }
6968                     
6969                 ];
6970         } else if ( this.fieldLabel.length) {
6971                 Roo.log(" label");
6972                  cfg.cn = [
6973                    
6974                     {
6975                         tag: 'label',
6976                         //cls : 'input-group-addon',
6977                         html : this.fieldLabel
6978                         
6979                     },
6980                     
6981                     inputblock
6982                     
6983                 ];
6984
6985         } else {
6986             
6987                 Roo.log(" no label && no align");
6988                 cfg.cn = [
6989                     
6990                         inputblock
6991                     
6992                 ];
6993                 
6994                 
6995         };
6996         Roo.log('input-parentType: ' + this.parentType);
6997         
6998         if (this.parentType === 'Navbar' &&  this.parent().bar) {
6999            cfg.cls += ' navbar-form';
7000            Roo.log(cfg);
7001         }
7002         
7003         return cfg;
7004         
7005     },
7006     /**
7007      * return the real input element.
7008      */
7009     inputEl: function ()
7010     {
7011         return this.el.select('input.form-control',true).first();
7012     },
7013     setDisabled : function(v)
7014     {
7015         var i  = this.inputEl().dom;
7016         if (!v) {
7017             i.removeAttribute('disabled');
7018             return;
7019             
7020         }
7021         i.setAttribute('disabled','true');
7022     },
7023     initEvents : function()
7024     {
7025         
7026         this.inputEl().on("keydown" , this.fireKey,  this);
7027         this.inputEl().on("focus", this.onFocus,  this);
7028         this.inputEl().on("blur", this.onBlur,  this);
7029         
7030         this.inputEl().relayEvent('keyup', this);
7031
7032         // reference to original value for reset
7033         this.originalValue = this.getValue();
7034         //Roo.form.TextField.superclass.initEvents.call(this);
7035         if(this.validationEvent == 'keyup'){
7036             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
7037             this.inputEl().on('keyup', this.filterValidation, this);
7038         }
7039         else if(this.validationEvent !== false){
7040             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
7041         }
7042         
7043         if(this.selectOnFocus){
7044             this.on("focus", this.preFocus, this);
7045             
7046         }
7047         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
7048             this.inputEl().on("keypress", this.filterKeys, this);
7049         }
7050        /* if(this.grow){
7051             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
7052             this.el.on("click", this.autoSize,  this);
7053         }
7054         */
7055         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
7056             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
7057         }
7058         
7059         if (typeof(this.before) == 'object') {
7060             this.before.render(this.el.select('.roo-input-before',true).first());
7061         }
7062         if (typeof(this.after) == 'object') {
7063             this.after.render(this.el.select('.roo-input-after',true).first());
7064         }
7065         
7066         
7067     },
7068     filterValidation : function(e){
7069         if(!e.isNavKeyPress()){
7070             this.validationTask.delay(this.validationDelay);
7071         }
7072     },
7073      /**
7074      * Validates the field value
7075      * @return {Boolean} True if the value is valid, else false
7076      */
7077     validate : function(){
7078         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
7079         if(this.disabled || this.validateValue(this.getRawValue())){
7080             this.clearInvalid();
7081             return true;
7082         }
7083         return false;
7084     },
7085     
7086     
7087     /**
7088      * Validates a value according to the field's validation rules and marks the field as invalid
7089      * if the validation fails
7090      * @param {Mixed} value The value to validate
7091      * @return {Boolean} True if the value is valid, else false
7092      */
7093     validateValue : function(value){
7094         if(value.length < 1)  { // if it's blank
7095              if(this.allowBlank){
7096                 this.clearInvalid();
7097                 return true;
7098              }else{
7099                 this.markInvalid(this.blankText);
7100                 return false;
7101              }
7102         }
7103         if(value.length < this.minLength){
7104             this.markInvalid(String.format(this.minLengthText, this.minLength));
7105             return false;
7106         }
7107         if(value.length > this.maxLength){
7108             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
7109             return false;
7110         }
7111         if(this.vtype){
7112             var vt = Roo.form.VTypes;
7113             if(!vt[this.vtype](value, this)){
7114                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
7115                 return false;
7116             }
7117         }
7118         if(typeof this.validator == "function"){
7119             var msg = this.validator(value);
7120             if(msg !== true){
7121                 this.markInvalid(msg);
7122                 return false;
7123             }
7124         }
7125         if(this.regex && !this.regex.test(value)){
7126             this.markInvalid(this.regexText);
7127             return false;
7128         }
7129         return true;
7130     },
7131
7132     
7133     
7134      // private
7135     fireKey : function(e){
7136         //Roo.log('field ' + e.getKey());
7137         if(e.isNavKeyPress()){
7138             this.fireEvent("specialkey", this, e);
7139         }
7140     },
7141     focus : function (selectText){
7142         if(this.rendered){
7143             this.inputEl().focus();
7144             if(selectText === true){
7145                 this.inputEl().dom.select();
7146             }
7147         }
7148         return this;
7149     } ,
7150     
7151     onFocus : function(){
7152         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7153            // this.el.addClass(this.focusClass);
7154         }
7155         if(!this.hasFocus){
7156             this.hasFocus = true;
7157             this.startValue = this.getValue();
7158             this.fireEvent("focus", this);
7159         }
7160     },
7161     
7162     beforeBlur : Roo.emptyFn,
7163
7164     
7165     // private
7166     onBlur : function(){
7167         this.beforeBlur();
7168         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7169             //this.el.removeClass(this.focusClass);
7170         }
7171         this.hasFocus = false;
7172         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
7173             this.validate();
7174         }
7175         var v = this.getValue();
7176         if(String(v) !== String(this.startValue)){
7177             this.fireEvent('change', this, v, this.startValue);
7178         }
7179         this.fireEvent("blur", this);
7180     },
7181     
7182     /**
7183      * Resets the current field value to the originally loaded value and clears any validation messages
7184      */
7185     reset : function(){
7186         this.setValue(this.originalValue);
7187         this.clearInvalid();
7188     },
7189      /**
7190      * Returns the name of the field
7191      * @return {Mixed} name The name field
7192      */
7193     getName: function(){
7194         return this.name;
7195     },
7196      /**
7197      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
7198      * @return {Mixed} value The field value
7199      */
7200     getValue : function(){
7201         
7202         var v = this.inputEl().getValue();
7203         
7204         return v;
7205     },
7206     /**
7207      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
7208      * @return {Mixed} value The field value
7209      */
7210     getRawValue : function(){
7211         var v = this.inputEl().getValue();
7212         
7213         return v;
7214     },
7215     
7216     /**
7217      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
7218      * @param {Mixed} value The value to set
7219      */
7220     setRawValue : function(v){
7221         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7222     },
7223     
7224     selectText : function(start, end){
7225         var v = this.getRawValue();
7226         if(v.length > 0){
7227             start = start === undefined ? 0 : start;
7228             end = end === undefined ? v.length : end;
7229             var d = this.inputEl().dom;
7230             if(d.setSelectionRange){
7231                 d.setSelectionRange(start, end);
7232             }else if(d.createTextRange){
7233                 var range = d.createTextRange();
7234                 range.moveStart("character", start);
7235                 range.moveEnd("character", v.length-end);
7236                 range.select();
7237             }
7238         }
7239     },
7240     
7241     /**
7242      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
7243      * @param {Mixed} value The value to set
7244      */
7245     setValue : function(v){
7246         this.value = v;
7247         if(this.rendered){
7248             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7249             this.validate();
7250         }
7251     },
7252     
7253     /*
7254     processValue : function(value){
7255         if(this.stripCharsRe){
7256             var newValue = value.replace(this.stripCharsRe, '');
7257             if(newValue !== value){
7258                 this.setRawValue(newValue);
7259                 return newValue;
7260             }
7261         }
7262         return value;
7263     },
7264   */
7265     preFocus : function(){
7266         
7267         if(this.selectOnFocus){
7268             this.inputEl().dom.select();
7269         }
7270     },
7271     filterKeys : function(e){
7272         var k = e.getKey();
7273         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
7274             return;
7275         }
7276         var c = e.getCharCode(), cc = String.fromCharCode(c);
7277         if(Roo.isIE && (e.isSpecialKey() || !cc)){
7278             return;
7279         }
7280         if(!this.maskRe.test(cc)){
7281             e.stopEvent();
7282         }
7283     },
7284      /**
7285      * Clear any invalid styles/messages for this field
7286      */
7287     clearInvalid : function(){
7288         
7289         if(!this.el || this.preventMark){ // not rendered
7290             return;
7291         }
7292         this.el.removeClass(this.invalidClass);
7293         /*
7294         switch(this.msgTarget){
7295             case 'qtip':
7296                 this.el.dom.qtip = '';
7297                 break;
7298             case 'title':
7299                 this.el.dom.title = '';
7300                 break;
7301             case 'under':
7302                 if(this.errorEl){
7303                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
7304                 }
7305                 break;
7306             case 'side':
7307                 if(this.errorIcon){
7308                     this.errorIcon.dom.qtip = '';
7309                     this.errorIcon.hide();
7310                     this.un('resize', this.alignErrorIcon, this);
7311                 }
7312                 break;
7313             default:
7314                 var t = Roo.getDom(this.msgTarget);
7315                 t.innerHTML = '';
7316                 t.style.display = 'none';
7317                 break;
7318         }
7319         */
7320         this.fireEvent('valid', this);
7321     },
7322      /**
7323      * Mark this field as invalid
7324      * @param {String} msg The validation message
7325      */
7326     markInvalid : function(msg){
7327         if(!this.el  || this.preventMark){ // not rendered
7328             return;
7329         }
7330         this.el.addClass(this.invalidClass);
7331         /*
7332         msg = msg || this.invalidText;
7333         switch(this.msgTarget){
7334             case 'qtip':
7335                 this.el.dom.qtip = msg;
7336                 this.el.dom.qclass = 'x-form-invalid-tip';
7337                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
7338                     Roo.QuickTips.enable();
7339                 }
7340                 break;
7341             case 'title':
7342                 this.el.dom.title = msg;
7343                 break;
7344             case 'under':
7345                 if(!this.errorEl){
7346                     var elp = this.el.findParent('.x-form-element', 5, true);
7347                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
7348                     this.errorEl.setWidth(elp.getWidth(true)-20);
7349                 }
7350                 this.errorEl.update(msg);
7351                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
7352                 break;
7353             case 'side':
7354                 if(!this.errorIcon){
7355                     var elp = this.el.findParent('.x-form-element', 5, true);
7356                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
7357                 }
7358                 this.alignErrorIcon();
7359                 this.errorIcon.dom.qtip = msg;
7360                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
7361                 this.errorIcon.show();
7362                 this.on('resize', this.alignErrorIcon, this);
7363                 break;
7364             default:
7365                 var t = Roo.getDom(this.msgTarget);
7366                 t.innerHTML = msg;
7367                 t.style.display = this.msgDisplay;
7368                 break;
7369         }
7370         */
7371         this.fireEvent('invalid', this, msg);
7372     },
7373     // private
7374     SafariOnKeyDown : function(event)
7375     {
7376         // this is a workaround for a password hang bug on chrome/ webkit.
7377         
7378         var isSelectAll = false;
7379         
7380         if(this.inputEl().dom.selectionEnd > 0){
7381             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
7382         }
7383         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
7384             event.preventDefault();
7385             this.setValue('');
7386             return;
7387         }
7388         
7389         if(isSelectAll){ // backspace and delete key
7390             
7391             event.preventDefault();
7392             // this is very hacky as keydown always get's upper case.
7393             //
7394             var cc = String.fromCharCode(event.getCharCode());
7395             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
7396             
7397         }
7398     },
7399     adjustWidth : function(tag, w){
7400         tag = tag.toLowerCase();
7401         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
7402             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
7403                 if(tag == 'input'){
7404                     return w + 2;
7405                 }
7406                 if(tag == 'textarea'){
7407                     return w-2;
7408                 }
7409             }else if(Roo.isOpera){
7410                 if(tag == 'input'){
7411                     return w + 2;
7412                 }
7413                 if(tag == 'textarea'){
7414                     return w-2;
7415                 }
7416             }
7417         }
7418         return w;
7419     }
7420     
7421 });
7422
7423  
7424 /*
7425  * - LGPL
7426  *
7427  * Input
7428  * 
7429  */
7430
7431 /**
7432  * @class Roo.bootstrap.TextArea
7433  * @extends Roo.bootstrap.Input
7434  * Bootstrap TextArea class
7435  * @cfg {Number} cols Specifies the visible width of a text area
7436  * @cfg {Number} rows Specifies the visible number of lines in a text area
7437  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
7438  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
7439  * @cfg {string} html text
7440  * 
7441  * @constructor
7442  * Create a new TextArea
7443  * @param {Object} config The config object
7444  */
7445
7446 Roo.bootstrap.TextArea = function(config){
7447     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
7448    
7449 };
7450
7451 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
7452      
7453     cols : false,
7454     rows : 5,
7455     readOnly : false,
7456     warp : 'soft',
7457     resize : false,
7458     value: false,
7459     html: false,
7460     
7461     getAutoCreate : function(){
7462         
7463         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7464         
7465         var id = Roo.id();
7466         
7467         var cfg = {};
7468         
7469         var input =  {
7470             tag: 'textarea',
7471             id : id,
7472             warp : this.warp,
7473             rows : this.rows,
7474             value : this.value || '',
7475             html: this.html || '',
7476             cls : 'form-control',
7477             placeholder : this.placeholder || '' 
7478             
7479         };
7480         
7481         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7482             input.maxLength = this.maxLength;
7483         }
7484         
7485         if(this.resize){
7486             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
7487         }
7488         
7489         if(this.cols){
7490             input.cols = this.cols;
7491         }
7492         
7493         if (this.readOnly) {
7494             input.readonly = true;
7495         }
7496         
7497         if (this.name) {
7498             input.name = this.name;
7499         }
7500         
7501         if (this.size) {
7502             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
7503         }
7504         
7505         var settings=this;
7506         ['xs','sm','md','lg'].map(function(size){
7507             if (settings[size]) {
7508                 cfg.cls += ' col-' + size + '-' + settings[size];
7509             }
7510         });
7511         
7512         var inputblock = input;
7513         
7514         if (this.before || this.after) {
7515             
7516             inputblock = {
7517                 cls : 'input-group',
7518                 cn :  [] 
7519             };
7520             if (this.before) {
7521                 inputblock.cn.push({
7522                     tag :'span',
7523                     cls : 'input-group-addon',
7524                     html : this.before
7525                 });
7526             }
7527             inputblock.cn.push(input);
7528             if (this.after) {
7529                 inputblock.cn.push({
7530                     tag :'span',
7531                     cls : 'input-group-addon',
7532                     html : this.after
7533                 });
7534             }
7535             
7536         }
7537         
7538         if (align ==='left' && this.fieldLabel.length) {
7539                 Roo.log("left and has label");
7540                 cfg.cn = [
7541                     
7542                     {
7543                         tag: 'label',
7544                         'for' :  id,
7545                         cls : 'control-label col-sm-' + this.labelWidth,
7546                         html : this.fieldLabel
7547                         
7548                     },
7549                     {
7550                         cls : "col-sm-" + (12 - this.labelWidth), 
7551                         cn: [
7552                             inputblock
7553                         ]
7554                     }
7555                     
7556                 ];
7557         } else if ( this.fieldLabel.length) {
7558                 Roo.log(" label");
7559                  cfg.cn = [
7560                    
7561                     {
7562                         tag: 'label',
7563                         //cls : 'input-group-addon',
7564                         html : this.fieldLabel
7565                         
7566                     },
7567                     
7568                     inputblock
7569                     
7570                 ];
7571
7572         } else {
7573             
7574                    Roo.log(" no label && no align");
7575                 cfg.cn = [
7576                     
7577                         inputblock
7578                     
7579                 ];
7580                 
7581                 
7582         }
7583         
7584         if (this.disabled) {
7585             input.disabled=true;
7586         }
7587         
7588         return cfg;
7589         
7590     },
7591     /**
7592      * return the real textarea element.
7593      */
7594     inputEl: function ()
7595     {
7596         return this.el.select('textarea.form-control',true).first();
7597     }
7598 });
7599
7600  
7601 /*
7602  * - LGPL
7603  *
7604  * trigger field - base class for combo..
7605  * 
7606  */
7607  
7608 /**
7609  * @class Roo.bootstrap.TriggerField
7610  * @extends Roo.bootstrap.Input
7611  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
7612  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
7613  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
7614  * for which you can provide a custom implementation.  For example:
7615  * <pre><code>
7616 var trigger = new Roo.bootstrap.TriggerField();
7617 trigger.onTriggerClick = myTriggerFn;
7618 trigger.applyTo('my-field');
7619 </code></pre>
7620  *
7621  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
7622  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
7623  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
7624  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
7625  * @constructor
7626  * Create a new TriggerField.
7627  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
7628  * to the base TextField)
7629  */
7630 Roo.bootstrap.TriggerField = function(config){
7631     this.mimicing = false;
7632     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
7633 };
7634
7635 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
7636     /**
7637      * @cfg {String} triggerClass A CSS class to apply to the trigger
7638      */
7639      /**
7640      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
7641      */
7642     hideTrigger:false,
7643
7644     /** @cfg {Boolean} grow @hide */
7645     /** @cfg {Number} growMin @hide */
7646     /** @cfg {Number} growMax @hide */
7647
7648     /**
7649      * @hide 
7650      * @method
7651      */
7652     autoSize: Roo.emptyFn,
7653     // private
7654     monitorTab : true,
7655     // private
7656     deferHeight : true,
7657
7658     
7659     actionMode : 'wrap',
7660     
7661     
7662     
7663     getAutoCreate : function(){
7664        
7665         var align = this.labelAlign || this.parentLabelAlign();
7666         
7667         var id = Roo.id();
7668         
7669         var cfg = {
7670             cls: 'form-group' //input-group
7671         };
7672         
7673         
7674         var input =  {
7675             tag: 'input',
7676             id : id,
7677             type : this.inputType,
7678             cls : 'form-control',
7679             autocomplete: 'off',
7680             placeholder : this.placeholder || '' 
7681             
7682         };
7683         if (this.name) {
7684             input.name = this.name;
7685         }
7686         if (this.size) {
7687             input.cls += ' input-' + this.size;
7688         }
7689         
7690         if (this.disabled) {
7691             input.disabled=true;
7692         }
7693         
7694         var inputblock = input;
7695         
7696         if (this.before || this.after) {
7697             
7698             inputblock = {
7699                 cls : 'input-group',
7700                 cn :  [] 
7701             };
7702             if (this.before) {
7703                 inputblock.cn.push({
7704                     tag :'span',
7705                     cls : 'input-group-addon',
7706                     html : this.before
7707                 });
7708             }
7709             inputblock.cn.push(input);
7710             if (this.after) {
7711                 inputblock.cn.push({
7712                     tag :'span',
7713                     cls : 'input-group-addon',
7714                     html : this.after
7715                 });
7716             }
7717             
7718         };
7719         
7720         var box = {
7721             tag: 'div',
7722             cn: [
7723                 {
7724                     tag: 'input',
7725                     type : 'hidden',
7726                     cls: 'form-hidden-field'
7727                 },
7728                 inputblock
7729             ]
7730             
7731         };
7732         
7733         if(this.multiple){
7734             Roo.log('multiple');
7735             
7736             box = {
7737                 tag: 'div',
7738                 cn: [
7739                     {
7740                         tag: 'input',
7741                         type : 'hidden',
7742                         cls: 'form-hidden-field'
7743                     },
7744                     {
7745                         tag: 'ul',
7746                         cls: 'select2-choices',
7747                         cn:[
7748                             {
7749                                 tag: 'li',
7750                                 cls: 'select2-search-field',
7751                                 cn: [
7752
7753                                     inputblock
7754                                 ]
7755                             }
7756                         ]
7757                     }
7758                 ]
7759             }
7760         };
7761         
7762         var combobox = {
7763             cls: 'select2-container input-group',
7764             cn: [
7765                 box
7766 //                {
7767 //                    tag: 'ul',
7768 //                    cls: 'typeahead typeahead-long dropdown-menu',
7769 //                    style: 'display:none'
7770 //                }
7771             ]
7772         };
7773         
7774         if(!this.multiple){
7775             combobox.cn.push({
7776                 tag :'span',
7777                 cls : 'input-group-addon btn dropdown-toggle',
7778                 cn : [
7779                     {
7780                         tag: 'span',
7781                         cls: 'caret'
7782                     },
7783                     {
7784                         tag: 'span',
7785                         cls: 'combobox-clear',
7786                         cn  : [
7787                             {
7788                                 tag : 'i',
7789                                 cls: 'icon-remove'
7790                             }
7791                         ]
7792                     }
7793                 ]
7794
7795             })
7796         }
7797         
7798         if(this.multiple){
7799             combobox.cls += ' select2-container-multi';
7800         }
7801         
7802         if (align ==='left' && this.fieldLabel.length) {
7803             
7804                 Roo.log("left and has label");
7805                 cfg.cn = [
7806                     
7807                     {
7808                         tag: 'label',
7809                         'for' :  id,
7810                         cls : 'control-label col-sm-' + this.labelWidth,
7811                         html : this.fieldLabel
7812                         
7813                     },
7814                     {
7815                         cls : "col-sm-" + (12 - this.labelWidth), 
7816                         cn: [
7817                             combobox
7818                         ]
7819                     }
7820                     
7821                 ];
7822         } else if ( this.fieldLabel.length) {
7823                 Roo.log(" label");
7824                  cfg.cn = [
7825                    
7826                     {
7827                         tag: 'label',
7828                         //cls : 'input-group-addon',
7829                         html : this.fieldLabel
7830                         
7831                     },
7832                     
7833                     combobox
7834                     
7835                 ];
7836
7837         } else {
7838             
7839                 Roo.log(" no label && no align");
7840                 cfg = combobox
7841                      
7842                 
7843         }
7844          
7845         var settings=this;
7846         ['xs','sm','md','lg'].map(function(size){
7847             if (settings[size]) {
7848                 cfg.cls += ' col-' + size + '-' + settings[size];
7849             }
7850         });
7851         
7852         return cfg;
7853         
7854     },
7855     
7856     
7857     
7858     // private
7859     onResize : function(w, h){
7860 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
7861 //        if(typeof w == 'number'){
7862 //            var x = w - this.trigger.getWidth();
7863 //            this.inputEl().setWidth(this.adjustWidth('input', x));
7864 //            this.trigger.setStyle('left', x+'px');
7865 //        }
7866     },
7867
7868     // private
7869     adjustSize : Roo.BoxComponent.prototype.adjustSize,
7870
7871     // private
7872     getResizeEl : function(){
7873         return this.inputEl();
7874     },
7875
7876     // private
7877     getPositionEl : function(){
7878         return this.inputEl();
7879     },
7880
7881     // private
7882     alignErrorIcon : function(){
7883         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
7884     },
7885
7886     // private
7887     initEvents : function(){
7888         
7889         this.createList();
7890         
7891         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
7892         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
7893         if(!this.multiple){
7894             this.trigger = this.el.select('span.dropdown-toggle',true).first();
7895             if(this.hideTrigger){
7896                 this.trigger.setDisplayed(false);
7897             }
7898             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
7899         }
7900         
7901         if(this.multiple){
7902             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
7903         }
7904         
7905         //this.trigger.addClassOnOver('x-form-trigger-over');
7906         //this.trigger.addClassOnClick('x-form-trigger-click');
7907         
7908         //if(!this.width){
7909         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
7910         //}
7911     },
7912     
7913     createList : function()
7914     {
7915         this.list = Roo.get(document.body).createChild({
7916             tag: 'ul',
7917             cls: 'typeahead typeahead-long dropdown-menu',
7918             style: 'display:none'
7919         });
7920         
7921         this.list.setVisibilityMode(Roo.Element.DISPLAY);
7922     },
7923
7924     // private
7925     initTrigger : function(){
7926        
7927     },
7928
7929     // private
7930     onDestroy : function(){
7931         if(this.trigger){
7932             this.trigger.removeAllListeners();
7933           //  this.trigger.remove();
7934         }
7935         //if(this.wrap){
7936         //    this.wrap.remove();
7937         //}
7938         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
7939     },
7940
7941     // private
7942     onFocus : function(){
7943         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
7944         /*
7945         if(!this.mimicing){
7946             this.wrap.addClass('x-trigger-wrap-focus');
7947             this.mimicing = true;
7948             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
7949             if(this.monitorTab){
7950                 this.el.on("keydown", this.checkTab, this);
7951             }
7952         }
7953         */
7954     },
7955
7956     // private
7957     checkTab : function(e){
7958         if(e.getKey() == e.TAB){
7959             this.triggerBlur();
7960         }
7961     },
7962
7963     // private
7964     onBlur : function(){
7965         // do nothing
7966     },
7967
7968     // private
7969     mimicBlur : function(e, t){
7970         /*
7971         if(!this.wrap.contains(t) && this.validateBlur()){
7972             this.triggerBlur();
7973         }
7974         */
7975     },
7976
7977     // private
7978     triggerBlur : function(){
7979         this.mimicing = false;
7980         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
7981         if(this.monitorTab){
7982             this.el.un("keydown", this.checkTab, this);
7983         }
7984         //this.wrap.removeClass('x-trigger-wrap-focus');
7985         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
7986     },
7987
7988     // private
7989     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
7990     validateBlur : function(e, t){
7991         return true;
7992     },
7993
7994     // private
7995     onDisable : function(){
7996         this.inputEl().dom.disabled = true;
7997         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
7998         //if(this.wrap){
7999         //    this.wrap.addClass('x-item-disabled');
8000         //}
8001     },
8002
8003     // private
8004     onEnable : function(){
8005         this.inputEl().dom.disabled = false;
8006         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
8007         //if(this.wrap){
8008         //    this.el.removeClass('x-item-disabled');
8009         //}
8010     },
8011
8012     // private
8013     onShow : function(){
8014         var ae = this.getActionEl();
8015         
8016         if(ae){
8017             ae.dom.style.display = '';
8018             ae.dom.style.visibility = 'visible';
8019         }
8020     },
8021
8022     // private
8023     
8024     onHide : function(){
8025         var ae = this.getActionEl();
8026         ae.dom.style.display = 'none';
8027     },
8028
8029     /**
8030      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
8031      * by an implementing function.
8032      * @method
8033      * @param {EventObject} e
8034      */
8035     onTriggerClick : Roo.emptyFn
8036 });
8037  /*
8038  * Based on:
8039  * Ext JS Library 1.1.1
8040  * Copyright(c) 2006-2007, Ext JS, LLC.
8041  *
8042  * Originally Released Under LGPL - original licence link has changed is not relivant.
8043  *
8044  * Fork - LGPL
8045  * <script type="text/javascript">
8046  */
8047
8048
8049 /**
8050  * @class Roo.data.SortTypes
8051  * @singleton
8052  * Defines the default sorting (casting?) comparison functions used when sorting data.
8053  */
8054 Roo.data.SortTypes = {
8055     /**
8056      * Default sort that does nothing
8057      * @param {Mixed} s The value being converted
8058      * @return {Mixed} The comparison value
8059      */
8060     none : function(s){
8061         return s;
8062     },
8063     
8064     /**
8065      * The regular expression used to strip tags
8066      * @type {RegExp}
8067      * @property
8068      */
8069     stripTagsRE : /<\/?[^>]+>/gi,
8070     
8071     /**
8072      * Strips all HTML tags to sort on text only
8073      * @param {Mixed} s The value being converted
8074      * @return {String} The comparison value
8075      */
8076     asText : function(s){
8077         return String(s).replace(this.stripTagsRE, "");
8078     },
8079     
8080     /**
8081      * Strips all HTML tags to sort on text only - Case insensitive
8082      * @param {Mixed} s The value being converted
8083      * @return {String} The comparison value
8084      */
8085     asUCText : function(s){
8086         return String(s).toUpperCase().replace(this.stripTagsRE, "");
8087     },
8088     
8089     /**
8090      * Case insensitive string
8091      * @param {Mixed} s The value being converted
8092      * @return {String} The comparison value
8093      */
8094     asUCString : function(s) {
8095         return String(s).toUpperCase();
8096     },
8097     
8098     /**
8099      * Date sorting
8100      * @param {Mixed} s The value being converted
8101      * @return {Number} The comparison value
8102      */
8103     asDate : function(s) {
8104         if(!s){
8105             return 0;
8106         }
8107         if(s instanceof Date){
8108             return s.getTime();
8109         }
8110         return Date.parse(String(s));
8111     },
8112     
8113     /**
8114      * Float sorting
8115      * @param {Mixed} s The value being converted
8116      * @return {Float} The comparison value
8117      */
8118     asFloat : function(s) {
8119         var val = parseFloat(String(s).replace(/,/g, ""));
8120         if(isNaN(val)) val = 0;
8121         return val;
8122     },
8123     
8124     /**
8125      * Integer sorting
8126      * @param {Mixed} s The value being converted
8127      * @return {Number} The comparison value
8128      */
8129     asInt : function(s) {
8130         var val = parseInt(String(s).replace(/,/g, ""));
8131         if(isNaN(val)) val = 0;
8132         return val;
8133     }
8134 };/*
8135  * Based on:
8136  * Ext JS Library 1.1.1
8137  * Copyright(c) 2006-2007, Ext JS, LLC.
8138  *
8139  * Originally Released Under LGPL - original licence link has changed is not relivant.
8140  *
8141  * Fork - LGPL
8142  * <script type="text/javascript">
8143  */
8144
8145 /**
8146 * @class Roo.data.Record
8147  * Instances of this class encapsulate both record <em>definition</em> information, and record
8148  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
8149  * to access Records cached in an {@link Roo.data.Store} object.<br>
8150  * <p>
8151  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
8152  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
8153  * objects.<br>
8154  * <p>
8155  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
8156  * @constructor
8157  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
8158  * {@link #create}. The parameters are the same.
8159  * @param {Array} data An associative Array of data values keyed by the field name.
8160  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
8161  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
8162  * not specified an integer id is generated.
8163  */
8164 Roo.data.Record = function(data, id){
8165     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
8166     this.data = data;
8167 };
8168
8169 /**
8170  * Generate a constructor for a specific record layout.
8171  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
8172  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
8173  * Each field definition object may contain the following properties: <ul>
8174  * <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,
8175  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
8176  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
8177  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
8178  * is being used, then this is a string containing the javascript expression to reference the data relative to 
8179  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
8180  * to the data item relative to the record element. If the mapping expression is the same as the field name,
8181  * this may be omitted.</p></li>
8182  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
8183  * <ul><li>auto (Default, implies no conversion)</li>
8184  * <li>string</li>
8185  * <li>int</li>
8186  * <li>float</li>
8187  * <li>boolean</li>
8188  * <li>date</li></ul></p></li>
8189  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
8190  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
8191  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
8192  * by the Reader into an object that will be stored in the Record. It is passed the
8193  * following parameters:<ul>
8194  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
8195  * </ul></p></li>
8196  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
8197  * </ul>
8198  * <br>usage:<br><pre><code>
8199 var TopicRecord = Roo.data.Record.create(
8200     {name: 'title', mapping: 'topic_title'},
8201     {name: 'author', mapping: 'username'},
8202     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
8203     {name: 'lastPost', mapping: 'post_time', type: 'date'},
8204     {name: 'lastPoster', mapping: 'user2'},
8205     {name: 'excerpt', mapping: 'post_text'}
8206 );
8207
8208 var myNewRecord = new TopicRecord({
8209     title: 'Do my job please',
8210     author: 'noobie',
8211     totalPosts: 1,
8212     lastPost: new Date(),
8213     lastPoster: 'Animal',
8214     excerpt: 'No way dude!'
8215 });
8216 myStore.add(myNewRecord);
8217 </code></pre>
8218  * @method create
8219  * @static
8220  */
8221 Roo.data.Record.create = function(o){
8222     var f = function(){
8223         f.superclass.constructor.apply(this, arguments);
8224     };
8225     Roo.extend(f, Roo.data.Record);
8226     var p = f.prototype;
8227     p.fields = new Roo.util.MixedCollection(false, function(field){
8228         return field.name;
8229     });
8230     for(var i = 0, len = o.length; i < len; i++){
8231         p.fields.add(new Roo.data.Field(o[i]));
8232     }
8233     f.getField = function(name){
8234         return p.fields.get(name);  
8235     };
8236     return f;
8237 };
8238
8239 Roo.data.Record.AUTO_ID = 1000;
8240 Roo.data.Record.EDIT = 'edit';
8241 Roo.data.Record.REJECT = 'reject';
8242 Roo.data.Record.COMMIT = 'commit';
8243
8244 Roo.data.Record.prototype = {
8245     /**
8246      * Readonly flag - true if this record has been modified.
8247      * @type Boolean
8248      */
8249     dirty : false,
8250     editing : false,
8251     error: null,
8252     modified: null,
8253
8254     // private
8255     join : function(store){
8256         this.store = store;
8257     },
8258
8259     /**
8260      * Set the named field to the specified value.
8261      * @param {String} name The name of the field to set.
8262      * @param {Object} value The value to set the field to.
8263      */
8264     set : function(name, value){
8265         if(this.data[name] == value){
8266             return;
8267         }
8268         this.dirty = true;
8269         if(!this.modified){
8270             this.modified = {};
8271         }
8272         if(typeof this.modified[name] == 'undefined'){
8273             this.modified[name] = this.data[name];
8274         }
8275         this.data[name] = value;
8276         if(!this.editing && this.store){
8277             this.store.afterEdit(this);
8278         }       
8279     },
8280
8281     /**
8282      * Get the value of the named field.
8283      * @param {String} name The name of the field to get the value of.
8284      * @return {Object} The value of the field.
8285      */
8286     get : function(name){
8287         return this.data[name]; 
8288     },
8289
8290     // private
8291     beginEdit : function(){
8292         this.editing = true;
8293         this.modified = {}; 
8294     },
8295
8296     // private
8297     cancelEdit : function(){
8298         this.editing = false;
8299         delete this.modified;
8300     },
8301
8302     // private
8303     endEdit : function(){
8304         this.editing = false;
8305         if(this.dirty && this.store){
8306             this.store.afterEdit(this);
8307         }
8308     },
8309
8310     /**
8311      * Usually called by the {@link Roo.data.Store} which owns the Record.
8312      * Rejects all changes made to the Record since either creation, or the last commit operation.
8313      * Modified fields are reverted to their original values.
8314      * <p>
8315      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8316      * of reject operations.
8317      */
8318     reject : function(){
8319         var m = this.modified;
8320         for(var n in m){
8321             if(typeof m[n] != "function"){
8322                 this.data[n] = m[n];
8323             }
8324         }
8325         this.dirty = false;
8326         delete this.modified;
8327         this.editing = false;
8328         if(this.store){
8329             this.store.afterReject(this);
8330         }
8331     },
8332
8333     /**
8334      * Usually called by the {@link Roo.data.Store} which owns the Record.
8335      * Commits all changes made to the Record since either creation, or the last commit operation.
8336      * <p>
8337      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8338      * of commit operations.
8339      */
8340     commit : function(){
8341         this.dirty = false;
8342         delete this.modified;
8343         this.editing = false;
8344         if(this.store){
8345             this.store.afterCommit(this);
8346         }
8347     },
8348
8349     // private
8350     hasError : function(){
8351         return this.error != null;
8352     },
8353
8354     // private
8355     clearError : function(){
8356         this.error = null;
8357     },
8358
8359     /**
8360      * Creates a copy of this record.
8361      * @param {String} id (optional) A new record id if you don't want to use this record's id
8362      * @return {Record}
8363      */
8364     copy : function(newId) {
8365         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
8366     }
8367 };/*
8368  * Based on:
8369  * Ext JS Library 1.1.1
8370  * Copyright(c) 2006-2007, Ext JS, LLC.
8371  *
8372  * Originally Released Under LGPL - original licence link has changed is not relivant.
8373  *
8374  * Fork - LGPL
8375  * <script type="text/javascript">
8376  */
8377
8378
8379
8380 /**
8381  * @class Roo.data.Store
8382  * @extends Roo.util.Observable
8383  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
8384  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
8385  * <p>
8386  * 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
8387  * has no knowledge of the format of the data returned by the Proxy.<br>
8388  * <p>
8389  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
8390  * instances from the data object. These records are cached and made available through accessor functions.
8391  * @constructor
8392  * Creates a new Store.
8393  * @param {Object} config A config object containing the objects needed for the Store to access data,
8394  * and read the data into Records.
8395  */
8396 Roo.data.Store = function(config){
8397     this.data = new Roo.util.MixedCollection(false);
8398     this.data.getKey = function(o){
8399         return o.id;
8400     };
8401     this.baseParams = {};
8402     // private
8403     this.paramNames = {
8404         "start" : "start",
8405         "limit" : "limit",
8406         "sort" : "sort",
8407         "dir" : "dir",
8408         "multisort" : "_multisort"
8409     };
8410
8411     if(config && config.data){
8412         this.inlineData = config.data;
8413         delete config.data;
8414     }
8415
8416     Roo.apply(this, config);
8417     
8418     if(this.reader){ // reader passed
8419         this.reader = Roo.factory(this.reader, Roo.data);
8420         this.reader.xmodule = this.xmodule || false;
8421         if(!this.recordType){
8422             this.recordType = this.reader.recordType;
8423         }
8424         if(this.reader.onMetaChange){
8425             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
8426         }
8427     }
8428
8429     if(this.recordType){
8430         this.fields = this.recordType.prototype.fields;
8431     }
8432     this.modified = [];
8433
8434     this.addEvents({
8435         /**
8436          * @event datachanged
8437          * Fires when the data cache has changed, and a widget which is using this Store
8438          * as a Record cache should refresh its view.
8439          * @param {Store} this
8440          */
8441         datachanged : true,
8442         /**
8443          * @event metachange
8444          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
8445          * @param {Store} this
8446          * @param {Object} meta The JSON metadata
8447          */
8448         metachange : true,
8449         /**
8450          * @event add
8451          * Fires when Records have been added to the Store
8452          * @param {Store} this
8453          * @param {Roo.data.Record[]} records The array of Records added
8454          * @param {Number} index The index at which the record(s) were added
8455          */
8456         add : true,
8457         /**
8458          * @event remove
8459          * Fires when a Record has been removed from the Store
8460          * @param {Store} this
8461          * @param {Roo.data.Record} record The Record that was removed
8462          * @param {Number} index The index at which the record was removed
8463          */
8464         remove : true,
8465         /**
8466          * @event update
8467          * Fires when a Record has been updated
8468          * @param {Store} this
8469          * @param {Roo.data.Record} record The Record that was updated
8470          * @param {String} operation The update operation being performed.  Value may be one of:
8471          * <pre><code>
8472  Roo.data.Record.EDIT
8473  Roo.data.Record.REJECT
8474  Roo.data.Record.COMMIT
8475          * </code></pre>
8476          */
8477         update : true,
8478         /**
8479          * @event clear
8480          * Fires when the data cache has been cleared.
8481          * @param {Store} this
8482          */
8483         clear : true,
8484         /**
8485          * @event beforeload
8486          * Fires before a request is made for a new data object.  If the beforeload handler returns false
8487          * the load action will be canceled.
8488          * @param {Store} this
8489          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8490          */
8491         beforeload : true,
8492         /**
8493          * @event beforeloadadd
8494          * Fires after a new set of Records has been loaded.
8495          * @param {Store} this
8496          * @param {Roo.data.Record[]} records The Records that were loaded
8497          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8498          */
8499         beforeloadadd : true,
8500         /**
8501          * @event load
8502          * Fires after a new set of Records has been loaded, before they are added to the store.
8503          * @param {Store} this
8504          * @param {Roo.data.Record[]} records The Records that were loaded
8505          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8506          * @params {Object} return from reader
8507          */
8508         load : true,
8509         /**
8510          * @event loadexception
8511          * Fires if an exception occurs in the Proxy during loading.
8512          * Called with the signature of the Proxy's "loadexception" event.
8513          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
8514          * 
8515          * @param {Proxy} 
8516          * @param {Object} return from JsonData.reader() - success, totalRecords, records
8517          * @param {Object} load options 
8518          * @param {Object} jsonData from your request (normally this contains the Exception)
8519          */
8520         loadexception : true
8521     });
8522     
8523     if(this.proxy){
8524         this.proxy = Roo.factory(this.proxy, Roo.data);
8525         this.proxy.xmodule = this.xmodule || false;
8526         this.relayEvents(this.proxy,  ["loadexception"]);
8527     }
8528     this.sortToggle = {};
8529     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
8530
8531     Roo.data.Store.superclass.constructor.call(this);
8532
8533     if(this.inlineData){
8534         this.loadData(this.inlineData);
8535         delete this.inlineData;
8536     }
8537 };
8538
8539 Roo.extend(Roo.data.Store, Roo.util.Observable, {
8540      /**
8541     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
8542     * without a remote query - used by combo/forms at present.
8543     */
8544     
8545     /**
8546     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
8547     */
8548     /**
8549     * @cfg {Array} data Inline data to be loaded when the store is initialized.
8550     */
8551     /**
8552     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
8553     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
8554     */
8555     /**
8556     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
8557     * on any HTTP request
8558     */
8559     /**
8560     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
8561     */
8562     /**
8563     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
8564     */
8565     multiSort: false,
8566     /**
8567     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
8568     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
8569     */
8570     remoteSort : false,
8571
8572     /**
8573     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
8574      * loaded or when a record is removed. (defaults to false).
8575     */
8576     pruneModifiedRecords : false,
8577
8578     // private
8579     lastOptions : null,
8580
8581     /**
8582      * Add Records to the Store and fires the add event.
8583      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8584      */
8585     add : function(records){
8586         records = [].concat(records);
8587         for(var i = 0, len = records.length; i < len; i++){
8588             records[i].join(this);
8589         }
8590         var index = this.data.length;
8591         this.data.addAll(records);
8592         this.fireEvent("add", this, records, index);
8593     },
8594
8595     /**
8596      * Remove a Record from the Store and fires the remove event.
8597      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
8598      */
8599     remove : function(record){
8600         var index = this.data.indexOf(record);
8601         this.data.removeAt(index);
8602         if(this.pruneModifiedRecords){
8603             this.modified.remove(record);
8604         }
8605         this.fireEvent("remove", this, record, index);
8606     },
8607
8608     /**
8609      * Remove all Records from the Store and fires the clear event.
8610      */
8611     removeAll : function(){
8612         this.data.clear();
8613         if(this.pruneModifiedRecords){
8614             this.modified = [];
8615         }
8616         this.fireEvent("clear", this);
8617     },
8618
8619     /**
8620      * Inserts Records to the Store at the given index and fires the add event.
8621      * @param {Number} index The start index at which to insert the passed Records.
8622      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8623      */
8624     insert : function(index, records){
8625         records = [].concat(records);
8626         for(var i = 0, len = records.length; i < len; i++){
8627             this.data.insert(index, records[i]);
8628             records[i].join(this);
8629         }
8630         this.fireEvent("add", this, records, index);
8631     },
8632
8633     /**
8634      * Get the index within the cache of the passed Record.
8635      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
8636      * @return {Number} The index of the passed Record. Returns -1 if not found.
8637      */
8638     indexOf : function(record){
8639         return this.data.indexOf(record);
8640     },
8641
8642     /**
8643      * Get the index within the cache of the Record with the passed id.
8644      * @param {String} id The id of the Record to find.
8645      * @return {Number} The index of the Record. Returns -1 if not found.
8646      */
8647     indexOfId : function(id){
8648         return this.data.indexOfKey(id);
8649     },
8650
8651     /**
8652      * Get the Record with the specified id.
8653      * @param {String} id The id of the Record to find.
8654      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
8655      */
8656     getById : function(id){
8657         return this.data.key(id);
8658     },
8659
8660     /**
8661      * Get the Record at the specified index.
8662      * @param {Number} index The index of the Record to find.
8663      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
8664      */
8665     getAt : function(index){
8666         return this.data.itemAt(index);
8667     },
8668
8669     /**
8670      * Returns a range of Records between specified indices.
8671      * @param {Number} startIndex (optional) The starting index (defaults to 0)
8672      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
8673      * @return {Roo.data.Record[]} An array of Records
8674      */
8675     getRange : function(start, end){
8676         return this.data.getRange(start, end);
8677     },
8678
8679     // private
8680     storeOptions : function(o){
8681         o = Roo.apply({}, o);
8682         delete o.callback;
8683         delete o.scope;
8684         this.lastOptions = o;
8685     },
8686
8687     /**
8688      * Loads the Record cache from the configured Proxy using the configured Reader.
8689      * <p>
8690      * If using remote paging, then the first load call must specify the <em>start</em>
8691      * and <em>limit</em> properties in the options.params property to establish the initial
8692      * position within the dataset, and the number of Records to cache on each read from the Proxy.
8693      * <p>
8694      * <strong>It is important to note that for remote data sources, loading is asynchronous,
8695      * and this call will return before the new data has been loaded. Perform any post-processing
8696      * in a callback function, or in a "load" event handler.</strong>
8697      * <p>
8698      * @param {Object} options An object containing properties which control loading options:<ul>
8699      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
8700      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
8701      * passed the following arguments:<ul>
8702      * <li>r : Roo.data.Record[]</li>
8703      * <li>options: Options object from the load call</li>
8704      * <li>success: Boolean success indicator</li></ul></li>
8705      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
8706      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
8707      * </ul>
8708      */
8709     load : function(options){
8710         options = options || {};
8711         if(this.fireEvent("beforeload", this, options) !== false){
8712             this.storeOptions(options);
8713             var p = Roo.apply(options.params || {}, this.baseParams);
8714             // if meta was not loaded from remote source.. try requesting it.
8715             if (!this.reader.metaFromRemote) {
8716                 p._requestMeta = 1;
8717             }
8718             if(this.sortInfo && this.remoteSort){
8719                 var pn = this.paramNames;
8720                 p[pn["sort"]] = this.sortInfo.field;
8721                 p[pn["dir"]] = this.sortInfo.direction;
8722             }
8723             if (this.multiSort) {
8724                 var pn = this.paramNames;
8725                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
8726             }
8727             
8728             this.proxy.load(p, this.reader, this.loadRecords, this, options);
8729         }
8730     },
8731
8732     /**
8733      * Reloads the Record cache from the configured Proxy using the configured Reader and
8734      * the options from the last load operation performed.
8735      * @param {Object} options (optional) An object containing properties which may override the options
8736      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
8737      * the most recently used options are reused).
8738      */
8739     reload : function(options){
8740         this.load(Roo.applyIf(options||{}, this.lastOptions));
8741     },
8742
8743     // private
8744     // Called as a callback by the Reader during a load operation.
8745     loadRecords : function(o, options, success){
8746         if(!o || success === false){
8747             if(success !== false){
8748                 this.fireEvent("load", this, [], options, o);
8749             }
8750             if(options.callback){
8751                 options.callback.call(options.scope || this, [], options, false);
8752             }
8753             return;
8754         }
8755         // if data returned failure - throw an exception.
8756         if (o.success === false) {
8757             // show a message if no listener is registered.
8758             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
8759                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
8760             }
8761             // loadmask wil be hooked into this..
8762             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
8763             return;
8764         }
8765         var r = o.records, t = o.totalRecords || r.length;
8766         
8767         this.fireEvent("beforeloadadd", this, r, options, o);
8768         
8769         if(!options || options.add !== true){
8770             if(this.pruneModifiedRecords){
8771                 this.modified = [];
8772             }
8773             for(var i = 0, len = r.length; i < len; i++){
8774                 r[i].join(this);
8775             }
8776             if(this.snapshot){
8777                 this.data = this.snapshot;
8778                 delete this.snapshot;
8779             }
8780             this.data.clear();
8781             this.data.addAll(r);
8782             this.totalLength = t;
8783             this.applySort();
8784             this.fireEvent("datachanged", this);
8785         }else{
8786             this.totalLength = Math.max(t, this.data.length+r.length);
8787             this.add(r);
8788         }
8789         this.fireEvent("load", this, r, options, o);
8790         if(options.callback){
8791             options.callback.call(options.scope || this, r, options, true);
8792         }
8793     },
8794
8795
8796     /**
8797      * Loads data from a passed data block. A Reader which understands the format of the data
8798      * must have been configured in the constructor.
8799      * @param {Object} data The data block from which to read the Records.  The format of the data expected
8800      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
8801      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
8802      */
8803     loadData : function(o, append){
8804         var r = this.reader.readRecords(o);
8805         this.loadRecords(r, {add: append}, true);
8806     },
8807
8808     /**
8809      * Gets the number of cached records.
8810      * <p>
8811      * <em>If using paging, this may not be the total size of the dataset. If the data object
8812      * used by the Reader contains the dataset size, then the getTotalCount() function returns
8813      * the data set size</em>
8814      */
8815     getCount : function(){
8816         return this.data.length || 0;
8817     },
8818
8819     /**
8820      * Gets the total number of records in the dataset as returned by the server.
8821      * <p>
8822      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
8823      * the dataset size</em>
8824      */
8825     getTotalCount : function(){
8826         return this.totalLength || 0;
8827     },
8828
8829     /**
8830      * Returns the sort state of the Store as an object with two properties:
8831      * <pre><code>
8832  field {String} The name of the field by which the Records are sorted
8833  direction {String} The sort order, "ASC" or "DESC"
8834      * </code></pre>
8835      */
8836     getSortState : function(){
8837         return this.sortInfo;
8838     },
8839
8840     // private
8841     applySort : function(){
8842         if(this.sortInfo && !this.remoteSort){
8843             var s = this.sortInfo, f = s.field;
8844             var st = this.fields.get(f).sortType;
8845             var fn = function(r1, r2){
8846                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
8847                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
8848             };
8849             this.data.sort(s.direction, fn);
8850             if(this.snapshot && this.snapshot != this.data){
8851                 this.snapshot.sort(s.direction, fn);
8852             }
8853         }
8854     },
8855
8856     /**
8857      * Sets the default sort column and order to be used by the next load operation.
8858      * @param {String} fieldName The name of the field to sort by.
8859      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
8860      */
8861     setDefaultSort : function(field, dir){
8862         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
8863     },
8864
8865     /**
8866      * Sort the Records.
8867      * If remote sorting is used, the sort is performed on the server, and the cache is
8868      * reloaded. If local sorting is used, the cache is sorted internally.
8869      * @param {String} fieldName The name of the field to sort by.
8870      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
8871      */
8872     sort : function(fieldName, dir){
8873         var f = this.fields.get(fieldName);
8874         if(!dir){
8875             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
8876             
8877             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
8878                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
8879             }else{
8880                 dir = f.sortDir;
8881             }
8882         }
8883         this.sortToggle[f.name] = dir;
8884         this.sortInfo = {field: f.name, direction: dir};
8885         if(!this.remoteSort){
8886             this.applySort();
8887             this.fireEvent("datachanged", this);
8888         }else{
8889             this.load(this.lastOptions);
8890         }
8891     },
8892
8893     /**
8894      * Calls the specified function for each of the Records in the cache.
8895      * @param {Function} fn The function to call. The Record is passed as the first parameter.
8896      * Returning <em>false</em> aborts and exits the iteration.
8897      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
8898      */
8899     each : function(fn, scope){
8900         this.data.each(fn, scope);
8901     },
8902
8903     /**
8904      * Gets all records modified since the last commit.  Modified records are persisted across load operations
8905      * (e.g., during paging).
8906      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
8907      */
8908     getModifiedRecords : function(){
8909         return this.modified;
8910     },
8911
8912     // private
8913     createFilterFn : function(property, value, anyMatch){
8914         if(!value.exec){ // not a regex
8915             value = String(value);
8916             if(value.length == 0){
8917                 return false;
8918             }
8919             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
8920         }
8921         return function(r){
8922             return value.test(r.data[property]);
8923         };
8924     },
8925
8926     /**
8927      * Sums the value of <i>property</i> for each record between start and end and returns the result.
8928      * @param {String} property A field on your records
8929      * @param {Number} start The record index to start at (defaults to 0)
8930      * @param {Number} end The last record index to include (defaults to length - 1)
8931      * @return {Number} The sum
8932      */
8933     sum : function(property, start, end){
8934         var rs = this.data.items, v = 0;
8935         start = start || 0;
8936         end = (end || end === 0) ? end : rs.length-1;
8937
8938         for(var i = start; i <= end; i++){
8939             v += (rs[i].data[property] || 0);
8940         }
8941         return v;
8942     },
8943
8944     /**
8945      * Filter the records by a specified property.
8946      * @param {String} field A field on your records
8947      * @param {String/RegExp} value Either a string that the field
8948      * should start with or a RegExp to test against the field
8949      * @param {Boolean} anyMatch True to match any part not just the beginning
8950      */
8951     filter : function(property, value, anyMatch){
8952         var fn = this.createFilterFn(property, value, anyMatch);
8953         return fn ? this.filterBy(fn) : this.clearFilter();
8954     },
8955
8956     /**
8957      * Filter by a function. The specified function will be called with each
8958      * record in this data source. If the function returns true the record is included,
8959      * otherwise it is filtered.
8960      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
8961      * @param {Object} scope (optional) The scope of the function (defaults to this)
8962      */
8963     filterBy : function(fn, scope){
8964         this.snapshot = this.snapshot || this.data;
8965         this.data = this.queryBy(fn, scope||this);
8966         this.fireEvent("datachanged", this);
8967     },
8968
8969     /**
8970      * Query the records by a specified property.
8971      * @param {String} field A field on your records
8972      * @param {String/RegExp} value Either a string that the field
8973      * should start with or a RegExp to test against the field
8974      * @param {Boolean} anyMatch True to match any part not just the beginning
8975      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
8976      */
8977     query : function(property, value, anyMatch){
8978         var fn = this.createFilterFn(property, value, anyMatch);
8979         return fn ? this.queryBy(fn) : this.data.clone();
8980     },
8981
8982     /**
8983      * Query by a function. The specified function will be called with each
8984      * record in this data source. If the function returns true the record is included
8985      * in the results.
8986      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
8987      * @param {Object} scope (optional) The scope of the function (defaults to this)
8988       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
8989      **/
8990     queryBy : function(fn, scope){
8991         var data = this.snapshot || this.data;
8992         return data.filterBy(fn, scope||this);
8993     },
8994
8995     /**
8996      * Collects unique values for a particular dataIndex from this store.
8997      * @param {String} dataIndex The property to collect
8998      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
8999      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
9000      * @return {Array} An array of the unique values
9001      **/
9002     collect : function(dataIndex, allowNull, bypassFilter){
9003         var d = (bypassFilter === true && this.snapshot) ?
9004                 this.snapshot.items : this.data.items;
9005         var v, sv, r = [], l = {};
9006         for(var i = 0, len = d.length; i < len; i++){
9007             v = d[i].data[dataIndex];
9008             sv = String(v);
9009             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
9010                 l[sv] = true;
9011                 r[r.length] = v;
9012             }
9013         }
9014         return r;
9015     },
9016
9017     /**
9018      * Revert to a view of the Record cache with no filtering applied.
9019      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
9020      */
9021     clearFilter : function(suppressEvent){
9022         if(this.snapshot && this.snapshot != this.data){
9023             this.data = this.snapshot;
9024             delete this.snapshot;
9025             if(suppressEvent !== true){
9026                 this.fireEvent("datachanged", this);
9027             }
9028         }
9029     },
9030
9031     // private
9032     afterEdit : function(record){
9033         if(this.modified.indexOf(record) == -1){
9034             this.modified.push(record);
9035         }
9036         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
9037     },
9038     
9039     // private
9040     afterReject : function(record){
9041         this.modified.remove(record);
9042         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
9043     },
9044
9045     // private
9046     afterCommit : function(record){
9047         this.modified.remove(record);
9048         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
9049     },
9050
9051     /**
9052      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
9053      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
9054      */
9055     commitChanges : function(){
9056         var m = this.modified.slice(0);
9057         this.modified = [];
9058         for(var i = 0, len = m.length; i < len; i++){
9059             m[i].commit();
9060         }
9061     },
9062
9063     /**
9064      * Cancel outstanding changes on all changed records.
9065      */
9066     rejectChanges : function(){
9067         var m = this.modified.slice(0);
9068         this.modified = [];
9069         for(var i = 0, len = m.length; i < len; i++){
9070             m[i].reject();
9071         }
9072     },
9073
9074     onMetaChange : function(meta, rtype, o){
9075         this.recordType = rtype;
9076         this.fields = rtype.prototype.fields;
9077         delete this.snapshot;
9078         this.sortInfo = meta.sortInfo || this.sortInfo;
9079         this.modified = [];
9080         this.fireEvent('metachange', this, this.reader.meta);
9081     },
9082     
9083     moveIndex : function(data, type)
9084     {
9085         var index = this.indexOf(data);
9086         
9087         var newIndex = index + type;
9088         
9089         this.remove(data);
9090         
9091         this.insert(newIndex, data);
9092         
9093     }
9094 });/*
9095  * Based on:
9096  * Ext JS Library 1.1.1
9097  * Copyright(c) 2006-2007, Ext JS, LLC.
9098  *
9099  * Originally Released Under LGPL - original licence link has changed is not relivant.
9100  *
9101  * Fork - LGPL
9102  * <script type="text/javascript">
9103  */
9104
9105 /**
9106  * @class Roo.data.SimpleStore
9107  * @extends Roo.data.Store
9108  * Small helper class to make creating Stores from Array data easier.
9109  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
9110  * @cfg {Array} fields An array of field definition objects, or field name strings.
9111  * @cfg {Array} data The multi-dimensional array of data
9112  * @constructor
9113  * @param {Object} config
9114  */
9115 Roo.data.SimpleStore = function(config){
9116     Roo.data.SimpleStore.superclass.constructor.call(this, {
9117         isLocal : true,
9118         reader: new Roo.data.ArrayReader({
9119                 id: config.id
9120             },
9121             Roo.data.Record.create(config.fields)
9122         ),
9123         proxy : new Roo.data.MemoryProxy(config.data)
9124     });
9125     this.load();
9126 };
9127 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
9128  * Based on:
9129  * Ext JS Library 1.1.1
9130  * Copyright(c) 2006-2007, Ext JS, LLC.
9131  *
9132  * Originally Released Under LGPL - original licence link has changed is not relivant.
9133  *
9134  * Fork - LGPL
9135  * <script type="text/javascript">
9136  */
9137
9138 /**
9139 /**
9140  * @extends Roo.data.Store
9141  * @class Roo.data.JsonStore
9142  * Small helper class to make creating Stores for JSON data easier. <br/>
9143 <pre><code>
9144 var store = new Roo.data.JsonStore({
9145     url: 'get-images.php',
9146     root: 'images',
9147     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
9148 });
9149 </code></pre>
9150  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
9151  * JsonReader and HttpProxy (unless inline data is provided).</b>
9152  * @cfg {Array} fields An array of field definition objects, or field name strings.
9153  * @constructor
9154  * @param {Object} config
9155  */
9156 Roo.data.JsonStore = function(c){
9157     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
9158         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
9159         reader: new Roo.data.JsonReader(c, c.fields)
9160     }));
9161 };
9162 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
9163  * Based on:
9164  * Ext JS Library 1.1.1
9165  * Copyright(c) 2006-2007, Ext JS, LLC.
9166  *
9167  * Originally Released Under LGPL - original licence link has changed is not relivant.
9168  *
9169  * Fork - LGPL
9170  * <script type="text/javascript">
9171  */
9172
9173  
9174 Roo.data.Field = function(config){
9175     if(typeof config == "string"){
9176         config = {name: config};
9177     }
9178     Roo.apply(this, config);
9179     
9180     if(!this.type){
9181         this.type = "auto";
9182     }
9183     
9184     var st = Roo.data.SortTypes;
9185     // named sortTypes are supported, here we look them up
9186     if(typeof this.sortType == "string"){
9187         this.sortType = st[this.sortType];
9188     }
9189     
9190     // set default sortType for strings and dates
9191     if(!this.sortType){
9192         switch(this.type){
9193             case "string":
9194                 this.sortType = st.asUCString;
9195                 break;
9196             case "date":
9197                 this.sortType = st.asDate;
9198                 break;
9199             default:
9200                 this.sortType = st.none;
9201         }
9202     }
9203
9204     // define once
9205     var stripRe = /[\$,%]/g;
9206
9207     // prebuilt conversion function for this field, instead of
9208     // switching every time we're reading a value
9209     if(!this.convert){
9210         var cv, dateFormat = this.dateFormat;
9211         switch(this.type){
9212             case "":
9213             case "auto":
9214             case undefined:
9215                 cv = function(v){ return v; };
9216                 break;
9217             case "string":
9218                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
9219                 break;
9220             case "int":
9221                 cv = function(v){
9222                     return v !== undefined && v !== null && v !== '' ?
9223                            parseInt(String(v).replace(stripRe, ""), 10) : '';
9224                     };
9225                 break;
9226             case "float":
9227                 cv = function(v){
9228                     return v !== undefined && v !== null && v !== '' ?
9229                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
9230                     };
9231                 break;
9232             case "bool":
9233             case "boolean":
9234                 cv = function(v){ return v === true || v === "true" || v == 1; };
9235                 break;
9236             case "date":
9237                 cv = function(v){
9238                     if(!v){
9239                         return '';
9240                     }
9241                     if(v instanceof Date){
9242                         return v;
9243                     }
9244                     if(dateFormat){
9245                         if(dateFormat == "timestamp"){
9246                             return new Date(v*1000);
9247                         }
9248                         return Date.parseDate(v, dateFormat);
9249                     }
9250                     var parsed = Date.parse(v);
9251                     return parsed ? new Date(parsed) : null;
9252                 };
9253              break;
9254             
9255         }
9256         this.convert = cv;
9257     }
9258 };
9259
9260 Roo.data.Field.prototype = {
9261     dateFormat: null,
9262     defaultValue: "",
9263     mapping: null,
9264     sortType : null,
9265     sortDir : "ASC"
9266 };/*
9267  * Based on:
9268  * Ext JS Library 1.1.1
9269  * Copyright(c) 2006-2007, Ext JS, LLC.
9270  *
9271  * Originally Released Under LGPL - original licence link has changed is not relivant.
9272  *
9273  * Fork - LGPL
9274  * <script type="text/javascript">
9275  */
9276  
9277 // Base class for reading structured data from a data source.  This class is intended to be
9278 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
9279
9280 /**
9281  * @class Roo.data.DataReader
9282  * Base class for reading structured data from a data source.  This class is intended to be
9283  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
9284  */
9285
9286 Roo.data.DataReader = function(meta, recordType){
9287     
9288     this.meta = meta;
9289     
9290     this.recordType = recordType instanceof Array ? 
9291         Roo.data.Record.create(recordType) : recordType;
9292 };
9293
9294 Roo.data.DataReader.prototype = {
9295      /**
9296      * Create an empty record
9297      * @param {Object} data (optional) - overlay some values
9298      * @return {Roo.data.Record} record created.
9299      */
9300     newRow :  function(d) {
9301         var da =  {};
9302         this.recordType.prototype.fields.each(function(c) {
9303             switch( c.type) {
9304                 case 'int' : da[c.name] = 0; break;
9305                 case 'date' : da[c.name] = new Date(); break;
9306                 case 'float' : da[c.name] = 0.0; break;
9307                 case 'boolean' : da[c.name] = false; break;
9308                 default : da[c.name] = ""; break;
9309             }
9310             
9311         });
9312         return new this.recordType(Roo.apply(da, d));
9313     }
9314     
9315 };/*
9316  * Based on:
9317  * Ext JS Library 1.1.1
9318  * Copyright(c) 2006-2007, Ext JS, LLC.
9319  *
9320  * Originally Released Under LGPL - original licence link has changed is not relivant.
9321  *
9322  * Fork - LGPL
9323  * <script type="text/javascript">
9324  */
9325
9326 /**
9327  * @class Roo.data.DataProxy
9328  * @extends Roo.data.Observable
9329  * This class is an abstract base class for implementations which provide retrieval of
9330  * unformatted data objects.<br>
9331  * <p>
9332  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
9333  * (of the appropriate type which knows how to parse the data object) to provide a block of
9334  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
9335  * <p>
9336  * Custom implementations must implement the load method as described in
9337  * {@link Roo.data.HttpProxy#load}.
9338  */
9339 Roo.data.DataProxy = function(){
9340     this.addEvents({
9341         /**
9342          * @event beforeload
9343          * Fires before a network request is made to retrieve a data object.
9344          * @param {Object} This DataProxy object.
9345          * @param {Object} params The params parameter to the load function.
9346          */
9347         beforeload : true,
9348         /**
9349          * @event load
9350          * Fires before the load method's callback is called.
9351          * @param {Object} This DataProxy object.
9352          * @param {Object} o The data object.
9353          * @param {Object} arg The callback argument object passed to the load function.
9354          */
9355         load : true,
9356         /**
9357          * @event loadexception
9358          * Fires if an Exception occurs during data retrieval.
9359          * @param {Object} This DataProxy object.
9360          * @param {Object} o The data object.
9361          * @param {Object} arg The callback argument object passed to the load function.
9362          * @param {Object} e The Exception.
9363          */
9364         loadexception : true
9365     });
9366     Roo.data.DataProxy.superclass.constructor.call(this);
9367 };
9368
9369 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
9370
9371     /**
9372      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
9373      */
9374 /*
9375  * Based on:
9376  * Ext JS Library 1.1.1
9377  * Copyright(c) 2006-2007, Ext JS, LLC.
9378  *
9379  * Originally Released Under LGPL - original licence link has changed is not relivant.
9380  *
9381  * Fork - LGPL
9382  * <script type="text/javascript">
9383  */
9384 /**
9385  * @class Roo.data.MemoryProxy
9386  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
9387  * to the Reader when its load method is called.
9388  * @constructor
9389  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
9390  */
9391 Roo.data.MemoryProxy = function(data){
9392     if (data.data) {
9393         data = data.data;
9394     }
9395     Roo.data.MemoryProxy.superclass.constructor.call(this);
9396     this.data = data;
9397 };
9398
9399 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
9400     /**
9401      * Load data from the requested source (in this case an in-memory
9402      * data object passed to the constructor), read the data object into
9403      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9404      * process that block using the passed callback.
9405      * @param {Object} params This parameter is not used by the MemoryProxy class.
9406      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9407      * object into a block of Roo.data.Records.
9408      * @param {Function} callback The function into which to pass the block of Roo.data.records.
9409      * The function must be passed <ul>
9410      * <li>The Record block object</li>
9411      * <li>The "arg" argument from the load function</li>
9412      * <li>A boolean success indicator</li>
9413      * </ul>
9414      * @param {Object} scope The scope in which to call the callback
9415      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9416      */
9417     load : function(params, reader, callback, scope, arg){
9418         params = params || {};
9419         var result;
9420         try {
9421             result = reader.readRecords(this.data);
9422         }catch(e){
9423             this.fireEvent("loadexception", this, arg, null, e);
9424             callback.call(scope, null, arg, false);
9425             return;
9426         }
9427         callback.call(scope, result, arg, true);
9428     },
9429     
9430     // private
9431     update : function(params, records){
9432         
9433     }
9434 });/*
9435  * Based on:
9436  * Ext JS Library 1.1.1
9437  * Copyright(c) 2006-2007, Ext JS, LLC.
9438  *
9439  * Originally Released Under LGPL - original licence link has changed is not relivant.
9440  *
9441  * Fork - LGPL
9442  * <script type="text/javascript">
9443  */
9444 /**
9445  * @class Roo.data.HttpProxy
9446  * @extends Roo.data.DataProxy
9447  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
9448  * configured to reference a certain URL.<br><br>
9449  * <p>
9450  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
9451  * from which the running page was served.<br><br>
9452  * <p>
9453  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
9454  * <p>
9455  * Be aware that to enable the browser to parse an XML document, the server must set
9456  * the Content-Type header in the HTTP response to "text/xml".
9457  * @constructor
9458  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
9459  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
9460  * will be used to make the request.
9461  */
9462 Roo.data.HttpProxy = function(conn){
9463     Roo.data.HttpProxy.superclass.constructor.call(this);
9464     // is conn a conn config or a real conn?
9465     this.conn = conn;
9466     this.useAjax = !conn || !conn.events;
9467   
9468 };
9469
9470 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
9471     // thse are take from connection...
9472     
9473     /**
9474      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
9475      */
9476     /**
9477      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
9478      * extra parameters to each request made by this object. (defaults to undefined)
9479      */
9480     /**
9481      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
9482      *  to each request made by this object. (defaults to undefined)
9483      */
9484     /**
9485      * @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)
9486      */
9487     /**
9488      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
9489      */
9490      /**
9491      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
9492      * @type Boolean
9493      */
9494   
9495
9496     /**
9497      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
9498      * @type Boolean
9499      */
9500     /**
9501      * Return the {@link Roo.data.Connection} object being used by this Proxy.
9502      * @return {Connection} The Connection object. This object may be used to subscribe to events on
9503      * a finer-grained basis than the DataProxy events.
9504      */
9505     getConnection : function(){
9506         return this.useAjax ? Roo.Ajax : this.conn;
9507     },
9508
9509     /**
9510      * Load data from the configured {@link Roo.data.Connection}, read the data object into
9511      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
9512      * process that block using the passed callback.
9513      * @param {Object} params An object containing properties which are to be used as HTTP parameters
9514      * for the request to the remote server.
9515      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9516      * object into a block of Roo.data.Records.
9517      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9518      * The function must be passed <ul>
9519      * <li>The Record block object</li>
9520      * <li>The "arg" argument from the load function</li>
9521      * <li>A boolean success indicator</li>
9522      * </ul>
9523      * @param {Object} scope The scope in which to call the callback
9524      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9525      */
9526     load : function(params, reader, callback, scope, arg){
9527         if(this.fireEvent("beforeload", this, params) !== false){
9528             var  o = {
9529                 params : params || {},
9530                 request: {
9531                     callback : callback,
9532                     scope : scope,
9533                     arg : arg
9534                 },
9535                 reader: reader,
9536                 callback : this.loadResponse,
9537                 scope: this
9538             };
9539             if(this.useAjax){
9540                 Roo.applyIf(o, this.conn);
9541                 if(this.activeRequest){
9542                     Roo.Ajax.abort(this.activeRequest);
9543                 }
9544                 this.activeRequest = Roo.Ajax.request(o);
9545             }else{
9546                 this.conn.request(o);
9547             }
9548         }else{
9549             callback.call(scope||this, null, arg, false);
9550         }
9551     },
9552
9553     // private
9554     loadResponse : function(o, success, response){
9555         delete this.activeRequest;
9556         if(!success){
9557             this.fireEvent("loadexception", this, o, response);
9558             o.request.callback.call(o.request.scope, null, o.request.arg, false);
9559             return;
9560         }
9561         var result;
9562         try {
9563             result = o.reader.read(response);
9564         }catch(e){
9565             this.fireEvent("loadexception", this, o, response, e);
9566             o.request.callback.call(o.request.scope, null, o.request.arg, false);
9567             return;
9568         }
9569         
9570         this.fireEvent("load", this, o, o.request.arg);
9571         o.request.callback.call(o.request.scope, result, o.request.arg, true);
9572     },
9573
9574     // private
9575     update : function(dataSet){
9576
9577     },
9578
9579     // private
9580     updateResponse : function(dataSet){
9581
9582     }
9583 });/*
9584  * Based on:
9585  * Ext JS Library 1.1.1
9586  * Copyright(c) 2006-2007, Ext JS, LLC.
9587  *
9588  * Originally Released Under LGPL - original licence link has changed is not relivant.
9589  *
9590  * Fork - LGPL
9591  * <script type="text/javascript">
9592  */
9593
9594 /**
9595  * @class Roo.data.ScriptTagProxy
9596  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
9597  * other than the originating domain of the running page.<br><br>
9598  * <p>
9599  * <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
9600  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
9601  * <p>
9602  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
9603  * source code that is used as the source inside a &lt;script> tag.<br><br>
9604  * <p>
9605  * In order for the browser to process the returned data, the server must wrap the data object
9606  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
9607  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
9608  * depending on whether the callback name was passed:
9609  * <p>
9610  * <pre><code>
9611 boolean scriptTag = false;
9612 String cb = request.getParameter("callback");
9613 if (cb != null) {
9614     scriptTag = true;
9615     response.setContentType("text/javascript");
9616 } else {
9617     response.setContentType("application/x-json");
9618 }
9619 Writer out = response.getWriter();
9620 if (scriptTag) {
9621     out.write(cb + "(");
9622 }
9623 out.print(dataBlock.toJsonString());
9624 if (scriptTag) {
9625     out.write(");");
9626 }
9627 </pre></code>
9628  *
9629  * @constructor
9630  * @param {Object} config A configuration object.
9631  */
9632 Roo.data.ScriptTagProxy = function(config){
9633     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
9634     Roo.apply(this, config);
9635     this.head = document.getElementsByTagName("head")[0];
9636 };
9637
9638 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
9639
9640 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
9641     /**
9642      * @cfg {String} url The URL from which to request the data object.
9643      */
9644     /**
9645      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
9646      */
9647     timeout : 30000,
9648     /**
9649      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
9650      * the server the name of the callback function set up by the load call to process the returned data object.
9651      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
9652      * javascript output which calls this named function passing the data object as its only parameter.
9653      */
9654     callbackParam : "callback",
9655     /**
9656      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
9657      * name to the request.
9658      */
9659     nocache : true,
9660
9661     /**
9662      * Load data from the configured URL, read the data object into
9663      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9664      * process that block using the passed callback.
9665      * @param {Object} params An object containing properties which are to be used as HTTP parameters
9666      * for the request to the remote server.
9667      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9668      * object into a block of Roo.data.Records.
9669      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9670      * The function must be passed <ul>
9671      * <li>The Record block object</li>
9672      * <li>The "arg" argument from the load function</li>
9673      * <li>A boolean success indicator</li>
9674      * </ul>
9675      * @param {Object} scope The scope in which to call the callback
9676      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9677      */
9678     load : function(params, reader, callback, scope, arg){
9679         if(this.fireEvent("beforeload", this, params) !== false){
9680
9681             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
9682
9683             var url = this.url;
9684             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
9685             if(this.nocache){
9686                 url += "&_dc=" + (new Date().getTime());
9687             }
9688             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
9689             var trans = {
9690                 id : transId,
9691                 cb : "stcCallback"+transId,
9692                 scriptId : "stcScript"+transId,
9693                 params : params,
9694                 arg : arg,
9695                 url : url,
9696                 callback : callback,
9697                 scope : scope,
9698                 reader : reader
9699             };
9700             var conn = this;
9701
9702             window[trans.cb] = function(o){
9703                 conn.handleResponse(o, trans);
9704             };
9705
9706             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
9707
9708             if(this.autoAbort !== false){
9709                 this.abort();
9710             }
9711
9712             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
9713
9714             var script = document.createElement("script");
9715             script.setAttribute("src", url);
9716             script.setAttribute("type", "text/javascript");
9717             script.setAttribute("id", trans.scriptId);
9718             this.head.appendChild(script);
9719
9720             this.trans = trans;
9721         }else{
9722             callback.call(scope||this, null, arg, false);
9723         }
9724     },
9725
9726     // private
9727     isLoading : function(){
9728         return this.trans ? true : false;
9729     },
9730
9731     /**
9732      * Abort the current server request.
9733      */
9734     abort : function(){
9735         if(this.isLoading()){
9736             this.destroyTrans(this.trans);
9737         }
9738     },
9739
9740     // private
9741     destroyTrans : function(trans, isLoaded){
9742         this.head.removeChild(document.getElementById(trans.scriptId));
9743         clearTimeout(trans.timeoutId);
9744         if(isLoaded){
9745             window[trans.cb] = undefined;
9746             try{
9747                 delete window[trans.cb];
9748             }catch(e){}
9749         }else{
9750             // if hasn't been loaded, wait for load to remove it to prevent script error
9751             window[trans.cb] = function(){
9752                 window[trans.cb] = undefined;
9753                 try{
9754                     delete window[trans.cb];
9755                 }catch(e){}
9756             };
9757         }
9758     },
9759
9760     // private
9761     handleResponse : function(o, trans){
9762         this.trans = false;
9763         this.destroyTrans(trans, true);
9764         var result;
9765         try {
9766             result = trans.reader.readRecords(o);
9767         }catch(e){
9768             this.fireEvent("loadexception", this, o, trans.arg, e);
9769             trans.callback.call(trans.scope||window, null, trans.arg, false);
9770             return;
9771         }
9772         this.fireEvent("load", this, o, trans.arg);
9773         trans.callback.call(trans.scope||window, result, trans.arg, true);
9774     },
9775
9776     // private
9777     handleFailure : function(trans){
9778         this.trans = false;
9779         this.destroyTrans(trans, false);
9780         this.fireEvent("loadexception", this, null, trans.arg);
9781         trans.callback.call(trans.scope||window, null, trans.arg, false);
9782     }
9783 });/*
9784  * Based on:
9785  * Ext JS Library 1.1.1
9786  * Copyright(c) 2006-2007, Ext JS, LLC.
9787  *
9788  * Originally Released Under LGPL - original licence link has changed is not relivant.
9789  *
9790  * Fork - LGPL
9791  * <script type="text/javascript">
9792  */
9793
9794 /**
9795  * @class Roo.data.JsonReader
9796  * @extends Roo.data.DataReader
9797  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
9798  * based on mappings in a provided Roo.data.Record constructor.
9799  * 
9800  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
9801  * in the reply previously. 
9802  * 
9803  * <p>
9804  * Example code:
9805  * <pre><code>
9806 var RecordDef = Roo.data.Record.create([
9807     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
9808     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
9809 ]);
9810 var myReader = new Roo.data.JsonReader({
9811     totalProperty: "results",    // The property which contains the total dataset size (optional)
9812     root: "rows",                // The property which contains an Array of row objects
9813     id: "id"                     // The property within each row object that provides an ID for the record (optional)
9814 }, RecordDef);
9815 </code></pre>
9816  * <p>
9817  * This would consume a JSON file like this:
9818  * <pre><code>
9819 { 'results': 2, 'rows': [
9820     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
9821     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
9822 }
9823 </code></pre>
9824  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
9825  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
9826  * paged from the remote server.
9827  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
9828  * @cfg {String} root name of the property which contains the Array of row objects.
9829  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
9830  * @constructor
9831  * Create a new JsonReader
9832  * @param {Object} meta Metadata configuration options
9833  * @param {Object} recordType Either an Array of field definition objects,
9834  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
9835  */
9836 Roo.data.JsonReader = function(meta, recordType){
9837     
9838     meta = meta || {};
9839     // set some defaults:
9840     Roo.applyIf(meta, {
9841         totalProperty: 'total',
9842         successProperty : 'success',
9843         root : 'data',
9844         id : 'id'
9845     });
9846     
9847     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
9848 };
9849 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
9850     
9851     /**
9852      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
9853      * Used by Store query builder to append _requestMeta to params.
9854      * 
9855      */
9856     metaFromRemote : false,
9857     /**
9858      * This method is only used by a DataProxy which has retrieved data from a remote server.
9859      * @param {Object} response The XHR object which contains the JSON data in its responseText.
9860      * @return {Object} data A data block which is used by an Roo.data.Store object as
9861      * a cache of Roo.data.Records.
9862      */
9863     read : function(response){
9864         var json = response.responseText;
9865        
9866         var o = /* eval:var:o */ eval("("+json+")");
9867         if(!o) {
9868             throw {message: "JsonReader.read: Json object not found"};
9869         }
9870         
9871         if(o.metaData){
9872             
9873             delete this.ef;
9874             this.metaFromRemote = true;
9875             this.meta = o.metaData;
9876             this.recordType = Roo.data.Record.create(o.metaData.fields);
9877             this.onMetaChange(this.meta, this.recordType, o);
9878         }
9879         return this.readRecords(o);
9880     },
9881
9882     // private function a store will implement
9883     onMetaChange : function(meta, recordType, o){
9884
9885     },
9886
9887     /**
9888          * @ignore
9889          */
9890     simpleAccess: function(obj, subsc) {
9891         return obj[subsc];
9892     },
9893
9894         /**
9895          * @ignore
9896          */
9897     getJsonAccessor: function(){
9898         var re = /[\[\.]/;
9899         return function(expr) {
9900             try {
9901                 return(re.test(expr))
9902                     ? new Function("obj", "return obj." + expr)
9903                     : function(obj){
9904                         return obj[expr];
9905                     };
9906             } catch(e){}
9907             return Roo.emptyFn;
9908         };
9909     }(),
9910
9911     /**
9912      * Create a data block containing Roo.data.Records from an XML document.
9913      * @param {Object} o An object which contains an Array of row objects in the property specified
9914      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
9915      * which contains the total size of the dataset.
9916      * @return {Object} data A data block which is used by an Roo.data.Store object as
9917      * a cache of Roo.data.Records.
9918      */
9919     readRecords : function(o){
9920         /**
9921          * After any data loads, the raw JSON data is available for further custom processing.
9922          * @type Object
9923          */
9924         this.o = o;
9925         var s = this.meta, Record = this.recordType,
9926             f = Record.prototype.fields, fi = f.items, fl = f.length;
9927
9928 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
9929         if (!this.ef) {
9930             if(s.totalProperty) {
9931                     this.getTotal = this.getJsonAccessor(s.totalProperty);
9932                 }
9933                 if(s.successProperty) {
9934                     this.getSuccess = this.getJsonAccessor(s.successProperty);
9935                 }
9936                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
9937                 if (s.id) {
9938                         var g = this.getJsonAccessor(s.id);
9939                         this.getId = function(rec) {
9940                                 var r = g(rec);
9941                                 return (r === undefined || r === "") ? null : r;
9942                         };
9943                 } else {
9944                         this.getId = function(){return null;};
9945                 }
9946             this.ef = [];
9947             for(var jj = 0; jj < fl; jj++){
9948                 f = fi[jj];
9949                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
9950                 this.ef[jj] = this.getJsonAccessor(map);
9951             }
9952         }
9953
9954         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
9955         if(s.totalProperty){
9956             var vt = parseInt(this.getTotal(o), 10);
9957             if(!isNaN(vt)){
9958                 totalRecords = vt;
9959             }
9960         }
9961         if(s.successProperty){
9962             var vs = this.getSuccess(o);
9963             if(vs === false || vs === 'false'){
9964                 success = false;
9965             }
9966         }
9967         var records = [];
9968             for(var i = 0; i < c; i++){
9969                     var n = root[i];
9970                 var values = {};
9971                 var id = this.getId(n);
9972                 for(var j = 0; j < fl; j++){
9973                     f = fi[j];
9974                 var v = this.ef[j](n);
9975                 if (!f.convert) {
9976                     Roo.log('missing convert for ' + f.name);
9977                     Roo.log(f);
9978                     continue;
9979                 }
9980                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
9981                 }
9982                 var record = new Record(values, id);
9983                 record.json = n;
9984                 records[i] = record;
9985             }
9986             return {
9987             raw : o,
9988                 success : success,
9989                 records : records,
9990                 totalRecords : totalRecords
9991             };
9992     }
9993 });/*
9994  * Based on:
9995  * Ext JS Library 1.1.1
9996  * Copyright(c) 2006-2007, Ext JS, LLC.
9997  *
9998  * Originally Released Under LGPL - original licence link has changed is not relivant.
9999  *
10000  * Fork - LGPL
10001  * <script type="text/javascript">
10002  */
10003
10004 /**
10005  * @class Roo.data.ArrayReader
10006  * @extends Roo.data.DataReader
10007  * Data reader class to create an Array of Roo.data.Record objects from an Array.
10008  * Each element of that Array represents a row of data fields. The
10009  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
10010  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
10011  * <p>
10012  * Example code:.
10013  * <pre><code>
10014 var RecordDef = Roo.data.Record.create([
10015     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
10016     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
10017 ]);
10018 var myReader = new Roo.data.ArrayReader({
10019     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
10020 }, RecordDef);
10021 </code></pre>
10022  * <p>
10023  * This would consume an Array like this:
10024  * <pre><code>
10025 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
10026   </code></pre>
10027  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
10028  * @constructor
10029  * Create a new JsonReader
10030  * @param {Object} meta Metadata configuration options.
10031  * @param {Object} recordType Either an Array of field definition objects
10032  * as specified to {@link Roo.data.Record#create},
10033  * or an {@link Roo.data.Record} object
10034  * created using {@link Roo.data.Record#create}.
10035  */
10036 Roo.data.ArrayReader = function(meta, recordType){
10037     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
10038 };
10039
10040 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
10041     /**
10042      * Create a data block containing Roo.data.Records from an XML document.
10043      * @param {Object} o An Array of row objects which represents the dataset.
10044      * @return {Object} data A data block which is used by an Roo.data.Store object as
10045      * a cache of Roo.data.Records.
10046      */
10047     readRecords : function(o){
10048         var sid = this.meta ? this.meta.id : null;
10049         var recordType = this.recordType, fields = recordType.prototype.fields;
10050         var records = [];
10051         var root = o;
10052             for(var i = 0; i < root.length; i++){
10053                     var n = root[i];
10054                 var values = {};
10055                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
10056                 for(var j = 0, jlen = fields.length; j < jlen; j++){
10057                 var f = fields.items[j];
10058                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
10059                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
10060                 v = f.convert(v);
10061                 values[f.name] = v;
10062             }
10063                 var record = new recordType(values, id);
10064                 record.json = n;
10065                 records[records.length] = record;
10066             }
10067             return {
10068                 records : records,
10069                 totalRecords : records.length
10070             };
10071     }
10072 });/*
10073  * - LGPL
10074  * * 
10075  */
10076
10077 /**
10078  * @class Roo.bootstrap.ComboBox
10079  * @extends Roo.bootstrap.TriggerField
10080  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
10081  * @cfg {Boolean} append (true|false) default false
10082  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
10083  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
10084  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
10085  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
10086  * @constructor
10087  * Create a new ComboBox.
10088  * @param {Object} config Configuration options
10089  */
10090 Roo.bootstrap.ComboBox = function(config){
10091     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
10092     this.addEvents({
10093         /**
10094          * @event expand
10095          * Fires when the dropdown list is expanded
10096              * @param {Roo.bootstrap.ComboBox} combo This combo box
10097              */
10098         'expand' : true,
10099         /**
10100          * @event collapse
10101          * Fires when the dropdown list is collapsed
10102              * @param {Roo.bootstrap.ComboBox} combo This combo box
10103              */
10104         'collapse' : true,
10105         /**
10106          * @event beforeselect
10107          * Fires before a list item is selected. Return false to cancel the selection.
10108              * @param {Roo.bootstrap.ComboBox} combo This combo box
10109              * @param {Roo.data.Record} record The data record returned from the underlying store
10110              * @param {Number} index The index of the selected item in the dropdown list
10111              */
10112         'beforeselect' : true,
10113         /**
10114          * @event select
10115          * Fires when a list item is selected
10116              * @param {Roo.bootstrap.ComboBox} combo This combo box
10117              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
10118              * @param {Number} index The index of the selected item in the dropdown list
10119              */
10120         'select' : true,
10121         /**
10122          * @event beforequery
10123          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
10124          * The event object passed has these properties:
10125              * @param {Roo.bootstrap.ComboBox} combo This combo box
10126              * @param {String} query The query
10127              * @param {Boolean} forceAll true to force "all" query
10128              * @param {Boolean} cancel true to cancel the query
10129              * @param {Object} e The query event object
10130              */
10131         'beforequery': true,
10132          /**
10133          * @event add
10134          * Fires when the 'add' icon is pressed (add a listener to enable add button)
10135              * @param {Roo.bootstrap.ComboBox} combo This combo box
10136              */
10137         'add' : true,
10138         /**
10139          * @event edit
10140          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
10141              * @param {Roo.bootstrap.ComboBox} combo This combo box
10142              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
10143              */
10144         'edit' : true,
10145         /**
10146          * @event remove
10147          * Fires when the remove value from the combobox array
10148              * @param {Roo.bootstrap.ComboBox} combo This combo box
10149              */
10150         'remove' : true
10151         
10152     });
10153     
10154     this.item = [];
10155     this.tickItems = [];
10156     
10157     this.selectedIndex = -1;
10158     if(this.mode == 'local'){
10159         if(config.queryDelay === undefined){
10160             this.queryDelay = 10;
10161         }
10162         if(config.minChars === undefined){
10163             this.minChars = 0;
10164         }
10165     }
10166 };
10167
10168 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
10169      
10170     /**
10171      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
10172      * rendering into an Roo.Editor, defaults to false)
10173      */
10174     /**
10175      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
10176      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
10177      */
10178     /**
10179      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
10180      */
10181     /**
10182      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
10183      * the dropdown list (defaults to undefined, with no header element)
10184      */
10185
10186      /**
10187      * @cfg {String/Roo.Template} tpl The template to use to render the output
10188      */
10189      
10190      /**
10191      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
10192      */
10193     listWidth: undefined,
10194     /**
10195      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
10196      * mode = 'remote' or 'text' if mode = 'local')
10197      */
10198     displayField: undefined,
10199     /**
10200      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
10201      * mode = 'remote' or 'value' if mode = 'local'). 
10202      * Note: use of a valueField requires the user make a selection
10203      * in order for a value to be mapped.
10204      */
10205     valueField: undefined,
10206     
10207     
10208     /**
10209      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
10210      * field's data value (defaults to the underlying DOM element's name)
10211      */
10212     hiddenName: undefined,
10213     /**
10214      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
10215      */
10216     listClass: '',
10217     /**
10218      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
10219      */
10220     selectedClass: 'active',
10221     
10222     /**
10223      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
10224      */
10225     shadow:'sides',
10226     /**
10227      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
10228      * anchor positions (defaults to 'tl-bl')
10229      */
10230     listAlign: 'tl-bl?',
10231     /**
10232      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
10233      */
10234     maxHeight: 300,
10235     /**
10236      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
10237      * query specified by the allQuery config option (defaults to 'query')
10238      */
10239     triggerAction: 'query',
10240     /**
10241      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
10242      * (defaults to 4, does not apply if editable = false)
10243      */
10244     minChars : 4,
10245     /**
10246      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
10247      * delay (typeAheadDelay) if it matches a known value (defaults to false)
10248      */
10249     typeAhead: false,
10250     /**
10251      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
10252      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
10253      */
10254     queryDelay: 500,
10255     /**
10256      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
10257      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
10258      */
10259     pageSize: 0,
10260     /**
10261      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
10262      * when editable = true (defaults to false)
10263      */
10264     selectOnFocus:false,
10265     /**
10266      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
10267      */
10268     queryParam: 'query',
10269     /**
10270      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
10271      * when mode = 'remote' (defaults to 'Loading...')
10272      */
10273     loadingText: 'Loading...',
10274     /**
10275      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
10276      */
10277     resizable: false,
10278     /**
10279      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
10280      */
10281     handleHeight : 8,
10282     /**
10283      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
10284      * traditional select (defaults to true)
10285      */
10286     editable: true,
10287     /**
10288      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
10289      */
10290     allQuery: '',
10291     /**
10292      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
10293      */
10294     mode: 'remote',
10295     /**
10296      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
10297      * listWidth has a higher value)
10298      */
10299     minListWidth : 70,
10300     /**
10301      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
10302      * allow the user to set arbitrary text into the field (defaults to false)
10303      */
10304     forceSelection:false,
10305     /**
10306      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
10307      * if typeAhead = true (defaults to 250)
10308      */
10309     typeAheadDelay : 250,
10310     /**
10311      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
10312      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
10313      */
10314     valueNotFoundText : undefined,
10315     /**
10316      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
10317      */
10318     blockFocus : false,
10319     
10320     /**
10321      * @cfg {Boolean} disableClear Disable showing of clear button.
10322      */
10323     disableClear : false,
10324     /**
10325      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
10326      */
10327     alwaysQuery : false,
10328     
10329     /**
10330      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
10331      */
10332     multiple : false,
10333     
10334     //private
10335     addicon : false,
10336     editicon: false,
10337     
10338     page: 0,
10339     hasQuery: false,
10340     append: false,
10341     loadNext: false,
10342     autoFocus : true,
10343     tickable : false,
10344     btnPosition : 'right',
10345     triggerList : true,
10346     // element that contains real text value.. (when hidden is used..)
10347     
10348     getAutoCreate : function()
10349     {
10350         var cfg = false;
10351         
10352         /*
10353          *  Normal ComboBox
10354          */
10355         if(!this.tickable){
10356             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
10357             return cfg;
10358         }
10359         
10360         /*
10361          *  ComboBox with tickable selections
10362          */
10363              
10364         var align = this.labelAlign || this.parentLabelAlign();
10365         
10366         cfg = {
10367             cls : 'form-group roo-combobox-tickable' //input-group
10368         };
10369         
10370         
10371         var buttons = {
10372             tag : 'div',
10373             cls : 'tickable-buttons',
10374             cn : [
10375                 {
10376                     tag : 'button',
10377                     type : 'button',
10378                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
10379                     html : 'Edit'
10380                 },
10381                 {
10382                     tag : 'button',
10383                     type : 'button',
10384                     name : 'ok',
10385                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
10386                     html : 'Done'
10387                 },
10388                 {
10389                     tag : 'button',
10390                     type : 'button',
10391                     name : 'cancel',
10392                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
10393                     html : 'Cancel'
10394                 }
10395             ]
10396         };
10397         
10398         var _this = this;
10399         Roo.each(buttons.cn, function(c){
10400             if (_this.size) {
10401                 c.cls += ' btn-' + _this.size;
10402             }
10403
10404             if (_this.disabled) {
10405                 c.disabled = true;
10406             }
10407         });
10408         
10409         var box = {
10410             tag: 'div',
10411             cn: [
10412                 {
10413                     tag: 'input',
10414                     type : 'hidden',
10415                     cls: 'form-hidden-field'
10416                 },
10417                 {
10418                     tag: 'ul',
10419                     cls: 'select2-choices',
10420                     cn:[
10421                         {
10422                             tag: 'li',
10423                             cls: 'select2-search-field',
10424                             cn: [
10425
10426                                 buttons
10427                             ]
10428                         }
10429                     ]
10430                 }
10431             ]
10432         }
10433         
10434         var combobox = {
10435             cls: 'select2-container input-group select2-container-multi',
10436             cn: [
10437                 box
10438 //                {
10439 //                    tag: 'ul',
10440 //                    cls: 'typeahead typeahead-long dropdown-menu',
10441 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
10442 //                }
10443             ]
10444         };
10445         
10446         if (align ==='left' && this.fieldLabel.length) {
10447             
10448                 Roo.log("left and has label");
10449                 cfg.cn = [
10450                     
10451                     {
10452                         tag: 'label',
10453                         'for' :  id,
10454                         cls : 'control-label col-sm-' + this.labelWidth,
10455                         html : this.fieldLabel
10456                         
10457                     },
10458                     {
10459                         cls : "col-sm-" + (12 - this.labelWidth), 
10460                         cn: [
10461                             combobox
10462                         ]
10463                     }
10464                     
10465                 ];
10466         } else if ( this.fieldLabel.length) {
10467                 Roo.log(" label");
10468                  cfg.cn = [
10469                    
10470                     {
10471                         tag: 'label',
10472                         //cls : 'input-group-addon',
10473                         html : this.fieldLabel
10474                         
10475                     },
10476                     
10477                     combobox
10478                     
10479                 ];
10480
10481         } else {
10482             
10483                 Roo.log(" no label && no align");
10484                 cfg = combobox
10485                      
10486                 
10487         }
10488          
10489         var settings=this;
10490         ['xs','sm','md','lg'].map(function(size){
10491             if (settings[size]) {
10492                 cfg.cls += ' col-' + size + '-' + settings[size];
10493             }
10494         });
10495         
10496         return cfg;
10497         
10498     },
10499     
10500     // private
10501     initEvents: function()
10502     {
10503         
10504         if (!this.store) {
10505             throw "can not find store for combo";
10506         }
10507         this.store = Roo.factory(this.store, Roo.data);
10508         
10509         if(this.tickable){
10510             this.initTickableEvents();
10511             return;
10512         }
10513         
10514         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
10515         
10516         if(this.hiddenName){
10517             
10518             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10519             
10520             this.hiddenField.dom.value =
10521                 this.hiddenValue !== undefined ? this.hiddenValue :
10522                 this.value !== undefined ? this.value : '';
10523
10524             // prevent input submission
10525             this.el.dom.removeAttribute('name');
10526             this.hiddenField.dom.setAttribute('name', this.hiddenName);
10527              
10528              
10529         }
10530         //if(Roo.isGecko){
10531         //    this.el.dom.setAttribute('autocomplete', 'off');
10532         //}
10533         
10534         var cls = 'x-combo-list';
10535         
10536         //this.list = new Roo.Layer({
10537         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
10538         //});
10539         
10540         var _this = this;
10541         
10542         (function(){
10543             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
10544             _this.list.setWidth(lw);
10545         }).defer(100);
10546         
10547         this.list.on('mouseover', this.onViewOver, this);
10548         this.list.on('mousemove', this.onViewMove, this);
10549         
10550         this.list.on('scroll', this.onViewScroll, this);
10551         
10552         /*
10553         this.list.swallowEvent('mousewheel');
10554         this.assetHeight = 0;
10555
10556         if(this.title){
10557             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
10558             this.assetHeight += this.header.getHeight();
10559         }
10560
10561         this.innerList = this.list.createChild({cls:cls+'-inner'});
10562         this.innerList.on('mouseover', this.onViewOver, this);
10563         this.innerList.on('mousemove', this.onViewMove, this);
10564         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
10565         
10566         if(this.allowBlank && !this.pageSize && !this.disableClear){
10567             this.footer = this.list.createChild({cls:cls+'-ft'});
10568             this.pageTb = new Roo.Toolbar(this.footer);
10569            
10570         }
10571         if(this.pageSize){
10572             this.footer = this.list.createChild({cls:cls+'-ft'});
10573             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
10574                     {pageSize: this.pageSize});
10575             
10576         }
10577         
10578         if (this.pageTb && this.allowBlank && !this.disableClear) {
10579             var _this = this;
10580             this.pageTb.add(new Roo.Toolbar.Fill(), {
10581                 cls: 'x-btn-icon x-btn-clear',
10582                 text: '&#160;',
10583                 handler: function()
10584                 {
10585                     _this.collapse();
10586                     _this.clearValue();
10587                     _this.onSelect(false, -1);
10588                 }
10589             });
10590         }
10591         if (this.footer) {
10592             this.assetHeight += this.footer.getHeight();
10593         }
10594         */
10595             
10596         if(!this.tpl){
10597             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
10598         }
10599
10600         this.view = new Roo.View(this.list, this.tpl, {
10601             singleSelect:true, store: this.store, selectedClass: this.selectedClass
10602         });
10603         //this.view.wrapEl.setDisplayed(false);
10604         this.view.on('click', this.onViewClick, this);
10605         
10606         
10607         
10608         this.store.on('beforeload', this.onBeforeLoad, this);
10609         this.store.on('load', this.onLoad, this);
10610         this.store.on('loadexception', this.onLoadException, this);
10611         /*
10612         if(this.resizable){
10613             this.resizer = new Roo.Resizable(this.list,  {
10614                pinned:true, handles:'se'
10615             });
10616             this.resizer.on('resize', function(r, w, h){
10617                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
10618                 this.listWidth = w;
10619                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
10620                 this.restrictHeight();
10621             }, this);
10622             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
10623         }
10624         */
10625         if(!this.editable){
10626             this.editable = true;
10627             this.setEditable(false);
10628         }
10629         
10630         /*
10631         
10632         if (typeof(this.events.add.listeners) != 'undefined') {
10633             
10634             this.addicon = this.wrap.createChild(
10635                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
10636        
10637             this.addicon.on('click', function(e) {
10638                 this.fireEvent('add', this);
10639             }, this);
10640         }
10641         if (typeof(this.events.edit.listeners) != 'undefined') {
10642             
10643             this.editicon = this.wrap.createChild(
10644                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
10645             if (this.addicon) {
10646                 this.editicon.setStyle('margin-left', '40px');
10647             }
10648             this.editicon.on('click', function(e) {
10649                 
10650                 // we fire even  if inothing is selected..
10651                 this.fireEvent('edit', this, this.lastData );
10652                 
10653             }, this);
10654         }
10655         */
10656         
10657         this.keyNav = new Roo.KeyNav(this.inputEl(), {
10658             "up" : function(e){
10659                 this.inKeyMode = true;
10660                 this.selectPrev();
10661             },
10662
10663             "down" : function(e){
10664                 if(!this.isExpanded()){
10665                     this.onTriggerClick();
10666                 }else{
10667                     this.inKeyMode = true;
10668                     this.selectNext();
10669                 }
10670             },
10671
10672             "enter" : function(e){
10673 //                this.onViewClick();
10674                 //return true;
10675                 this.collapse();
10676                 
10677                 if(this.fireEvent("specialkey", this, e)){
10678                     this.onViewClick(false);
10679                 }
10680                 
10681                 return true;
10682             },
10683
10684             "esc" : function(e){
10685                 this.collapse();
10686             },
10687
10688             "tab" : function(e){
10689                 this.collapse();
10690                 
10691                 if(this.fireEvent("specialkey", this, e)){
10692                     this.onViewClick(false);
10693                 }
10694                 
10695                 return true;
10696             },
10697
10698             scope : this,
10699
10700             doRelay : function(foo, bar, hname){
10701                 if(hname == 'down' || this.scope.isExpanded()){
10702                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
10703                 }
10704                 return true;
10705             },
10706
10707             forceKeyDown: true
10708         });
10709         
10710         
10711         this.queryDelay = Math.max(this.queryDelay || 10,
10712                 this.mode == 'local' ? 10 : 250);
10713         
10714         
10715         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
10716         
10717         if(this.typeAhead){
10718             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
10719         }
10720         if(this.editable !== false){
10721             this.inputEl().on("keyup", this.onKeyUp, this);
10722         }
10723         if(this.forceSelection){
10724             this.inputEl().on('blur', this.doForce, this);
10725         }
10726         
10727         if(this.multiple){
10728             this.choices = this.el.select('ul.select2-choices', true).first();
10729             this.searchField = this.el.select('ul li.select2-search-field', true).first();
10730         }
10731     },
10732     
10733     initTickableEvents: function()
10734     {   
10735         this.createList();
10736         
10737         if(this.hiddenName){
10738             
10739             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10740             
10741             this.hiddenField.dom.value =
10742                 this.hiddenValue !== undefined ? this.hiddenValue :
10743                 this.value !== undefined ? this.value : '';
10744
10745             // prevent input submission
10746             this.el.dom.removeAttribute('name');
10747             this.hiddenField.dom.setAttribute('name', this.hiddenName);
10748              
10749              
10750         }
10751         
10752 //        this.list = this.el.select('ul.dropdown-menu',true).first();
10753         
10754         this.choices = this.el.select('ul.select2-choices', true).first();
10755         this.searchField = this.el.select('ul li.select2-search-field', true).first();
10756         if(this.triggerList){
10757             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
10758         }
10759          
10760         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
10761         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
10762         
10763         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
10764         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
10765         
10766         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
10767         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
10768         
10769         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
10770         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
10771         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
10772         
10773         this.okBtn.hide();
10774         this.cancelBtn.hide();
10775         
10776         var _this = this;
10777         
10778         (function(){
10779             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
10780             _this.list.setWidth(lw);
10781         }).defer(100);
10782         
10783         this.list.on('mouseover', this.onViewOver, this);
10784         this.list.on('mousemove', this.onViewMove, this);
10785         
10786         this.list.on('scroll', this.onViewScroll, this);
10787         
10788         if(!this.tpl){
10789             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>';
10790         }
10791
10792         this.view = new Roo.View(this.list, this.tpl, {
10793             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
10794         });
10795         
10796         //this.view.wrapEl.setDisplayed(false);
10797         this.view.on('click', this.onViewClick, this);
10798         
10799         
10800         
10801         this.store.on('beforeload', this.onBeforeLoad, this);
10802         this.store.on('load', this.onLoad, this);
10803         this.store.on('loadexception', this.onLoadException, this);
10804         
10805 //        this.keyNav = new Roo.KeyNav(this.inputEl(), {
10806 //            "up" : function(e){
10807 //                this.inKeyMode = true;
10808 //                this.selectPrev();
10809 //            },
10810 //
10811 //            "down" : function(e){
10812 //                if(!this.isExpanded()){
10813 //                    this.onTriggerClick();
10814 //                }else{
10815 //                    this.inKeyMode = true;
10816 //                    this.selectNext();
10817 //                }
10818 //            },
10819 //
10820 //            "enter" : function(e){
10821 ////                this.onViewClick();
10822 //                //return true;
10823 //                this.collapse();
10824 //                
10825 //                if(this.fireEvent("specialkey", this, e)){
10826 //                    this.onViewClick(false);
10827 //                }
10828 //                
10829 //                return true;
10830 //            },
10831 //
10832 //            "esc" : function(e){
10833 //                this.collapse();
10834 //            },
10835 //
10836 //            "tab" : function(e){
10837 //                this.collapse();
10838 //                
10839 //                if(this.fireEvent("specialkey", this, e)){
10840 //                    this.onViewClick(false);
10841 //                }
10842 //                
10843 //                return true;
10844 //            },
10845 //
10846 //            scope : this,
10847 //
10848 //            doRelay : function(foo, bar, hname){
10849 //                if(hname == 'down' || this.scope.isExpanded()){
10850 //                   return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
10851 //                }
10852 //                return true;
10853 //            },
10854 //
10855 //            forceKeyDown: true
10856 //        });
10857         
10858         
10859         this.queryDelay = Math.max(this.queryDelay || 10,
10860                 this.mode == 'local' ? 10 : 250);
10861         
10862         
10863         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
10864         
10865         if(this.typeAhead){
10866             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
10867         }
10868     },
10869
10870     onDestroy : function(){
10871         if(this.view){
10872             this.view.setStore(null);
10873             this.view.el.removeAllListeners();
10874             this.view.el.remove();
10875             this.view.purgeListeners();
10876         }
10877         if(this.list){
10878             this.list.dom.innerHTML  = '';
10879         }
10880         
10881         if(this.store){
10882             this.store.un('beforeload', this.onBeforeLoad, this);
10883             this.store.un('load', this.onLoad, this);
10884             this.store.un('loadexception', this.onLoadException, this);
10885         }
10886         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
10887     },
10888
10889     // private
10890     fireKey : function(e){
10891         if(e.isNavKeyPress() && !this.list.isVisible()){
10892             this.fireEvent("specialkey", this, e);
10893         }
10894     },
10895
10896     // private
10897     onResize: function(w, h){
10898 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
10899 //        
10900 //        if(typeof w != 'number'){
10901 //            // we do not handle it!?!?
10902 //            return;
10903 //        }
10904 //        var tw = this.trigger.getWidth();
10905 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
10906 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
10907 //        var x = w - tw;
10908 //        this.inputEl().setWidth( this.adjustWidth('input', x));
10909 //            
10910 //        //this.trigger.setStyle('left', x+'px');
10911 //        
10912 //        if(this.list && this.listWidth === undefined){
10913 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
10914 //            this.list.setWidth(lw);
10915 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
10916 //        }
10917         
10918     
10919         
10920     },
10921
10922     /**
10923      * Allow or prevent the user from directly editing the field text.  If false is passed,
10924      * the user will only be able to select from the items defined in the dropdown list.  This method
10925      * is the runtime equivalent of setting the 'editable' config option at config time.
10926      * @param {Boolean} value True to allow the user to directly edit the field text
10927      */
10928     setEditable : function(value){
10929         if(value == this.editable){
10930             return;
10931         }
10932         this.editable = value;
10933         if(!value){
10934             this.inputEl().dom.setAttribute('readOnly', true);
10935             this.inputEl().on('mousedown', this.onTriggerClick,  this);
10936             this.inputEl().addClass('x-combo-noedit');
10937         }else{
10938             this.inputEl().dom.setAttribute('readOnly', false);
10939             this.inputEl().un('mousedown', this.onTriggerClick,  this);
10940             this.inputEl().removeClass('x-combo-noedit');
10941         }
10942     },
10943
10944     // private
10945     
10946     onBeforeLoad : function(combo,opts){
10947         if(!this.hasFocus){
10948             return;
10949         }
10950          if (!opts.add) {
10951             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
10952          }
10953 //        this.restrictHeight();
10954         this.selectedIndex = -1;
10955     },
10956
10957     // private
10958     onLoad : function(){
10959         
10960         this.hasQuery = false;
10961         
10962         if(!this.hasFocus){
10963             return;
10964         }
10965         
10966         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
10967             this.loading.hide();
10968         }
10969         
10970         if(this.store.getCount() > 0){
10971             this.expand();
10972 //            this.restrictHeight();
10973             if(this.lastQuery == this.allQuery){
10974                 if(this.editable && !this.tickable){
10975                     this.inputEl().dom.select();
10976                 }
10977                 if(!this.selectByValue(this.value, true) && this.autoFocus){
10978                     this.select(0, true);
10979                 }
10980             }else{
10981                 if(this.autoFocus){
10982                     this.selectNext();
10983                 }
10984                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
10985                     this.taTask.delay(this.typeAheadDelay);
10986                 }
10987             }
10988         }else{
10989             this.onEmptyResults();
10990         }
10991         
10992         //this.el.focus();
10993     },
10994     // private
10995     onLoadException : function()
10996     {
10997         this.hasQuery = false;
10998         
10999         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11000             this.loading.hide();
11001         }
11002         
11003         this.collapse();
11004         Roo.log(this.store.reader.jsonData);
11005         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
11006             // fixme
11007             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
11008         }
11009         
11010         
11011     },
11012     // private
11013     onTypeAhead : function(){
11014         if(this.store.getCount() > 0){
11015             var r = this.store.getAt(0);
11016             var newValue = r.data[this.displayField];
11017             var len = newValue.length;
11018             var selStart = this.getRawValue().length;
11019             
11020             if(selStart != len){
11021                 this.setRawValue(newValue);
11022                 this.selectText(selStart, newValue.length);
11023             }
11024         }
11025     },
11026
11027     // private
11028     onSelect : function(record, index){
11029         
11030         if(this.fireEvent('beforeselect', this, record, index) !== false){
11031         
11032             this.setFromData(index > -1 ? record.data : false);
11033             
11034             this.collapse();
11035             this.fireEvent('select', this, record, index);
11036         }
11037     },
11038
11039     /**
11040      * Returns the currently selected field value or empty string if no value is set.
11041      * @return {String} value The selected value
11042      */
11043     getValue : function(){
11044         
11045         if(this.multiple){
11046             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
11047         }
11048         
11049         if(this.valueField){
11050             return typeof this.value != 'undefined' ? this.value : '';
11051         }else{
11052             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
11053         }
11054     },
11055
11056     /**
11057      * Clears any text/value currently set in the field
11058      */
11059     clearValue : function(){
11060         if(this.hiddenField){
11061             this.hiddenField.dom.value = '';
11062         }
11063         this.value = '';
11064         this.setRawValue('');
11065         this.lastSelectionText = '';
11066         
11067     },
11068
11069     /**
11070      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
11071      * will be displayed in the field.  If the value does not match the data value of an existing item,
11072      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
11073      * Otherwise the field will be blank (although the value will still be set).
11074      * @param {String} value The value to match
11075      */
11076     setValue : function(v){
11077         if(this.multiple){
11078             this.syncValue();
11079             return;
11080         }
11081         
11082         var text = v;
11083         if(this.valueField){
11084             var r = this.findRecord(this.valueField, v);
11085             if(r){
11086                 text = r.data[this.displayField];
11087             }else if(this.valueNotFoundText !== undefined){
11088                 text = this.valueNotFoundText;
11089             }
11090         }
11091         this.lastSelectionText = text;
11092         if(this.hiddenField){
11093             this.hiddenField.dom.value = v;
11094         }
11095         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
11096         this.value = v;
11097     },
11098     /**
11099      * @property {Object} the last set data for the element
11100      */
11101     
11102     lastData : false,
11103     /**
11104      * Sets the value of the field based on a object which is related to the record format for the store.
11105      * @param {Object} value the value to set as. or false on reset?
11106      */
11107     setFromData : function(o){
11108         
11109         if(this.multiple){
11110             if(typeof o.display_name !== 'string'){
11111                 for(var i=0;i<o.display_name.length;i++){
11112                     this.addItem({'id':o.id[i],'display_name':o.display_name[i]});
11113                 }
11114                 return;
11115             }
11116             this.addItem(o);
11117             return;
11118         }
11119             
11120         var dv = ''; // display value
11121         var vv = ''; // value value..
11122         this.lastData = o;
11123         if (this.displayField) {
11124             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11125         } else {
11126             // this is an error condition!!!
11127             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
11128         }
11129         
11130         if(this.valueField){
11131             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
11132         }
11133         
11134         if(this.hiddenField){
11135             this.hiddenField.dom.value = vv;
11136             
11137             this.lastSelectionText = dv;
11138             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11139             this.value = vv;
11140             return;
11141         }
11142         // no hidden field.. - we store the value in 'value', but still display
11143         // display field!!!!
11144         this.lastSelectionText = dv;
11145         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11146         this.value = vv;
11147         
11148         
11149     },
11150     // private
11151     reset : function(){
11152         // overridden so that last data is reset..
11153         this.setValue(this.originalValue);
11154         this.clearInvalid();
11155         this.lastData = false;
11156         if (this.view) {
11157             this.view.clearSelections();
11158         }
11159     },
11160     // private
11161     findRecord : function(prop, value){
11162         var record;
11163         if(this.store.getCount() > 0){
11164             this.store.each(function(r){
11165                 if(r.data[prop] == value){
11166                     record = r;
11167                     return false;
11168                 }
11169                 return true;
11170             });
11171         }
11172         return record;
11173     },
11174     
11175     getName: function()
11176     {
11177         // returns hidden if it's set..
11178         if (!this.rendered) {return ''};
11179         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
11180         
11181     },
11182     // private
11183     onViewMove : function(e, t){
11184         this.inKeyMode = false;
11185     },
11186
11187     // private
11188     onViewOver : function(e, t){
11189         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
11190             return;
11191         }
11192         var item = this.view.findItemFromChild(t);
11193         
11194         if(item){
11195             var index = this.view.indexOf(item);
11196             this.select(index, false);
11197         }
11198     },
11199
11200     // private
11201     onViewClick : function(view, doFocus, el, e)
11202     {
11203         var index = this.view.getSelectedIndexes()[0];
11204         
11205         var r = this.store.getAt(index);
11206         
11207         if(this.tickable){
11208             
11209             if(e.getTarget().nodeName.toLowerCase() != 'input'){
11210                 return;
11211             }
11212             
11213             var rm = false;
11214             var _this = this;
11215             
11216             Roo.each(this.tickItems, function(v,k){
11217                 
11218                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
11219                     _this.tickItems.splice(k, 1);
11220                     rm = true;
11221                     return;
11222                 }
11223             })
11224             
11225             if(rm){
11226                 return;
11227             }
11228             
11229             this.tickItems.push(r.data);
11230             return;
11231         }
11232         
11233         if(r){
11234             this.onSelect(r, index);
11235         }
11236         if(doFocus !== false && !this.blockFocus){
11237             this.inputEl().focus();
11238         }
11239     },
11240
11241     // private
11242     restrictHeight : function(){
11243         //this.innerList.dom.style.height = '';
11244         //var inner = this.innerList.dom;
11245         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
11246         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
11247         //this.list.beginUpdate();
11248         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
11249         this.list.alignTo(this.inputEl(), this.listAlign);
11250         this.list.alignTo(this.inputEl(), this.listAlign);
11251         //this.list.endUpdate();
11252     },
11253
11254     // private
11255     onEmptyResults : function(){
11256         this.collapse();
11257     },
11258
11259     /**
11260      * Returns true if the dropdown list is expanded, else false.
11261      */
11262     isExpanded : function(){
11263         return this.list.isVisible();
11264     },
11265
11266     /**
11267      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
11268      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11269      * @param {String} value The data value of the item to select
11270      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11271      * selected item if it is not currently in view (defaults to true)
11272      * @return {Boolean} True if the value matched an item in the list, else false
11273      */
11274     selectByValue : function(v, scrollIntoView){
11275         if(v !== undefined && v !== null){
11276             var r = this.findRecord(this.valueField || this.displayField, v);
11277             if(r){
11278                 this.select(this.store.indexOf(r), scrollIntoView);
11279                 return true;
11280             }
11281         }
11282         return false;
11283     },
11284
11285     /**
11286      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
11287      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11288      * @param {Number} index The zero-based index of the list item to select
11289      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11290      * selected item if it is not currently in view (defaults to true)
11291      */
11292     select : function(index, scrollIntoView){
11293         this.selectedIndex = index;
11294         this.view.select(index);
11295         if(scrollIntoView !== false){
11296             var el = this.view.getNode(index);
11297             if(el && !this.multiple && !this.tickable){
11298                 this.list.scrollChildIntoView(el, false);
11299             }
11300         }
11301     },
11302
11303     // private
11304     selectNext : function(){
11305         var ct = this.store.getCount();
11306         if(ct > 0){
11307             if(this.selectedIndex == -1){
11308                 this.select(0);
11309             }else if(this.selectedIndex < ct-1){
11310                 this.select(this.selectedIndex+1);
11311             }
11312         }
11313     },
11314
11315     // private
11316     selectPrev : function(){
11317         var ct = this.store.getCount();
11318         if(ct > 0){
11319             if(this.selectedIndex == -1){
11320                 this.select(0);
11321             }else if(this.selectedIndex != 0){
11322                 this.select(this.selectedIndex-1);
11323             }
11324         }
11325     },
11326
11327     // private
11328     onKeyUp : function(e){
11329         if(this.editable !== false && !e.isSpecialKey()){
11330             this.lastKey = e.getKey();
11331             this.dqTask.delay(this.queryDelay);
11332         }
11333     },
11334
11335     // private
11336     validateBlur : function(){
11337         return !this.list || !this.list.isVisible();   
11338     },
11339
11340     // private
11341     initQuery : function(){
11342         this.doQuery(this.getRawValue());
11343     },
11344
11345     // private
11346     doForce : function(){
11347         if(this.inputEl().dom.value.length > 0){
11348             this.inputEl().dom.value =
11349                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
11350              
11351         }
11352     },
11353
11354     /**
11355      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
11356      * query allowing the query action to be canceled if needed.
11357      * @param {String} query The SQL query to execute
11358      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
11359      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
11360      * saved in the current store (defaults to false)
11361      */
11362     doQuery : function(q, forceAll){
11363         
11364         if(q === undefined || q === null){
11365             q = '';
11366         }
11367         var qe = {
11368             query: q,
11369             forceAll: forceAll,
11370             combo: this,
11371             cancel:false
11372         };
11373         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
11374             return false;
11375         }
11376         q = qe.query;
11377         
11378         forceAll = qe.forceAll;
11379         if(forceAll === true || (q.length >= this.minChars)){
11380             
11381             this.hasQuery = true;
11382             
11383             if(this.lastQuery != q || this.alwaysQuery){
11384                 this.lastQuery = q;
11385                 if(this.mode == 'local'){
11386                     this.selectedIndex = -1;
11387                     if(forceAll){
11388                         this.store.clearFilter();
11389                     }else{
11390                         this.store.filter(this.displayField, q);
11391                     }
11392                     this.onLoad();
11393                 }else{
11394                     this.store.baseParams[this.queryParam] = q;
11395                     
11396                     var options = {params : this.getParams(q)};
11397                     
11398                     if(this.loadNext){
11399                         options.add = true;
11400                         options.params.start = this.page * this.pageSize;
11401                     }
11402                     
11403                     this.store.load(options);
11404                     /*
11405                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
11406                      *  we should expand the list on onLoad
11407                      *  so command out it
11408                      */
11409 //                    this.expand();
11410                 }
11411             }else{
11412                 this.selectedIndex = -1;
11413                 this.onLoad();   
11414             }
11415         }
11416         
11417         this.loadNext = false;
11418     },
11419
11420     // private
11421     getParams : function(q){
11422         var p = {};
11423         //p[this.queryParam] = q;
11424         
11425         if(this.pageSize){
11426             p.start = 0;
11427             p.limit = this.pageSize;
11428         }
11429         return p;
11430     },
11431
11432     /**
11433      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
11434      */
11435     collapse : function(){
11436         if(!this.isExpanded()){
11437             return;
11438         }
11439         
11440         this.hasFocus = false;
11441         
11442         this.list.hide();
11443         
11444         if(this.tickable){
11445             this.okBtn.hide();
11446             this.cancelBtn.hide();
11447             this.trigger.show();
11448         }
11449         
11450         Roo.get(document).un('mousedown', this.collapseIf, this);
11451         Roo.get(document).un('mousewheel', this.collapseIf, this);
11452         if (!this.editable) {
11453             Roo.get(document).un('keydown', this.listKeyPress, this);
11454         }
11455         this.fireEvent('collapse', this);
11456     },
11457
11458     // private
11459     collapseIf : function(e){
11460         var in_combo  = e.within(this.el);
11461         var in_list =  e.within(this.list);
11462         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
11463         
11464         if (in_combo || in_list || is_list) {
11465             //e.stopPropagation();
11466             return;
11467         }
11468         
11469         if(this.tickable){
11470             this.onTickableFooterButtonClick(e, false, false);
11471         }
11472
11473         this.collapse();
11474         
11475     },
11476
11477     /**
11478      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
11479      */
11480     expand : function(){
11481        
11482         if(this.isExpanded() || !this.hasFocus){
11483             return;
11484         }
11485          Roo.log('expand');
11486         
11487         this.list.show();
11488         
11489         this.restrictHeight();
11490         
11491         if(this.tickable){
11492             
11493             this.tickItems = Roo.apply([], this.item);
11494             
11495             this.okBtn.show();
11496             this.cancelBtn.show();
11497             this.trigger.hide();
11498             
11499         }
11500         
11501         Roo.get(document).on('mousedown', this.collapseIf, this);
11502         Roo.get(document).on('mousewheel', this.collapseIf, this);
11503         if (!this.editable) {
11504             Roo.get(document).on('keydown', this.listKeyPress, this);
11505         }
11506         
11507         this.fireEvent('expand', this);
11508     },
11509
11510     // private
11511     // Implements the default empty TriggerField.onTriggerClick function
11512     onTriggerClick : function(e)
11513     {
11514         Roo.log('trigger click');
11515         
11516         if(this.disabled || !this.triggerList){
11517             return;
11518         }
11519         
11520         this.page = 0;
11521         this.loadNext = false;
11522         
11523         if(this.isExpanded()){
11524             this.collapse();
11525             if (!this.blockFocus) {
11526                 this.inputEl().focus();
11527             }
11528             
11529         }else {
11530             this.hasFocus = true;
11531             if(this.triggerAction == 'all') {
11532                 this.doQuery(this.allQuery, true);
11533             } else {
11534                 this.doQuery(this.getRawValue());
11535             }
11536             if (!this.blockFocus) {
11537                 this.inputEl().focus();
11538             }
11539         }
11540     },
11541     
11542     onTickableTriggerClick : function(e)
11543     {
11544         if(this.disabled){
11545             return;
11546         }
11547         
11548         this.page = 0;
11549         this.loadNext = false;
11550         this.hasFocus = true;
11551         
11552         if(this.triggerAction == 'all') {
11553             this.doQuery(this.allQuery, true);
11554         } else {
11555             this.doQuery(this.getRawValue());
11556         }
11557     },
11558     
11559     onSearchFieldClick : function(e)
11560     {
11561         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
11562             return;
11563         }
11564         
11565         this.page = 0;
11566         this.loadNext = false;
11567         this.hasFocus = true;
11568         
11569         if(this.triggerAction == 'all') {
11570             this.doQuery(this.allQuery, true);
11571         } else {
11572             this.doQuery(this.getRawValue());
11573         }
11574     },
11575     
11576     listKeyPress : function(e)
11577     {
11578         //Roo.log('listkeypress');
11579         // scroll to first matching element based on key pres..
11580         if (e.isSpecialKey()) {
11581             return false;
11582         }
11583         var k = String.fromCharCode(e.getKey()).toUpperCase();
11584         //Roo.log(k);
11585         var match  = false;
11586         var csel = this.view.getSelectedNodes();
11587         var cselitem = false;
11588         if (csel.length) {
11589             var ix = this.view.indexOf(csel[0]);
11590             cselitem  = this.store.getAt(ix);
11591             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
11592                 cselitem = false;
11593             }
11594             
11595         }
11596         
11597         this.store.each(function(v) { 
11598             if (cselitem) {
11599                 // start at existing selection.
11600                 if (cselitem.id == v.id) {
11601                     cselitem = false;
11602                 }
11603                 return true;
11604             }
11605                 
11606             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
11607                 match = this.store.indexOf(v);
11608                 return false;
11609             }
11610             return true;
11611         }, this);
11612         
11613         if (match === false) {
11614             return true; // no more action?
11615         }
11616         // scroll to?
11617         this.view.select(match);
11618         var sn = Roo.get(this.view.getSelectedNodes()[0])
11619         //sn.scrollIntoView(sn.dom.parentNode, false);
11620     },
11621     
11622     onViewScroll : function(e, t){
11623         
11624         if(this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
11625             return;
11626         }
11627         
11628         this.hasQuery = true;
11629         
11630         this.loading = this.list.select('.loading', true).first();
11631         
11632         if(this.loading === null){
11633             this.list.createChild({
11634                 tag: 'div',
11635                 cls: 'loading select2-more-results select2-active',
11636                 html: 'Loading more results...'
11637             })
11638             
11639             this.loading = this.list.select('.loading', true).first();
11640             
11641             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
11642             
11643             this.loading.hide();
11644         }
11645         
11646         this.loading.show();
11647         
11648         var _combo = this;
11649         
11650         this.page++;
11651         this.loadNext = true;
11652         
11653         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
11654         
11655         return;
11656     },
11657     
11658     addItem : function(o)
11659     {   
11660         var dv = ''; // display value
11661         
11662         if (this.displayField) {
11663             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11664         } else {
11665             // this is an error condition!!!
11666             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
11667         }
11668         
11669         if(!dv.length){
11670             return;
11671         }
11672         
11673         var choice = this.choices.createChild({
11674             tag: 'li',
11675             cls: 'select2-search-choice',
11676             cn: [
11677                 {
11678                     tag: 'div',
11679                     html: dv
11680                 },
11681                 {
11682                     tag: 'a',
11683                     href: '#',
11684                     cls: 'select2-search-choice-close',
11685                     tabindex: '-1'
11686                 }
11687             ]
11688             
11689         }, this.searchField);
11690         
11691         var close = choice.select('a.select2-search-choice-close', true).first()
11692         
11693         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
11694         
11695         this.item.push(o);
11696         
11697         this.lastData = o;
11698         
11699         this.syncValue();
11700         
11701         this.inputEl().dom.value = '';
11702         
11703     },
11704     
11705     onRemoveItem : function(e, _self, o)
11706     {
11707         e.preventDefault();
11708         var index = this.item.indexOf(o.data) * 1;
11709         
11710         if( index < 0){
11711             Roo.log('not this item?!');
11712             return;
11713         }
11714         
11715         this.item.splice(index, 1);
11716         o.item.remove();
11717         
11718         this.syncValue();
11719         
11720         this.fireEvent('remove', this, e);
11721         
11722     },
11723     
11724     syncValue : function()
11725     {
11726         if(!this.item.length){
11727             this.clearValue();
11728             return;
11729         }
11730             
11731         var value = [];
11732         var _this = this;
11733         Roo.each(this.item, function(i){
11734             if(_this.valueField){
11735                 value.push(i[_this.valueField]);
11736                 return;
11737             }
11738
11739             value.push(i);
11740         });
11741
11742         this.value = value.join(',');
11743
11744         if(this.hiddenField){
11745             this.hiddenField.dom.value = this.value;
11746         }
11747     },
11748     
11749     clearItem : function()
11750     {
11751         if(!this.multiple){
11752             return;
11753         }
11754         
11755         this.item = [];
11756         
11757         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
11758            c.remove();
11759         });
11760         
11761         this.syncValue();
11762     },
11763     
11764     inputEl: function ()
11765     {
11766         if(this.tickable){
11767             return this.searchField;
11768         }
11769         return this.el.select('input.form-control',true).first();
11770     },
11771     
11772     
11773     onTickableFooterButtonClick : function(e, btn, el)
11774     {
11775         e.preventDefault();
11776         
11777         if(btn && btn.name == 'cancel'){
11778             this.tickItems = Roo.apply([], this.item);
11779             this.collapse();
11780             return;
11781         }
11782         
11783         this.clearItem();
11784         
11785         var _this = this;
11786         
11787         Roo.each(this.tickItems, function(o){
11788             _this.addItem(o);
11789         });
11790         
11791         this.collapse();
11792         
11793     }
11794     
11795     
11796
11797     /** 
11798     * @cfg {Boolean} grow 
11799     * @hide 
11800     */
11801     /** 
11802     * @cfg {Number} growMin 
11803     * @hide 
11804     */
11805     /** 
11806     * @cfg {Number} growMax 
11807     * @hide 
11808     */
11809     /**
11810      * @hide
11811      * @method autoSize
11812      */
11813 });
11814 /*
11815  * Based on:
11816  * Ext JS Library 1.1.1
11817  * Copyright(c) 2006-2007, Ext JS, LLC.
11818  *
11819  * Originally Released Under LGPL - original licence link has changed is not relivant.
11820  *
11821  * Fork - LGPL
11822  * <script type="text/javascript">
11823  */
11824
11825 /**
11826  * @class Roo.View
11827  * @extends Roo.util.Observable
11828  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
11829  * This class also supports single and multi selection modes. <br>
11830  * Create a data model bound view:
11831  <pre><code>
11832  var store = new Roo.data.Store(...);
11833
11834  var view = new Roo.View({
11835     el : "my-element",
11836     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
11837  
11838     singleSelect: true,
11839     selectedClass: "ydataview-selected",
11840     store: store
11841  });
11842
11843  // listen for node click?
11844  view.on("click", function(vw, index, node, e){
11845  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
11846  });
11847
11848  // load XML data
11849  dataModel.load("foobar.xml");
11850  </code></pre>
11851  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
11852  * <br><br>
11853  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
11854  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
11855  * 
11856  * Note: old style constructor is still suported (container, template, config)
11857  * 
11858  * @constructor
11859  * Create a new View
11860  * @param {Object} config The config object
11861  * 
11862  */
11863 Roo.View = function(config, depreciated_tpl, depreciated_config){
11864     
11865     this.parent = false;
11866     
11867     if (typeof(depreciated_tpl) == 'undefined') {
11868         // new way.. - universal constructor.
11869         Roo.apply(this, config);
11870         this.el  = Roo.get(this.el);
11871     } else {
11872         // old format..
11873         this.el  = Roo.get(config);
11874         this.tpl = depreciated_tpl;
11875         Roo.apply(this, depreciated_config);
11876     }
11877     this.wrapEl  = this.el.wrap().wrap();
11878     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
11879     
11880     
11881     if(typeof(this.tpl) == "string"){
11882         this.tpl = new Roo.Template(this.tpl);
11883     } else {
11884         // support xtype ctors..
11885         this.tpl = new Roo.factory(this.tpl, Roo);
11886     }
11887     
11888     
11889     this.tpl.compile();
11890     
11891     /** @private */
11892     this.addEvents({
11893         /**
11894          * @event beforeclick
11895          * Fires before a click is processed. Returns false to cancel the default action.
11896          * @param {Roo.View} this
11897          * @param {Number} index The index of the target node
11898          * @param {HTMLElement} node The target node
11899          * @param {Roo.EventObject} e The raw event object
11900          */
11901             "beforeclick" : true,
11902         /**
11903          * @event click
11904          * Fires when a template node is clicked.
11905          * @param {Roo.View} this
11906          * @param {Number} index The index of the target node
11907          * @param {HTMLElement} node The target node
11908          * @param {Roo.EventObject} e The raw event object
11909          */
11910             "click" : true,
11911         /**
11912          * @event dblclick
11913          * Fires when a template node is double clicked.
11914          * @param {Roo.View} this
11915          * @param {Number} index The index of the target node
11916          * @param {HTMLElement} node The target node
11917          * @param {Roo.EventObject} e The raw event object
11918          */
11919             "dblclick" : true,
11920         /**
11921          * @event contextmenu
11922          * Fires when a template node is right clicked.
11923          * @param {Roo.View} this
11924          * @param {Number} index The index of the target node
11925          * @param {HTMLElement} node The target node
11926          * @param {Roo.EventObject} e The raw event object
11927          */
11928             "contextmenu" : true,
11929         /**
11930          * @event selectionchange
11931          * Fires when the selected nodes change.
11932          * @param {Roo.View} this
11933          * @param {Array} selections Array of the selected nodes
11934          */
11935             "selectionchange" : true,
11936     
11937         /**
11938          * @event beforeselect
11939          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
11940          * @param {Roo.View} this
11941          * @param {HTMLElement} node The node to be selected
11942          * @param {Array} selections Array of currently selected nodes
11943          */
11944             "beforeselect" : true,
11945         /**
11946          * @event preparedata
11947          * Fires on every row to render, to allow you to change the data.
11948          * @param {Roo.View} this
11949          * @param {Object} data to be rendered (change this)
11950          */
11951           "preparedata" : true
11952           
11953           
11954         });
11955
11956
11957
11958     this.el.on({
11959         "click": this.onClick,
11960         "dblclick": this.onDblClick,
11961         "contextmenu": this.onContextMenu,
11962         scope:this
11963     });
11964
11965     this.selections = [];
11966     this.nodes = [];
11967     this.cmp = new Roo.CompositeElementLite([]);
11968     if(this.store){
11969         this.store = Roo.factory(this.store, Roo.data);
11970         this.setStore(this.store, true);
11971     }
11972     
11973     if ( this.footer && this.footer.xtype) {
11974            
11975          var fctr = this.wrapEl.appendChild(document.createElement("div"));
11976         
11977         this.footer.dataSource = this.store
11978         this.footer.container = fctr;
11979         this.footer = Roo.factory(this.footer, Roo);
11980         fctr.insertFirst(this.el);
11981         
11982         // this is a bit insane - as the paging toolbar seems to detach the el..
11983 //        dom.parentNode.parentNode.parentNode
11984          // they get detached?
11985     }
11986     
11987     
11988     Roo.View.superclass.constructor.call(this);
11989     
11990     
11991 };
11992
11993 Roo.extend(Roo.View, Roo.util.Observable, {
11994     
11995      /**
11996      * @cfg {Roo.data.Store} store Data store to load data from.
11997      */
11998     store : false,
11999     
12000     /**
12001      * @cfg {String|Roo.Element} el The container element.
12002      */
12003     el : '',
12004     
12005     /**
12006      * @cfg {String|Roo.Template} tpl The template used by this View 
12007      */
12008     tpl : false,
12009     /**
12010      * @cfg {String} dataName the named area of the template to use as the data area
12011      *                          Works with domtemplates roo-name="name"
12012      */
12013     dataName: false,
12014     /**
12015      * @cfg {String} selectedClass The css class to add to selected nodes
12016      */
12017     selectedClass : "x-view-selected",
12018      /**
12019      * @cfg {String} emptyText The empty text to show when nothing is loaded.
12020      */
12021     emptyText : "",
12022     
12023     /**
12024      * @cfg {String} text to display on mask (default Loading)
12025      */
12026     mask : false,
12027     /**
12028      * @cfg {Boolean} multiSelect Allow multiple selection
12029      */
12030     multiSelect : false,
12031     /**
12032      * @cfg {Boolean} singleSelect Allow single selection
12033      */
12034     singleSelect:  false,
12035     
12036     /**
12037      * @cfg {Boolean} toggleSelect - selecting 
12038      */
12039     toggleSelect : false,
12040     
12041     /**
12042      * @cfg {Boolean} tickable - selecting 
12043      */
12044     tickable : false,
12045     
12046     /**
12047      * Returns the element this view is bound to.
12048      * @return {Roo.Element}
12049      */
12050     getEl : function(){
12051         return this.wrapEl;
12052     },
12053     
12054     
12055
12056     /**
12057      * Refreshes the view. - called by datachanged on the store. - do not call directly.
12058      */
12059     refresh : function(){
12060         Roo.log('refresh');
12061         var t = this.tpl;
12062         
12063         // if we are using something like 'domtemplate', then
12064         // the what gets used is:
12065         // t.applySubtemplate(NAME, data, wrapping data..)
12066         // the outer template then get' applied with
12067         //     the store 'extra data'
12068         // and the body get's added to the
12069         //      roo-name="data" node?
12070         //      <span class='roo-tpl-{name}'></span> ?????
12071         
12072         
12073         
12074         this.clearSelections();
12075         this.el.update("");
12076         var html = [];
12077         var records = this.store.getRange();
12078         if(records.length < 1) {
12079             
12080             // is this valid??  = should it render a template??
12081             
12082             this.el.update(this.emptyText);
12083             return;
12084         }
12085         var el = this.el;
12086         if (this.dataName) {
12087             this.el.update(t.apply(this.store.meta)); //????
12088             el = this.el.child('.roo-tpl-' + this.dataName);
12089         }
12090         
12091         for(var i = 0, len = records.length; i < len; i++){
12092             var data = this.prepareData(records[i].data, i, records[i]);
12093             this.fireEvent("preparedata", this, data, i, records[i]);
12094             
12095             var d = Roo.apply({}, data);
12096             
12097             if(this.tickable){
12098                 Roo.apply(d, {'roo-id' : Roo.id()});
12099                 
12100                 var _this = this;
12101             
12102                 Roo.each(this.parent.item, function(item){
12103                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
12104                         return;
12105                     }
12106                     Roo.apply(d, {'roo-data-checked' : 'checked'});
12107                 });
12108             }
12109             
12110             html[html.length] = Roo.util.Format.trim(
12111                 this.dataName ?
12112                     t.applySubtemplate(this.dataName, d, this.store.meta) :
12113                     t.apply(d)
12114             );
12115         }
12116         
12117         
12118         
12119         el.update(html.join(""));
12120         this.nodes = el.dom.childNodes;
12121         this.updateIndexes(0);
12122     },
12123     
12124
12125     /**
12126      * Function to override to reformat the data that is sent to
12127      * the template for each node.
12128      * DEPRICATED - use the preparedata event handler.
12129      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
12130      * a JSON object for an UpdateManager bound view).
12131      */
12132     prepareData : function(data, index, record)
12133     {
12134         this.fireEvent("preparedata", this, data, index, record);
12135         return data;
12136     },
12137
12138     onUpdate : function(ds, record){
12139          Roo.log('on update');   
12140         this.clearSelections();
12141         var index = this.store.indexOf(record);
12142         var n = this.nodes[index];
12143         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
12144         n.parentNode.removeChild(n);
12145         this.updateIndexes(index, index);
12146     },
12147
12148     
12149     
12150 // --------- FIXME     
12151     onAdd : function(ds, records, index)
12152     {
12153         Roo.log(['on Add', ds, records, index] );        
12154         this.clearSelections();
12155         if(this.nodes.length == 0){
12156             this.refresh();
12157             return;
12158         }
12159         var n = this.nodes[index];
12160         for(var i = 0, len = records.length; i < len; i++){
12161             var d = this.prepareData(records[i].data, i, records[i]);
12162             if(n){
12163                 this.tpl.insertBefore(n, d);
12164             }else{
12165                 
12166                 this.tpl.append(this.el, d);
12167             }
12168         }
12169         this.updateIndexes(index);
12170     },
12171
12172     onRemove : function(ds, record, index){
12173         Roo.log('onRemove');
12174         this.clearSelections();
12175         var el = this.dataName  ?
12176             this.el.child('.roo-tpl-' + this.dataName) :
12177             this.el; 
12178         
12179         el.dom.removeChild(this.nodes[index]);
12180         this.updateIndexes(index);
12181     },
12182
12183     /**
12184      * Refresh an individual node.
12185      * @param {Number} index
12186      */
12187     refreshNode : function(index){
12188         this.onUpdate(this.store, this.store.getAt(index));
12189     },
12190
12191     updateIndexes : function(startIndex, endIndex){
12192         var ns = this.nodes;
12193         startIndex = startIndex || 0;
12194         endIndex = endIndex || ns.length - 1;
12195         for(var i = startIndex; i <= endIndex; i++){
12196             ns[i].nodeIndex = i;
12197         }
12198     },
12199
12200     /**
12201      * Changes the data store this view uses and refresh the view.
12202      * @param {Store} store
12203      */
12204     setStore : function(store, initial){
12205         if(!initial && this.store){
12206             this.store.un("datachanged", this.refresh);
12207             this.store.un("add", this.onAdd);
12208             this.store.un("remove", this.onRemove);
12209             this.store.un("update", this.onUpdate);
12210             this.store.un("clear", this.refresh);
12211             this.store.un("beforeload", this.onBeforeLoad);
12212             this.store.un("load", this.onLoad);
12213             this.store.un("loadexception", this.onLoad);
12214         }
12215         if(store){
12216           
12217             store.on("datachanged", this.refresh, this);
12218             store.on("add", this.onAdd, this);
12219             store.on("remove", this.onRemove, this);
12220             store.on("update", this.onUpdate, this);
12221             store.on("clear", this.refresh, this);
12222             store.on("beforeload", this.onBeforeLoad, this);
12223             store.on("load", this.onLoad, this);
12224             store.on("loadexception", this.onLoad, this);
12225         }
12226         
12227         if(store){
12228             this.refresh();
12229         }
12230     },
12231     /**
12232      * onbeforeLoad - masks the loading area.
12233      *
12234      */
12235     onBeforeLoad : function(store,opts)
12236     {
12237          Roo.log('onBeforeLoad');   
12238         if (!opts.add) {
12239             this.el.update("");
12240         }
12241         this.el.mask(this.mask ? this.mask : "Loading" ); 
12242     },
12243     onLoad : function ()
12244     {
12245         this.el.unmask();
12246     },
12247     
12248
12249     /**
12250      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
12251      * @param {HTMLElement} node
12252      * @return {HTMLElement} The template node
12253      */
12254     findItemFromChild : function(node){
12255         var el = this.dataName  ?
12256             this.el.child('.roo-tpl-' + this.dataName,true) :
12257             this.el.dom; 
12258         
12259         if(!node || node.parentNode == el){
12260                     return node;
12261             }
12262             var p = node.parentNode;
12263             while(p && p != el){
12264             if(p.parentNode == el){
12265                 return p;
12266             }
12267             p = p.parentNode;
12268         }
12269             return null;
12270     },
12271
12272     /** @ignore */
12273     onClick : function(e){
12274         var item = this.findItemFromChild(e.getTarget());
12275         if(item){
12276             var index = this.indexOf(item);
12277             if(this.onItemClick(item, index, e) !== false){
12278                 this.fireEvent("click", this, index, item, e);
12279             }
12280         }else{
12281             this.clearSelections();
12282         }
12283     },
12284
12285     /** @ignore */
12286     onContextMenu : function(e){
12287         var item = this.findItemFromChild(e.getTarget());
12288         if(item){
12289             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
12290         }
12291     },
12292
12293     /** @ignore */
12294     onDblClick : function(e){
12295         var item = this.findItemFromChild(e.getTarget());
12296         if(item){
12297             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
12298         }
12299     },
12300
12301     onItemClick : function(item, index, e)
12302     {
12303         if(this.fireEvent("beforeclick", this, index, item, e) === false){
12304             return false;
12305         }
12306         if (this.toggleSelect) {
12307             var m = this.isSelected(item) ? 'unselect' : 'select';
12308             Roo.log(m);
12309             var _t = this;
12310             _t[m](item, true, false);
12311             return true;
12312         }
12313         if(this.multiSelect || this.singleSelect){
12314             if(this.multiSelect && e.shiftKey && this.lastSelection){
12315                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
12316             }else{
12317                 this.select(item, this.multiSelect && e.ctrlKey);
12318                 this.lastSelection = item;
12319             }
12320             
12321             if(!this.tickable){
12322                 e.preventDefault();
12323             }
12324             
12325         }
12326         return true;
12327     },
12328
12329     /**
12330      * Get the number of selected nodes.
12331      * @return {Number}
12332      */
12333     getSelectionCount : function(){
12334         return this.selections.length;
12335     },
12336
12337     /**
12338      * Get the currently selected nodes.
12339      * @return {Array} An array of HTMLElements
12340      */
12341     getSelectedNodes : function(){
12342         return this.selections;
12343     },
12344
12345     /**
12346      * Get the indexes of the selected nodes.
12347      * @return {Array}
12348      */
12349     getSelectedIndexes : function(){
12350         var indexes = [], s = this.selections;
12351         for(var i = 0, len = s.length; i < len; i++){
12352             indexes.push(s[i].nodeIndex);
12353         }
12354         return indexes;
12355     },
12356
12357     /**
12358      * Clear all selections
12359      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
12360      */
12361     clearSelections : function(suppressEvent){
12362         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
12363             this.cmp.elements = this.selections;
12364             this.cmp.removeClass(this.selectedClass);
12365             this.selections = [];
12366             if(!suppressEvent){
12367                 this.fireEvent("selectionchange", this, this.selections);
12368             }
12369         }
12370     },
12371
12372     /**
12373      * Returns true if the passed node is selected
12374      * @param {HTMLElement/Number} node The node or node index
12375      * @return {Boolean}
12376      */
12377     isSelected : function(node){
12378         var s = this.selections;
12379         if(s.length < 1){
12380             return false;
12381         }
12382         node = this.getNode(node);
12383         return s.indexOf(node) !== -1;
12384     },
12385
12386     /**
12387      * Selects nodes.
12388      * @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
12389      * @param {Boolean} keepExisting (optional) true to keep existing selections
12390      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12391      */
12392     select : function(nodeInfo, keepExisting, suppressEvent){
12393         if(nodeInfo instanceof Array){
12394             if(!keepExisting){
12395                 this.clearSelections(true);
12396             }
12397             for(var i = 0, len = nodeInfo.length; i < len; i++){
12398                 this.select(nodeInfo[i], true, true);
12399             }
12400             return;
12401         } 
12402         var node = this.getNode(nodeInfo);
12403         if(!node || this.isSelected(node)){
12404             return; // already selected.
12405         }
12406         if(!keepExisting){
12407             this.clearSelections(true);
12408         }
12409         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
12410             Roo.fly(node).addClass(this.selectedClass);
12411             this.selections.push(node);
12412             if(!suppressEvent){
12413                 this.fireEvent("selectionchange", this, this.selections);
12414             }
12415         }
12416         
12417         
12418     },
12419       /**
12420      * Unselects nodes.
12421      * @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
12422      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
12423      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12424      */
12425     unselect : function(nodeInfo, keepExisting, suppressEvent)
12426     {
12427         if(nodeInfo instanceof Array){
12428             Roo.each(this.selections, function(s) {
12429                 this.unselect(s, nodeInfo);
12430             }, this);
12431             return;
12432         }
12433         var node = this.getNode(nodeInfo);
12434         if(!node || !this.isSelected(node)){
12435             Roo.log("not selected");
12436             return; // not selected.
12437         }
12438         // fireevent???
12439         var ns = [];
12440         Roo.each(this.selections, function(s) {
12441             if (s == node ) {
12442                 Roo.fly(node).removeClass(this.selectedClass);
12443
12444                 return;
12445             }
12446             ns.push(s);
12447         },this);
12448         
12449         this.selections= ns;
12450         this.fireEvent("selectionchange", this, this.selections);
12451     },
12452
12453     /**
12454      * Gets a template node.
12455      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12456      * @return {HTMLElement} The node or null if it wasn't found
12457      */
12458     getNode : function(nodeInfo){
12459         if(typeof nodeInfo == "string"){
12460             return document.getElementById(nodeInfo);
12461         }else if(typeof nodeInfo == "number"){
12462             return this.nodes[nodeInfo];
12463         }
12464         return nodeInfo;
12465     },
12466
12467     /**
12468      * Gets a range template nodes.
12469      * @param {Number} startIndex
12470      * @param {Number} endIndex
12471      * @return {Array} An array of nodes
12472      */
12473     getNodes : function(start, end){
12474         var ns = this.nodes;
12475         start = start || 0;
12476         end = typeof end == "undefined" ? ns.length - 1 : end;
12477         var nodes = [];
12478         if(start <= end){
12479             for(var i = start; i <= end; i++){
12480                 nodes.push(ns[i]);
12481             }
12482         } else{
12483             for(var i = start; i >= end; i--){
12484                 nodes.push(ns[i]);
12485             }
12486         }
12487         return nodes;
12488     },
12489
12490     /**
12491      * Finds the index of the passed node
12492      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12493      * @return {Number} The index of the node or -1
12494      */
12495     indexOf : function(node){
12496         node = this.getNode(node);
12497         if(typeof node.nodeIndex == "number"){
12498             return node.nodeIndex;
12499         }
12500         var ns = this.nodes;
12501         for(var i = 0, len = ns.length; i < len; i++){
12502             if(ns[i] == node){
12503                 return i;
12504             }
12505         }
12506         return -1;
12507     }
12508 });
12509 /*
12510  * - LGPL
12511  *
12512  * based on jquery fullcalendar
12513  * 
12514  */
12515
12516 Roo.bootstrap = Roo.bootstrap || {};
12517 /**
12518  * @class Roo.bootstrap.Calendar
12519  * @extends Roo.bootstrap.Component
12520  * Bootstrap Calendar class
12521  * @cfg {Boolean} loadMask (true|false) default false
12522  * @cfg {Object} header generate the user specific header of the calendar, default false
12523
12524  * @constructor
12525  * Create a new Container
12526  * @param {Object} config The config object
12527  */
12528
12529
12530
12531 Roo.bootstrap.Calendar = function(config){
12532     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
12533      this.addEvents({
12534         /**
12535              * @event select
12536              * Fires when a date is selected
12537              * @param {DatePicker} this
12538              * @param {Date} date The selected date
12539              */
12540         'select': true,
12541         /**
12542              * @event monthchange
12543              * Fires when the displayed month changes 
12544              * @param {DatePicker} this
12545              * @param {Date} date The selected month
12546              */
12547         'monthchange': true,
12548         /**
12549              * @event evententer
12550              * Fires when mouse over an event
12551              * @param {Calendar} this
12552              * @param {event} Event
12553              */
12554         'evententer': true,
12555         /**
12556              * @event eventleave
12557              * Fires when the mouse leaves an
12558              * @param {Calendar} this
12559              * @param {event}
12560              */
12561         'eventleave': true,
12562         /**
12563              * @event eventclick
12564              * Fires when the mouse click an
12565              * @param {Calendar} this
12566              * @param {event}
12567              */
12568         'eventclick': true
12569         
12570     });
12571
12572 };
12573
12574 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
12575     
12576      /**
12577      * @cfg {Number} startDay
12578      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
12579      */
12580     startDay : 0,
12581     
12582     loadMask : false,
12583     
12584     header : false,
12585       
12586     getAutoCreate : function(){
12587         
12588         
12589         var fc_button = function(name, corner, style, content ) {
12590             return Roo.apply({},{
12591                 tag : 'span',
12592                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
12593                          (corner.length ?
12594                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
12595                             ''
12596                         ),
12597                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
12598                 unselectable: 'on'
12599             });
12600         };
12601         
12602         var header = {};
12603         
12604         if(!this.header){
12605             header = {
12606                 tag : 'table',
12607                 cls : 'fc-header',
12608                 style : 'width:100%',
12609                 cn : [
12610                     {
12611                         tag: 'tr',
12612                         cn : [
12613                             {
12614                                 tag : 'td',
12615                                 cls : 'fc-header-left',
12616                                 cn : [
12617                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
12618                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
12619                                     { tag: 'span', cls: 'fc-header-space' },
12620                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
12621
12622
12623                                 ]
12624                             },
12625
12626                             {
12627                                 tag : 'td',
12628                                 cls : 'fc-header-center',
12629                                 cn : [
12630                                     {
12631                                         tag: 'span',
12632                                         cls: 'fc-header-title',
12633                                         cn : {
12634                                             tag: 'H2',
12635                                             html : 'month / year'
12636                                         }
12637                                     }
12638
12639                                 ]
12640                             },
12641                             {
12642                                 tag : 'td',
12643                                 cls : 'fc-header-right',
12644                                 cn : [
12645                               /*      fc_button('month', 'left', '', 'month' ),
12646                                     fc_button('week', '', '', 'week' ),
12647                                     fc_button('day', 'right', '', 'day' )
12648                                 */    
12649
12650                                 ]
12651                             }
12652
12653                         ]
12654                     }
12655                 ]
12656             };
12657         }
12658         
12659         header = this.header;
12660         
12661        
12662         var cal_heads = function() {
12663             var ret = [];
12664             // fixme - handle this.
12665             
12666             for (var i =0; i < Date.dayNames.length; i++) {
12667                 var d = Date.dayNames[i];
12668                 ret.push({
12669                     tag: 'th',
12670                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
12671                     html : d.substring(0,3)
12672                 });
12673                 
12674             }
12675             ret[0].cls += ' fc-first';
12676             ret[6].cls += ' fc-last';
12677             return ret;
12678         };
12679         var cal_cell = function(n) {
12680             return  {
12681                 tag: 'td',
12682                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
12683                 cn : [
12684                     {
12685                         cn : [
12686                             {
12687                                 cls: 'fc-day-number',
12688                                 html: 'D'
12689                             },
12690                             {
12691                                 cls: 'fc-day-content',
12692                              
12693                                 cn : [
12694                                      {
12695                                         style: 'position: relative;' // height: 17px;
12696                                     }
12697                                 ]
12698                             }
12699                             
12700                             
12701                         ]
12702                     }
12703                 ]
12704                 
12705             }
12706         };
12707         var cal_rows = function() {
12708             
12709             var ret = []
12710             for (var r = 0; r < 6; r++) {
12711                 var row= {
12712                     tag : 'tr',
12713                     cls : 'fc-week',
12714                     cn : []
12715                 };
12716                 
12717                 for (var i =0; i < Date.dayNames.length; i++) {
12718                     var d = Date.dayNames[i];
12719                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
12720
12721                 }
12722                 row.cn[0].cls+=' fc-first';
12723                 row.cn[0].cn[0].style = 'min-height:90px';
12724                 row.cn[6].cls+=' fc-last';
12725                 ret.push(row);
12726                 
12727             }
12728             ret[0].cls += ' fc-first';
12729             ret[4].cls += ' fc-prev-last';
12730             ret[5].cls += ' fc-last';
12731             return ret;
12732             
12733         };
12734         
12735         var cal_table = {
12736             tag: 'table',
12737             cls: 'fc-border-separate',
12738             style : 'width:100%',
12739             cellspacing  : 0,
12740             cn : [
12741                 { 
12742                     tag: 'thead',
12743                     cn : [
12744                         { 
12745                             tag: 'tr',
12746                             cls : 'fc-first fc-last',
12747                             cn : cal_heads()
12748                         }
12749                     ]
12750                 },
12751                 { 
12752                     tag: 'tbody',
12753                     cn : cal_rows()
12754                 }
12755                   
12756             ]
12757         };
12758          
12759          var cfg = {
12760             cls : 'fc fc-ltr',
12761             cn : [
12762                 header,
12763                 {
12764                     cls : 'fc-content',
12765                     style : "position: relative;",
12766                     cn : [
12767                         {
12768                             cls : 'fc-view fc-view-month fc-grid',
12769                             style : 'position: relative',
12770                             unselectable : 'on',
12771                             cn : [
12772                                 {
12773                                     cls : 'fc-event-container',
12774                                     style : 'position:absolute;z-index:8;top:0;left:0;'
12775                                 },
12776                                 cal_table
12777                             ]
12778                         }
12779                     ]
12780     
12781                 }
12782            ] 
12783             
12784         };
12785         
12786          
12787         
12788         return cfg;
12789     },
12790     
12791     
12792     initEvents : function()
12793     {
12794         if(!this.store){
12795             throw "can not find store for calendar";
12796         }
12797         
12798         var mark = {
12799             tag: "div",
12800             cls:"x-dlg-mask",
12801             style: "text-align:center",
12802             cn: [
12803                 {
12804                     tag: "div",
12805                     style: "background-color:white;width:50%;margin:250 auto",
12806                     cn: [
12807                         {
12808                             tag: "img",
12809                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
12810                         },
12811                         {
12812                             tag: "span",
12813                             html: "Loading"
12814                         }
12815                         
12816                     ]
12817                 }
12818             ]
12819         }
12820         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
12821         
12822         var size = this.el.select('.fc-content', true).first().getSize();
12823         this.maskEl.setSize(size.width, size.height);
12824         this.maskEl.enableDisplayMode("block");
12825         if(!this.loadMask){
12826             this.maskEl.hide();
12827         }
12828         
12829         this.store = Roo.factory(this.store, Roo.data);
12830         this.store.on('load', this.onLoad, this);
12831         this.store.on('beforeload', this.onBeforeLoad, this);
12832         
12833         this.resize();
12834         
12835         this.cells = this.el.select('.fc-day',true);
12836         //Roo.log(this.cells);
12837         this.textNodes = this.el.query('.fc-day-number');
12838         this.cells.addClassOnOver('fc-state-hover');
12839         
12840         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
12841         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
12842         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
12843         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
12844         
12845         this.on('monthchange', this.onMonthChange, this);
12846         
12847         this.update(new Date().clearTime());
12848     },
12849     
12850     resize : function() {
12851         var sz  = this.el.getSize();
12852         
12853         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
12854         this.el.select('.fc-day-content div',true).setHeight(34);
12855     },
12856     
12857     
12858     // private
12859     showPrevMonth : function(e){
12860         this.update(this.activeDate.add("mo", -1));
12861     },
12862     showToday : function(e){
12863         this.update(new Date().clearTime());
12864     },
12865     // private
12866     showNextMonth : function(e){
12867         this.update(this.activeDate.add("mo", 1));
12868     },
12869
12870     // private
12871     showPrevYear : function(){
12872         this.update(this.activeDate.add("y", -1));
12873     },
12874
12875     // private
12876     showNextYear : function(){
12877         this.update(this.activeDate.add("y", 1));
12878     },
12879
12880     
12881    // private
12882     update : function(date)
12883     {
12884         var vd = this.activeDate;
12885         this.activeDate = date;
12886 //        if(vd && this.el){
12887 //            var t = date.getTime();
12888 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
12889 //                Roo.log('using add remove');
12890 //                
12891 //                this.fireEvent('monthchange', this, date);
12892 //                
12893 //                this.cells.removeClass("fc-state-highlight");
12894 //                this.cells.each(function(c){
12895 //                   if(c.dateValue == t){
12896 //                       c.addClass("fc-state-highlight");
12897 //                       setTimeout(function(){
12898 //                            try{c.dom.firstChild.focus();}catch(e){}
12899 //                       }, 50);
12900 //                       return false;
12901 //                   }
12902 //                   return true;
12903 //                });
12904 //                return;
12905 //            }
12906 //        }
12907         
12908         var days = date.getDaysInMonth();
12909         
12910         var firstOfMonth = date.getFirstDateOfMonth();
12911         var startingPos = firstOfMonth.getDay()-this.startDay;
12912         
12913         if(startingPos < this.startDay){
12914             startingPos += 7;
12915         }
12916         
12917         var pm = date.add(Date.MONTH, -1);
12918         var prevStart = pm.getDaysInMonth()-startingPos;
12919 //        
12920         this.cells = this.el.select('.fc-day',true);
12921         this.textNodes = this.el.query('.fc-day-number');
12922         this.cells.addClassOnOver('fc-state-hover');
12923         
12924         var cells = this.cells.elements;
12925         var textEls = this.textNodes;
12926         
12927         Roo.each(cells, function(cell){
12928             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
12929         });
12930         
12931         days += startingPos;
12932
12933         // convert everything to numbers so it's fast
12934         var day = 86400000;
12935         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
12936         //Roo.log(d);
12937         //Roo.log(pm);
12938         //Roo.log(prevStart);
12939         
12940         var today = new Date().clearTime().getTime();
12941         var sel = date.clearTime().getTime();
12942         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
12943         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
12944         var ddMatch = this.disabledDatesRE;
12945         var ddText = this.disabledDatesText;
12946         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
12947         var ddaysText = this.disabledDaysText;
12948         var format = this.format;
12949         
12950         var setCellClass = function(cal, cell){
12951             cell.row = 0;
12952             cell.events = [];
12953             cell.more = [];
12954             //Roo.log('set Cell Class');
12955             cell.title = "";
12956             var t = d.getTime();
12957             
12958             //Roo.log(d);
12959             
12960             cell.dateValue = t;
12961             if(t == today){
12962                 cell.className += " fc-today";
12963                 cell.className += " fc-state-highlight";
12964                 cell.title = cal.todayText;
12965             }
12966             if(t == sel){
12967                 // disable highlight in other month..
12968                 //cell.className += " fc-state-highlight";
12969                 
12970             }
12971             // disabling
12972             if(t < min) {
12973                 cell.className = " fc-state-disabled";
12974                 cell.title = cal.minText;
12975                 return;
12976             }
12977             if(t > max) {
12978                 cell.className = " fc-state-disabled";
12979                 cell.title = cal.maxText;
12980                 return;
12981             }
12982             if(ddays){
12983                 if(ddays.indexOf(d.getDay()) != -1){
12984                     cell.title = ddaysText;
12985                     cell.className = " fc-state-disabled";
12986                 }
12987             }
12988             if(ddMatch && format){
12989                 var fvalue = d.dateFormat(format);
12990                 if(ddMatch.test(fvalue)){
12991                     cell.title = ddText.replace("%0", fvalue);
12992                     cell.className = " fc-state-disabled";
12993                 }
12994             }
12995             
12996             if (!cell.initialClassName) {
12997                 cell.initialClassName = cell.dom.className;
12998             }
12999             
13000             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
13001         };
13002
13003         var i = 0;
13004         
13005         for(; i < startingPos; i++) {
13006             textEls[i].innerHTML = (++prevStart);
13007             d.setDate(d.getDate()+1);
13008             
13009             cells[i].className = "fc-past fc-other-month";
13010             setCellClass(this, cells[i]);
13011         }
13012         
13013         var intDay = 0;
13014         
13015         for(; i < days; i++){
13016             intDay = i - startingPos + 1;
13017             textEls[i].innerHTML = (intDay);
13018             d.setDate(d.getDate()+1);
13019             
13020             cells[i].className = ''; // "x-date-active";
13021             setCellClass(this, cells[i]);
13022         }
13023         var extraDays = 0;
13024         
13025         for(; i < 42; i++) {
13026             textEls[i].innerHTML = (++extraDays);
13027             d.setDate(d.getDate()+1);
13028             
13029             cells[i].className = "fc-future fc-other-month";
13030             setCellClass(this, cells[i]);
13031         }
13032         
13033         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
13034         
13035         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
13036         
13037         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
13038         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
13039         
13040         if(totalRows != 6){
13041             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
13042             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
13043         }
13044         
13045         this.fireEvent('monthchange', this, date);
13046         
13047         
13048         /*
13049         if(!this.internalRender){
13050             var main = this.el.dom.firstChild;
13051             var w = main.offsetWidth;
13052             this.el.setWidth(w + this.el.getBorderWidth("lr"));
13053             Roo.fly(main).setWidth(w);
13054             this.internalRender = true;
13055             // opera does not respect the auto grow header center column
13056             // then, after it gets a width opera refuses to recalculate
13057             // without a second pass
13058             if(Roo.isOpera && !this.secondPass){
13059                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
13060                 this.secondPass = true;
13061                 this.update.defer(10, this, [date]);
13062             }
13063         }
13064         */
13065         
13066     },
13067     
13068     findCell : function(dt) {
13069         dt = dt.clearTime().getTime();
13070         var ret = false;
13071         this.cells.each(function(c){
13072             //Roo.log("check " +c.dateValue + '?=' + dt);
13073             if(c.dateValue == dt){
13074                 ret = c;
13075                 return false;
13076             }
13077             return true;
13078         });
13079         
13080         return ret;
13081     },
13082     
13083     findCells : function(ev) {
13084         var s = ev.start.clone().clearTime().getTime();
13085        // Roo.log(s);
13086         var e= ev.end.clone().clearTime().getTime();
13087        // Roo.log(e);
13088         var ret = [];
13089         this.cells.each(function(c){
13090              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
13091             
13092             if(c.dateValue > e){
13093                 return ;
13094             }
13095             if(c.dateValue < s){
13096                 return ;
13097             }
13098             ret.push(c);
13099         });
13100         
13101         return ret;    
13102     },
13103     
13104 //    findBestRow: function(cells)
13105 //    {
13106 //        var ret = 0;
13107 //        
13108 //        for (var i =0 ; i < cells.length;i++) {
13109 //            ret  = Math.max(cells[i].rows || 0,ret);
13110 //        }
13111 //        return ret;
13112 //        
13113 //    },
13114     
13115     
13116     addItem : function(ev)
13117     {
13118         // look for vertical location slot in
13119         var cells = this.findCells(ev);
13120         
13121 //        ev.row = this.findBestRow(cells);
13122         
13123         // work out the location.
13124         
13125         var crow = false;
13126         var rows = [];
13127         for(var i =0; i < cells.length; i++) {
13128             
13129             cells[i].row = cells[0].row;
13130             
13131             if(i == 0){
13132                 cells[i].row = cells[i].row + 1;
13133             }
13134             
13135             if (!crow) {
13136                 crow = {
13137                     start : cells[i],
13138                     end :  cells[i]
13139                 };
13140                 continue;
13141             }
13142             if (crow.start.getY() == cells[i].getY()) {
13143                 // on same row.
13144                 crow.end = cells[i];
13145                 continue;
13146             }
13147             // different row.
13148             rows.push(crow);
13149             crow = {
13150                 start: cells[i],
13151                 end : cells[i]
13152             };
13153             
13154         }
13155         
13156         rows.push(crow);
13157         ev.els = [];
13158         ev.rows = rows;
13159         ev.cells = cells;
13160         
13161         cells[0].events.push(ev);
13162         
13163         this.calevents.push(ev);
13164     },
13165     
13166     clearEvents: function() {
13167         
13168         if(!this.calevents){
13169             return;
13170         }
13171         
13172         Roo.each(this.cells.elements, function(c){
13173             c.row = 0;
13174             c.events = [];
13175             c.more = [];
13176         });
13177         
13178         Roo.each(this.calevents, function(e) {
13179             Roo.each(e.els, function(el) {
13180                 el.un('mouseenter' ,this.onEventEnter, this);
13181                 el.un('mouseleave' ,this.onEventLeave, this);
13182                 el.remove();
13183             },this);
13184         },this);
13185         
13186         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
13187             e.remove();
13188         });
13189         
13190     },
13191     
13192     renderEvents: function()
13193     {   
13194         var _this = this;
13195         
13196         this.cells.each(function(c) {
13197             
13198             if(c.row < 5){
13199                 return;
13200             }
13201             
13202             var ev = c.events;
13203             
13204             var r = 4;
13205             if(c.row != c.events.length){
13206                 r = 4 - (4 - (c.row - c.events.length));
13207             }
13208             
13209             c.events = ev.slice(0, r);
13210             c.more = ev.slice(r);
13211             
13212             if(c.more.length && c.more.length == 1){
13213                 c.events.push(c.more.pop());
13214             }
13215             
13216             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
13217             
13218         });
13219             
13220         this.cells.each(function(c) {
13221             
13222             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
13223             
13224             
13225             for (var e = 0; e < c.events.length; e++){
13226                 var ev = c.events[e];
13227                 var rows = ev.rows;
13228                 
13229                 for(var i = 0; i < rows.length; i++) {
13230                 
13231                     // how many rows should it span..
13232
13233                     var  cfg = {
13234                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
13235                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
13236
13237                         unselectable : "on",
13238                         cn : [
13239                             {
13240                                 cls: 'fc-event-inner',
13241                                 cn : [
13242     //                                {
13243     //                                  tag:'span',
13244     //                                  cls: 'fc-event-time',
13245     //                                  html : cells.length > 1 ? '' : ev.time
13246     //                                },
13247                                     {
13248                                       tag:'span',
13249                                       cls: 'fc-event-title',
13250                                       html : String.format('{0}', ev.title)
13251                                     }
13252
13253
13254                                 ]
13255                             },
13256                             {
13257                                 cls: 'ui-resizable-handle ui-resizable-e',
13258                                 html : '&nbsp;&nbsp;&nbsp'
13259                             }
13260
13261                         ]
13262                     };
13263
13264                     if (i == 0) {
13265                         cfg.cls += ' fc-event-start';
13266                     }
13267                     if ((i+1) == rows.length) {
13268                         cfg.cls += ' fc-event-end';
13269                     }
13270
13271                     var ctr = _this.el.select('.fc-event-container',true).first();
13272                     var cg = ctr.createChild(cfg);
13273
13274                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
13275                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
13276
13277                     var r = (c.more.length) ? 1 : 0;
13278                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
13279                     cg.setWidth(ebox.right - sbox.x -2);
13280
13281                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
13282                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
13283                     cg.on('click', _this.onEventClick, _this, ev);
13284
13285                     ev.els.push(cg);
13286                     
13287                 }
13288                 
13289             }
13290             
13291             
13292             if(c.more.length){
13293                 var  cfg = {
13294                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
13295                     style : 'position: absolute',
13296                     unselectable : "on",
13297                     cn : [
13298                         {
13299                             cls: 'fc-event-inner',
13300                             cn : [
13301                                 {
13302                                   tag:'span',
13303                                   cls: 'fc-event-title',
13304                                   html : 'More'
13305                                 }
13306
13307
13308                             ]
13309                         },
13310                         {
13311                             cls: 'ui-resizable-handle ui-resizable-e',
13312                             html : '&nbsp;&nbsp;&nbsp'
13313                         }
13314
13315                     ]
13316                 };
13317
13318                 var ctr = _this.el.select('.fc-event-container',true).first();
13319                 var cg = ctr.createChild(cfg);
13320
13321                 var sbox = c.select('.fc-day-content',true).first().getBox();
13322                 var ebox = c.select('.fc-day-content',true).first().getBox();
13323                 //Roo.log(cg);
13324                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
13325                 cg.setWidth(ebox.right - sbox.x -2);
13326
13327                 cg.on('click', _this.onMoreEventClick, _this, c.more);
13328                 
13329             }
13330             
13331         });
13332         
13333         
13334         
13335     },
13336     
13337     onEventEnter: function (e, el,event,d) {
13338         this.fireEvent('evententer', this, el, event);
13339     },
13340     
13341     onEventLeave: function (e, el,event,d) {
13342         this.fireEvent('eventleave', this, el, event);
13343     },
13344     
13345     onEventClick: function (e, el,event,d) {
13346         this.fireEvent('eventclick', this, el, event);
13347     },
13348     
13349     onMonthChange: function () {
13350         this.store.load();
13351     },
13352     
13353     onMoreEventClick: function(e, el, more)
13354     {
13355         var _this = this;
13356         
13357         this.calpopover.placement = 'right';
13358         this.calpopover.setTitle('More');
13359         
13360         this.calpopover.setContent('');
13361         
13362         var ctr = this.calpopover.el.select('.popover-content', true).first();
13363         
13364         Roo.each(more, function(m){
13365             var cfg = {
13366                 cls : 'fc-event-hori fc-event-draggable',
13367                 html : m.title
13368             }
13369             var cg = ctr.createChild(cfg);
13370             
13371             cg.on('click', _this.onEventClick, _this, m);
13372         });
13373         
13374         this.calpopover.show(el);
13375         
13376         
13377     },
13378     
13379     onLoad: function () 
13380     {   
13381         this.calevents = [];
13382         var cal = this;
13383         
13384         if(this.store.getCount() > 0){
13385             this.store.data.each(function(d){
13386                cal.addItem({
13387                     id : d.data.id,
13388                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
13389                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
13390                     time : d.data.start_time,
13391                     title : d.data.title,
13392                     description : d.data.description,
13393                     venue : d.data.venue
13394                 });
13395             });
13396         }
13397         
13398         this.renderEvents();
13399         
13400         if(this.calevents.length && this.loadMask){
13401             this.maskEl.hide();
13402         }
13403     },
13404     
13405     onBeforeLoad: function()
13406     {
13407         this.clearEvents();
13408         if(this.loadMask){
13409             this.maskEl.show();
13410         }
13411     }
13412 });
13413
13414  
13415  /*
13416  * - LGPL
13417  *
13418  * element
13419  * 
13420  */
13421
13422 /**
13423  * @class Roo.bootstrap.Popover
13424  * @extends Roo.bootstrap.Component
13425  * Bootstrap Popover class
13426  * @cfg {String} html contents of the popover   (or false to use children..)
13427  * @cfg {String} title of popover (or false to hide)
13428  * @cfg {String} placement how it is placed
13429  * @cfg {String} trigger click || hover (or false to trigger manually)
13430  * @cfg {String} over what (parent or false to trigger manually.)
13431  * 
13432  * @constructor
13433  * Create a new Popover
13434  * @param {Object} config The config object
13435  */
13436
13437 Roo.bootstrap.Popover = function(config){
13438     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
13439 };
13440
13441 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
13442     
13443     title: 'Fill in a title',
13444     html: false,
13445     
13446     placement : 'right',
13447     trigger : 'hover', // hover
13448     
13449     over: 'parent',
13450     
13451     can_build_overlaid : false,
13452     
13453     getChildContainer : function()
13454     {
13455         return this.el.select('.popover-content',true).first();
13456     },
13457     
13458     getAutoCreate : function(){
13459          Roo.log('make popover?');
13460         var cfg = {
13461            cls : 'popover roo-dynamic',
13462            style: 'display:block',
13463            cn : [
13464                 {
13465                     cls : 'arrow'
13466                 },
13467                 {
13468                     cls : 'popover-inner',
13469                     cn : [
13470                         {
13471                             tag: 'h3',
13472                             cls: 'popover-title',
13473                             html : this.title
13474                         },
13475                         {
13476                             cls : 'popover-content',
13477                             html : this.html
13478                         }
13479                     ]
13480                     
13481                 }
13482            ]
13483         };
13484         
13485         return cfg;
13486     },
13487     setTitle: function(str)
13488     {
13489         this.el.select('.popover-title',true).first().dom.innerHTML = str;
13490     },
13491     setContent: function(str)
13492     {
13493         this.el.select('.popover-content',true).first().dom.innerHTML = str;
13494     },
13495     // as it get's added to the bottom of the page.
13496     onRender : function(ct, position)
13497     {
13498         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
13499         if(!this.el){
13500             var cfg = Roo.apply({},  this.getAutoCreate());
13501             cfg.id = Roo.id();
13502             
13503             if (this.cls) {
13504                 cfg.cls += ' ' + this.cls;
13505             }
13506             if (this.style) {
13507                 cfg.style = this.style;
13508             }
13509             Roo.log("adding to ")
13510             this.el = Roo.get(document.body).createChild(cfg, position);
13511             Roo.log(this.el);
13512         }
13513         this.initEvents();
13514     },
13515     
13516     initEvents : function()
13517     {
13518         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
13519         this.el.enableDisplayMode('block');
13520         this.el.hide();
13521         if (this.over === false) {
13522             return; 
13523         }
13524         if (this.triggers === false) {
13525             return;
13526         }
13527         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13528         var triggers = this.trigger ? this.trigger.split(' ') : [];
13529         Roo.each(triggers, function(trigger) {
13530         
13531             if (trigger == 'click') {
13532                 on_el.on('click', this.toggle, this);
13533             } else if (trigger != 'manual') {
13534                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
13535                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
13536       
13537                 on_el.on(eventIn  ,this.enter, this);
13538                 on_el.on(eventOut, this.leave, this);
13539             }
13540         }, this);
13541         
13542     },
13543     
13544     
13545     // private
13546     timeout : null,
13547     hoverState : null,
13548     
13549     toggle : function () {
13550         this.hoverState == 'in' ? this.leave() : this.enter();
13551     },
13552     
13553     enter : function () {
13554        
13555     
13556         clearTimeout(this.timeout);
13557     
13558         this.hoverState = 'in'
13559     
13560         if (!this.delay || !this.delay.show) {
13561             this.show();
13562             return 
13563         }
13564         var _t = this;
13565         this.timeout = setTimeout(function () {
13566             if (_t.hoverState == 'in') {
13567                 _t.show();
13568             }
13569         }, this.delay.show)
13570     },
13571     leave : function() {
13572         clearTimeout(this.timeout);
13573     
13574         this.hoverState = 'out'
13575     
13576         if (!this.delay || !this.delay.hide) {
13577             this.hide();
13578             return 
13579         }
13580         var _t = this;
13581         this.timeout = setTimeout(function () {
13582             if (_t.hoverState == 'out') {
13583                 _t.hide();
13584             }
13585         }, this.delay.hide)
13586     },
13587     
13588     show : function (on_el)
13589     {
13590         if (!on_el) {
13591             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13592         }
13593         // set content.
13594         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
13595         if (this.html !== false) {
13596             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
13597         }
13598         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
13599         if (!this.title.length) {
13600             this.el.select('.popover-title',true).hide();
13601         }
13602         
13603         var placement = typeof this.placement == 'function' ?
13604             this.placement.call(this, this.el, on_el) :
13605             this.placement;
13606             
13607         var autoToken = /\s?auto?\s?/i;
13608         var autoPlace = autoToken.test(placement);
13609         if (autoPlace) {
13610             placement = placement.replace(autoToken, '') || 'top';
13611         }
13612         
13613         //this.el.detach()
13614         //this.el.setXY([0,0]);
13615         this.el.show();
13616         this.el.dom.style.display='block';
13617         this.el.addClass(placement);
13618         
13619         //this.el.appendTo(on_el);
13620         
13621         var p = this.getPosition();
13622         var box = this.el.getBox();
13623         
13624         if (autoPlace) {
13625             // fixme..
13626         }
13627         var align = Roo.bootstrap.Popover.alignment[placement]
13628         this.el.alignTo(on_el, align[0],align[1]);
13629         //var arrow = this.el.select('.arrow',true).first();
13630         //arrow.set(align[2], 
13631         
13632         this.el.addClass('in');
13633         this.hoverState = null;
13634         
13635         if (this.el.hasClass('fade')) {
13636             // fade it?
13637         }
13638         
13639     },
13640     hide : function()
13641     {
13642         this.el.setXY([0,0]);
13643         this.el.removeClass('in');
13644         this.el.hide();
13645         
13646     }
13647     
13648 });
13649
13650 Roo.bootstrap.Popover.alignment = {
13651     'left' : ['r-l', [-10,0], 'right'],
13652     'right' : ['l-r', [10,0], 'left'],
13653     'bottom' : ['t-b', [0,10], 'top'],
13654     'top' : [ 'b-t', [0,-10], 'bottom']
13655 };
13656
13657  /*
13658  * - LGPL
13659  *
13660  * Progress
13661  * 
13662  */
13663
13664 /**
13665  * @class Roo.bootstrap.Progress
13666  * @extends Roo.bootstrap.Component
13667  * Bootstrap Progress class
13668  * @cfg {Boolean} striped striped of the progress bar
13669  * @cfg {Boolean} active animated of the progress bar
13670  * 
13671  * 
13672  * @constructor
13673  * Create a new Progress
13674  * @param {Object} config The config object
13675  */
13676
13677 Roo.bootstrap.Progress = function(config){
13678     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
13679 };
13680
13681 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
13682     
13683     striped : false,
13684     active: false,
13685     
13686     getAutoCreate : function(){
13687         var cfg = {
13688             tag: 'div',
13689             cls: 'progress'
13690         };
13691         
13692         
13693         if(this.striped){
13694             cfg.cls += ' progress-striped';
13695         }
13696       
13697         if(this.active){
13698             cfg.cls += ' active';
13699         }
13700         
13701         
13702         return cfg;
13703     }
13704    
13705 });
13706
13707  
13708
13709  /*
13710  * - LGPL
13711  *
13712  * ProgressBar
13713  * 
13714  */
13715
13716 /**
13717  * @class Roo.bootstrap.ProgressBar
13718  * @extends Roo.bootstrap.Component
13719  * Bootstrap ProgressBar class
13720  * @cfg {Number} aria_valuenow aria-value now
13721  * @cfg {Number} aria_valuemin aria-value min
13722  * @cfg {Number} aria_valuemax aria-value max
13723  * @cfg {String} label label for the progress bar
13724  * @cfg {String} panel (success | info | warning | danger )
13725  * @cfg {String} role role of the progress bar
13726  * @cfg {String} sr_only text
13727  * 
13728  * 
13729  * @constructor
13730  * Create a new ProgressBar
13731  * @param {Object} config The config object
13732  */
13733
13734 Roo.bootstrap.ProgressBar = function(config){
13735     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
13736 };
13737
13738 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
13739     
13740     aria_valuenow : 0,
13741     aria_valuemin : 0,
13742     aria_valuemax : 100,
13743     label : false,
13744     panel : false,
13745     role : false,
13746     sr_only: false,
13747     
13748     getAutoCreate : function()
13749     {
13750         
13751         var cfg = {
13752             tag: 'div',
13753             cls: 'progress-bar',
13754             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
13755         };
13756         
13757         if(this.sr_only){
13758             cfg.cn = {
13759                 tag: 'span',
13760                 cls: 'sr-only',
13761                 html: this.sr_only
13762             }
13763         }
13764         
13765         if(this.role){
13766             cfg.role = this.role;
13767         }
13768         
13769         if(this.aria_valuenow){
13770             cfg['aria-valuenow'] = this.aria_valuenow;
13771         }
13772         
13773         if(this.aria_valuemin){
13774             cfg['aria-valuemin'] = this.aria_valuemin;
13775         }
13776         
13777         if(this.aria_valuemax){
13778             cfg['aria-valuemax'] = this.aria_valuemax;
13779         }
13780         
13781         if(this.label && !this.sr_only){
13782             cfg.html = this.label;
13783         }
13784         
13785         if(this.panel){
13786             cfg.cls += ' progress-bar-' + this.panel;
13787         }
13788         
13789         return cfg;
13790     },
13791     
13792     update : function(aria_valuenow)
13793     {
13794         this.aria_valuenow = aria_valuenow;
13795         
13796         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
13797     }
13798    
13799 });
13800
13801  
13802
13803  /*
13804  * - LGPL
13805  *
13806  * column
13807  * 
13808  */
13809
13810 /**
13811  * @class Roo.bootstrap.TabGroup
13812  * @extends Roo.bootstrap.Column
13813  * Bootstrap Column class
13814  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
13815  * @cfg {Boolean} carousel true to make the group behave like a carousel
13816  * 
13817  * @constructor
13818  * Create a new TabGroup
13819  * @param {Object} config The config object
13820  */
13821
13822 Roo.bootstrap.TabGroup = function(config){
13823     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
13824     if (!this.navId) {
13825         this.navId = Roo.id();
13826     }
13827     this.tabs = [];
13828     Roo.bootstrap.TabGroup.register(this);
13829     
13830 };
13831
13832 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
13833     
13834     carousel : false,
13835     transition : false,
13836      
13837     getAutoCreate : function()
13838     {
13839         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
13840         
13841         cfg.cls += ' tab-content';
13842         
13843         if (this.carousel) {
13844             cfg.cls += ' carousel slide';
13845             cfg.cn = [{
13846                cls : 'carousel-inner'
13847             }]
13848         }
13849         
13850         
13851         return cfg;
13852     },
13853     getChildContainer : function()
13854     {
13855         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
13856     },
13857     
13858     /**
13859     * register a Navigation item
13860     * @param {Roo.bootstrap.NavItem} the navitem to add
13861     */
13862     register : function(item)
13863     {
13864         this.tabs.push( item);
13865         item.navId = this.navId; // not really needed..
13866     
13867     },
13868     
13869     getActivePanel : function()
13870     {
13871         var r = false;
13872         Roo.each(this.tabs, function(t) {
13873             if (t.active) {
13874                 r = t;
13875                 return false;
13876             }
13877             return null;
13878         });
13879         return r;
13880         
13881     },
13882     getPanelByName : function(n)
13883     {
13884         var r = false;
13885         Roo.each(this.tabs, function(t) {
13886             if (t.tabId == n) {
13887                 r = t;
13888                 return false;
13889             }
13890             return null;
13891         });
13892         return r;
13893     },
13894     indexOfPanel : function(p)
13895     {
13896         var r = false;
13897         Roo.each(this.tabs, function(t,i) {
13898             if (t.tabId == p.tabId) {
13899                 r = i;
13900                 return false;
13901             }
13902             return null;
13903         });
13904         return r;
13905     },
13906     /**
13907      * show a specific panel
13908      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
13909      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
13910      */
13911     showPanel : function (pan)
13912     {
13913         
13914         if (typeof(pan) == 'number') {
13915             pan = this.tabs[pan];
13916         }
13917         if (typeof(pan) == 'string') {
13918             pan = this.getPanelByName(pan);
13919         }
13920         if (pan.tabId == this.getActivePanel().tabId) {
13921             return true;
13922         }
13923         var cur = this.getActivePanel();
13924         
13925         if (false === cur.fireEvent('beforedeactivate')) {
13926             return false;
13927         }
13928         
13929         if (this.carousel) {
13930             this.transition = true;
13931             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
13932             var lr = dir == 'next' ? 'left' : 'right';
13933             pan.el.addClass(dir); // or prev
13934             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
13935             cur.el.addClass(lr); // or right
13936             pan.el.addClass(lr);
13937             
13938             var _this = this;
13939             cur.el.on('transitionend', function() {
13940                 Roo.log("trans end?");
13941                 
13942                 pan.el.removeClass([lr,dir]);
13943                 pan.setActive(true);
13944                 
13945                 cur.el.removeClass([lr]);
13946                 cur.setActive(false);
13947                 
13948                 _this.transition = false;
13949                 
13950             }, this, { single:  true } );
13951             return true;
13952         }
13953         
13954         cur.setActive(false);
13955         pan.setActive(true);
13956         return true;
13957         
13958     },
13959     showPanelNext : function()
13960     {
13961         var i = this.indexOfPanel(this.getActivePanel());
13962         if (i > this.tabs.length) {
13963             return;
13964         }
13965         this.showPanel(this.tabs[i+1]);
13966     },
13967     showPanelPrev : function()
13968     {
13969         var i = this.indexOfPanel(this.getActivePanel());
13970         if (i  < 1) {
13971             return;
13972         }
13973         this.showPanel(this.tabs[i-1]);
13974     }
13975     
13976     
13977   
13978 });
13979
13980  
13981
13982  
13983  
13984 Roo.apply(Roo.bootstrap.TabGroup, {
13985     
13986     groups: {},
13987      /**
13988     * register a Navigation Group
13989     * @param {Roo.bootstrap.NavGroup} the navgroup to add
13990     */
13991     register : function(navgrp)
13992     {
13993         this.groups[navgrp.navId] = navgrp;
13994         
13995     },
13996     /**
13997     * fetch a Navigation Group based on the navigation ID
13998     * if one does not exist , it will get created.
13999     * @param {string} the navgroup to add
14000     * @returns {Roo.bootstrap.NavGroup} the navgroup 
14001     */
14002     get: function(navId) {
14003         if (typeof(this.groups[navId]) == 'undefined') {
14004             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
14005         }
14006         return this.groups[navId] ;
14007     }
14008     
14009     
14010     
14011 });
14012
14013  /*
14014  * - LGPL
14015  *
14016  * TabPanel
14017  * 
14018  */
14019
14020 /**
14021  * @class Roo.bootstrap.TabPanel
14022  * @extends Roo.bootstrap.Component
14023  * Bootstrap TabPanel class
14024  * @cfg {Boolean} active panel active
14025  * @cfg {String} html panel content
14026  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
14027  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
14028  * 
14029  * 
14030  * @constructor
14031  * Create a new TabPanel
14032  * @param {Object} config The config object
14033  */
14034
14035 Roo.bootstrap.TabPanel = function(config){
14036     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
14037     this.addEvents({
14038         /**
14039              * @event changed
14040              * Fires when the active status changes
14041              * @param {Roo.bootstrap.TabPanel} this
14042              * @param {Boolean} state the new state
14043             
14044          */
14045         'changed': true,
14046         /**
14047              * @event beforedeactivate
14048              * Fires before a tab is de-activated - can be used to do validation on a form.
14049              * @param {Roo.bootstrap.TabPanel} this
14050              * @return {Boolean} false if there is an error
14051             
14052          */
14053         'beforedeactivate': true
14054      });
14055     
14056     this.tabId = this.tabId || Roo.id();
14057   
14058 };
14059
14060 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
14061     
14062     active: false,
14063     html: false,
14064     tabId: false,
14065     navId : false,
14066     
14067     getAutoCreate : function(){
14068         var cfg = {
14069             tag: 'div',
14070             // item is needed for carousel - not sure if it has any effect otherwise
14071             cls: 'tab-pane item',
14072             html: this.html || ''
14073         };
14074         
14075         if(this.active){
14076             cfg.cls += ' active';
14077         }
14078         
14079         if(this.tabId){
14080             cfg.tabId = this.tabId;
14081         }
14082         
14083         
14084         return cfg;
14085     },
14086     
14087     initEvents:  function()
14088     {
14089         Roo.log('-------- init events on tab panel ---------');
14090         
14091         var p = this.parent();
14092         this.navId = this.navId || p.navId;
14093         
14094         if (typeof(this.navId) != 'undefined') {
14095             // not really needed.. but just in case.. parent should be a NavGroup.
14096             var tg = Roo.bootstrap.TabGroup.get(this.navId);
14097             Roo.log(['register', tg, this]);
14098             tg.register(this);
14099         }
14100     },
14101     
14102     
14103     onRender : function(ct, position)
14104     {
14105        // Roo.log("Call onRender: " + this.xtype);
14106         
14107         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
14108         
14109         
14110         
14111         
14112         
14113     },
14114     
14115     setActive: function(state)
14116     {
14117         Roo.log("panel - set active " + this.tabId + "=" + state);
14118         
14119         this.active = state;
14120         if (!state) {
14121             this.el.removeClass('active');
14122             
14123         } else  if (!this.el.hasClass('active')) {
14124             this.el.addClass('active');
14125         }
14126         this.fireEvent('changed', this, state);
14127     }
14128     
14129     
14130 });
14131  
14132
14133  
14134
14135  /*
14136  * - LGPL
14137  *
14138  * DateField
14139  * 
14140  */
14141
14142 /**
14143  * @class Roo.bootstrap.DateField
14144  * @extends Roo.bootstrap.Input
14145  * Bootstrap DateField class
14146  * @cfg {Number} weekStart default 0
14147  * @cfg {Number} weekStart default 0
14148  * @cfg {Number} viewMode default empty, (months|years)
14149  * @cfg {Number} minViewMode default empty, (months|years)
14150  * @cfg {Number} startDate default -Infinity
14151  * @cfg {Number} endDate default Infinity
14152  * @cfg {Boolean} todayHighlight default false
14153  * @cfg {Boolean} todayBtn default false
14154  * @cfg {Boolean} calendarWeeks default false
14155  * @cfg {Object} daysOfWeekDisabled default empty
14156  * 
14157  * @cfg {Boolean} keyboardNavigation default true
14158  * @cfg {String} language default en
14159  * 
14160  * @constructor
14161  * Create a new DateField
14162  * @param {Object} config The config object
14163  */
14164
14165 Roo.bootstrap.DateField = function(config){
14166     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
14167      this.addEvents({
14168             /**
14169              * @event show
14170              * Fires when this field show.
14171              * @param {Roo.bootstrap.DateField} this
14172              * @param {Mixed} date The date value
14173              */
14174             show : true,
14175             /**
14176              * @event show
14177              * Fires when this field hide.
14178              * @param {Roo.bootstrap.DateField} this
14179              * @param {Mixed} date The date value
14180              */
14181             hide : true,
14182             /**
14183              * @event select
14184              * Fires when select a date.
14185              * @param {Roo.bootstrap.DateField} this
14186              * @param {Mixed} date The date value
14187              */
14188             select : true
14189         });
14190 };
14191
14192 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
14193     
14194     /**
14195      * @cfg {String} format
14196      * The default date format string which can be overriden for localization support.  The format must be
14197      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
14198      */
14199     format : "m/d/y",
14200     /**
14201      * @cfg {String} altFormats
14202      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
14203      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
14204      */
14205     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
14206     
14207     weekStart : 0,
14208     
14209     viewMode : '',
14210     
14211     minViewMode : '',
14212     
14213     todayHighlight : false,
14214     
14215     todayBtn: false,
14216     
14217     language: 'en',
14218     
14219     keyboardNavigation: true,
14220     
14221     calendarWeeks: false,
14222     
14223     startDate: -Infinity,
14224     
14225     endDate: Infinity,
14226     
14227     daysOfWeekDisabled: [],
14228     
14229     _events: [],
14230     
14231     UTCDate: function()
14232     {
14233         return new Date(Date.UTC.apply(Date, arguments));
14234     },
14235     
14236     UTCToday: function()
14237     {
14238         var today = new Date();
14239         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
14240     },
14241     
14242     getDate: function() {
14243             var d = this.getUTCDate();
14244             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
14245     },
14246     
14247     getUTCDate: function() {
14248             return this.date;
14249     },
14250     
14251     setDate: function(d) {
14252             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
14253     },
14254     
14255     setUTCDate: function(d) {
14256             this.date = d;
14257             this.setValue(this.formatDate(this.date));
14258     },
14259         
14260     onRender: function(ct, position)
14261     {
14262         
14263         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
14264         
14265         this.language = this.language || 'en';
14266         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
14267         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
14268         
14269         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
14270         this.format = this.format || 'm/d/y';
14271         this.isInline = false;
14272         this.isInput = true;
14273         this.component = this.el.select('.add-on', true).first() || false;
14274         this.component = (this.component && this.component.length === 0) ? false : this.component;
14275         this.hasInput = this.component && this.inputEL().length;
14276         
14277         if (typeof(this.minViewMode === 'string')) {
14278             switch (this.minViewMode) {
14279                 case 'months':
14280                     this.minViewMode = 1;
14281                     break;
14282                 case 'years':
14283                     this.minViewMode = 2;
14284                     break;
14285                 default:
14286                     this.minViewMode = 0;
14287                     break;
14288             }
14289         }
14290         
14291         if (typeof(this.viewMode === 'string')) {
14292             switch (this.viewMode) {
14293                 case 'months':
14294                     this.viewMode = 1;
14295                     break;
14296                 case 'years':
14297                     this.viewMode = 2;
14298                     break;
14299                 default:
14300                     this.viewMode = 0;
14301                     break;
14302             }
14303         }
14304                 
14305         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
14306         
14307         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14308         
14309         this.picker().on('mousedown', this.onMousedown, this);
14310         this.picker().on('click', this.onClick, this);
14311         
14312         this.picker().addClass('datepicker-dropdown');
14313         
14314         this.startViewMode = this.viewMode;
14315         
14316         
14317         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
14318             if(!this.calendarWeeks){
14319                 v.remove();
14320                 return;
14321             };
14322             
14323             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
14324             v.attr('colspan', function(i, val){
14325                 return parseInt(val) + 1;
14326             });
14327         })
14328                         
14329         
14330         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
14331         
14332         this.setStartDate(this.startDate);
14333         this.setEndDate(this.endDate);
14334         
14335         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
14336         
14337         this.fillDow();
14338         this.fillMonths();
14339         this.update();
14340         this.showMode();
14341         
14342         if(this.isInline) {
14343             this.show();
14344         }
14345     },
14346     
14347     picker : function()
14348     {
14349         return this.el.select('.datepicker', true).first();
14350     },
14351     
14352     fillDow: function()
14353     {
14354         var dowCnt = this.weekStart;
14355         
14356         var dow = {
14357             tag: 'tr',
14358             cn: [
14359                 
14360             ]
14361         };
14362         
14363         if(this.calendarWeeks){
14364             dow.cn.push({
14365                 tag: 'th',
14366                 cls: 'cw',
14367                 html: '&nbsp;'
14368             })
14369         }
14370         
14371         while (dowCnt < this.weekStart + 7) {
14372             dow.cn.push({
14373                 tag: 'th',
14374                 cls: 'dow',
14375                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
14376             });
14377         }
14378         
14379         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
14380     },
14381     
14382     fillMonths: function()
14383     {    
14384         var i = 0
14385         var months = this.picker().select('>.datepicker-months td', true).first();
14386         
14387         months.dom.innerHTML = '';
14388         
14389         while (i < 12) {
14390             var month = {
14391                 tag: 'span',
14392                 cls: 'month',
14393                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
14394             }
14395             
14396             months.createChild(month);
14397         }
14398         
14399     },
14400     
14401     update: function()
14402     {
14403         
14404         this.date = (typeof(this.date) === 'undefined') ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
14405         
14406         if (this.date < this.startDate) {
14407             this.viewDate = new Date(this.startDate);
14408         } else if (this.date > this.endDate) {
14409             this.viewDate = new Date(this.endDate);
14410         } else {
14411             this.viewDate = new Date(this.date);
14412         }
14413         
14414         this.fill();
14415     },
14416     
14417     fill: function() 
14418     {
14419         var d = new Date(this.viewDate),
14420                 year = d.getUTCFullYear(),
14421                 month = d.getUTCMonth(),
14422                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
14423                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
14424                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
14425                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
14426                 currentDate = this.date && this.date.valueOf(),
14427                 today = this.UTCToday();
14428         
14429         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
14430         
14431 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
14432         
14433 //        this.picker.select('>tfoot th.today').
14434 //                                              .text(dates[this.language].today)
14435 //                                              .toggle(this.todayBtn !== false);
14436     
14437         this.updateNavArrows();
14438         this.fillMonths();
14439                                                 
14440         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
14441         
14442         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
14443          
14444         prevMonth.setUTCDate(day);
14445         
14446         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
14447         
14448         var nextMonth = new Date(prevMonth);
14449         
14450         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
14451         
14452         nextMonth = nextMonth.valueOf();
14453         
14454         var fillMonths = false;
14455         
14456         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
14457         
14458         while(prevMonth.valueOf() < nextMonth) {
14459             var clsName = '';
14460             
14461             if (prevMonth.getUTCDay() === this.weekStart) {
14462                 if(fillMonths){
14463                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
14464                 }
14465                     
14466                 fillMonths = {
14467                     tag: 'tr',
14468                     cn: []
14469                 };
14470                 
14471                 if(this.calendarWeeks){
14472                     // ISO 8601: First week contains first thursday.
14473                     // ISO also states week starts on Monday, but we can be more abstract here.
14474                     var
14475                     // Start of current week: based on weekstart/current date
14476                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
14477                     // Thursday of this week
14478                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
14479                     // First Thursday of year, year from thursday
14480                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
14481                     // Calendar week: ms between thursdays, div ms per day, div 7 days
14482                     calWeek =  (th - yth) / 864e5 / 7 + 1;
14483                     
14484                     fillMonths.cn.push({
14485                         tag: 'td',
14486                         cls: 'cw',
14487                         html: calWeek
14488                     });
14489                 }
14490             }
14491             
14492             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
14493                 clsName += ' old';
14494             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
14495                 clsName += ' new';
14496             }
14497             if (this.todayHighlight &&
14498                 prevMonth.getUTCFullYear() == today.getFullYear() &&
14499                 prevMonth.getUTCMonth() == today.getMonth() &&
14500                 prevMonth.getUTCDate() == today.getDate()) {
14501                 clsName += ' today';
14502             }
14503             
14504             if (currentDate && prevMonth.valueOf() === currentDate) {
14505                 clsName += ' active';
14506             }
14507             
14508             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
14509                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
14510                     clsName += ' disabled';
14511             }
14512             
14513             fillMonths.cn.push({
14514                 tag: 'td',
14515                 cls: 'day ' + clsName,
14516                 html: prevMonth.getDate()
14517             })
14518             
14519             prevMonth.setDate(prevMonth.getDate()+1);
14520         }
14521           
14522         var currentYear = this.date && this.date.getUTCFullYear();
14523         var currentMonth = this.date && this.date.getUTCMonth();
14524         
14525         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
14526         
14527         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
14528             v.removeClass('active');
14529             
14530             if(currentYear === year && k === currentMonth){
14531                 v.addClass('active');
14532             }
14533             
14534             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
14535                 v.addClass('disabled');
14536             }
14537             
14538         });
14539         
14540         
14541         year = parseInt(year/10, 10) * 10;
14542         
14543         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
14544         
14545         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
14546         
14547         year -= 1;
14548         for (var i = -1; i < 11; i++) {
14549             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
14550                 tag: 'span',
14551                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
14552                 html: year
14553             })
14554             
14555             year += 1;
14556         }
14557     },
14558     
14559     showMode: function(dir) 
14560     {
14561         if (dir) {
14562             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
14563         }
14564         Roo.each(this.picker().select('>div',true).elements, function(v){
14565             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14566             v.hide();
14567         });
14568         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
14569     },
14570     
14571     place: function()
14572     {
14573         if(this.isInline) return;
14574         
14575         this.picker().removeClass(['bottom', 'top']);
14576         
14577         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
14578             /*
14579              * place to the top of element!
14580              *
14581              */
14582             
14583             this.picker().addClass('top');
14584             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
14585             
14586             return;
14587         }
14588         
14589         this.picker().addClass('bottom');
14590         
14591         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
14592     },
14593     
14594     parseDate : function(value)
14595     {
14596         if(!value || value instanceof Date){
14597             return value;
14598         }
14599         var v = Date.parseDate(value, this.format);
14600         if (!v && this.useIso) {
14601             v = Date.parseDate(value, 'Y-m-d');
14602         }
14603         if(!v && this.altFormats){
14604             if(!this.altFormatsArray){
14605                 this.altFormatsArray = this.altFormats.split("|");
14606             }
14607             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
14608                 v = Date.parseDate(value, this.altFormatsArray[i]);
14609             }
14610         }
14611         return v;
14612     },
14613     
14614     formatDate : function(date, fmt)
14615     {
14616         return (!date || !(date instanceof Date)) ?
14617         date : date.dateFormat(fmt || this.format);
14618     },
14619     
14620     onFocus : function()
14621     {
14622         Roo.bootstrap.DateField.superclass.onFocus.call(this);
14623         this.show();
14624     },
14625     
14626     onBlur : function()
14627     {
14628         Roo.bootstrap.DateField.superclass.onBlur.call(this);
14629         
14630         var d = this.inputEl().getValue();
14631         
14632         if(d && d.length){
14633             this.setValue(d);
14634         }
14635                 
14636         this.hide();
14637     },
14638     
14639     show : function()
14640     {
14641         this.picker().show();
14642         this.update();
14643         this.place();
14644         
14645         this.fireEvent('show', this, this.date);
14646     },
14647     
14648     hide : function()
14649     {
14650         if(this.isInline) return;
14651         this.picker().hide();
14652         this.viewMode = this.startViewMode;
14653         this.showMode();
14654         
14655         this.fireEvent('hide', this, this.date);
14656         
14657     },
14658     
14659     onMousedown: function(e)
14660     {
14661         e.stopPropagation();
14662         e.preventDefault();
14663     },
14664     
14665     keyup: function(e)
14666     {
14667         Roo.bootstrap.DateField.superclass.keyup.call(this);
14668         this.update();
14669     },
14670
14671     setValue: function(v)
14672     {
14673         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
14674         
14675         var d = new Date(v);
14676         
14677         if(isNaN(d.getTime())){
14678             return;
14679         }
14680         
14681         this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
14682
14683         this.update();
14684
14685         this.fireEvent('select', this, this.date);
14686         
14687     },
14688     
14689     getValue: function()
14690     {
14691         return this.formatDate(this.date);
14692     },
14693     
14694     fireKey: function(e)
14695     {
14696         if (!this.picker().isVisible()){
14697             if (e.keyCode == 27) // allow escape to hide and re-show picker
14698                 this.show();
14699             return;
14700         }
14701         
14702         var dateChanged = false,
14703         dir, day, month,
14704         newDate, newViewDate;
14705         
14706         switch(e.keyCode){
14707             case 27: // escape
14708                 this.hide();
14709                 e.preventDefault();
14710                 break;
14711             case 37: // left
14712             case 39: // right
14713                 if (!this.keyboardNavigation) break;
14714                 dir = e.keyCode == 37 ? -1 : 1;
14715                 
14716                 if (e.ctrlKey){
14717                     newDate = this.moveYear(this.date, dir);
14718                     newViewDate = this.moveYear(this.viewDate, dir);
14719                 } else if (e.shiftKey){
14720                     newDate = this.moveMonth(this.date, dir);
14721                     newViewDate = this.moveMonth(this.viewDate, dir);
14722                 } else {
14723                     newDate = new Date(this.date);
14724                     newDate.setUTCDate(this.date.getUTCDate() + dir);
14725                     newViewDate = new Date(this.viewDate);
14726                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
14727                 }
14728                 if (this.dateWithinRange(newDate)){
14729                     this.date = newDate;
14730                     this.viewDate = newViewDate;
14731                     this.setValue(this.formatDate(this.date));
14732 //                    this.update();
14733                     e.preventDefault();
14734                     dateChanged = true;
14735                 }
14736                 break;
14737             case 38: // up
14738             case 40: // down
14739                 if (!this.keyboardNavigation) break;
14740                 dir = e.keyCode == 38 ? -1 : 1;
14741                 if (e.ctrlKey){
14742                     newDate = this.moveYear(this.date, dir);
14743                     newViewDate = this.moveYear(this.viewDate, dir);
14744                 } else if (e.shiftKey){
14745                     newDate = this.moveMonth(this.date, dir);
14746                     newViewDate = this.moveMonth(this.viewDate, dir);
14747                 } else {
14748                     newDate = new Date(this.date);
14749                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
14750                     newViewDate = new Date(this.viewDate);
14751                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
14752                 }
14753                 if (this.dateWithinRange(newDate)){
14754                     this.date = newDate;
14755                     this.viewDate = newViewDate;
14756                     this.setValue(this.formatDate(this.date));
14757 //                    this.update();
14758                     e.preventDefault();
14759                     dateChanged = true;
14760                 }
14761                 break;
14762             case 13: // enter
14763                 this.setValue(this.formatDate(this.date));
14764                 this.hide();
14765                 e.preventDefault();
14766                 break;
14767             case 9: // tab
14768                 this.setValue(this.formatDate(this.date));
14769                 this.hide();
14770                 break;
14771             case 16: // shift
14772             case 17: // ctrl
14773             case 18: // alt
14774                 break;
14775             default :
14776                 this.hide();
14777                 
14778         }
14779     },
14780     
14781     
14782     onClick: function(e) 
14783     {
14784         e.stopPropagation();
14785         e.preventDefault();
14786         
14787         var target = e.getTarget();
14788         
14789         if(target.nodeName.toLowerCase() === 'i'){
14790             target = Roo.get(target).dom.parentNode;
14791         }
14792         
14793         var nodeName = target.nodeName;
14794         var className = target.className;
14795         var html = target.innerHTML;
14796         
14797         switch(nodeName.toLowerCase()) {
14798             case 'th':
14799                 switch(className) {
14800                     case 'switch':
14801                         this.showMode(1);
14802                         break;
14803                     case 'prev':
14804                     case 'next':
14805                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
14806                         switch(this.viewMode){
14807                                 case 0:
14808                                         this.viewDate = this.moveMonth(this.viewDate, dir);
14809                                         break;
14810                                 case 1:
14811                                 case 2:
14812                                         this.viewDate = this.moveYear(this.viewDate, dir);
14813                                         break;
14814                         }
14815                         this.fill();
14816                         break;
14817                     case 'today':
14818                         var date = new Date();
14819                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
14820 //                        this.fill()
14821                         this.setValue(this.formatDate(this.date));
14822                         
14823                         this.hide();
14824                         break;
14825                 }
14826                 break;
14827             case 'span':
14828                 if (className.indexOf('disabled') === -1) {
14829                     this.viewDate.setUTCDate(1);
14830                     if (className.indexOf('month') !== -1) {
14831                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
14832                     } else {
14833                         var year = parseInt(html, 10) || 0;
14834                         this.viewDate.setUTCFullYear(year);
14835                         
14836                     }
14837                     this.showMode(-1);
14838                     this.fill();
14839                 }
14840                 break;
14841                 
14842             case 'td':
14843                 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
14844                     var day = parseInt(html, 10) || 1;
14845                     var year = this.viewDate.getUTCFullYear(),
14846                         month = this.viewDate.getUTCMonth();
14847
14848                     if (className.indexOf('old') !== -1) {
14849                         if(month === 0 ){
14850                             month = 11;
14851                             year -= 1;
14852                         }else{
14853                             month -= 1;
14854                         }
14855                     } else if (className.indexOf('new') !== -1) {
14856                         if (month == 11) {
14857                             month = 0;
14858                             year += 1;
14859                         } else {
14860                             month += 1;
14861                         }
14862                     }
14863                     this.date = this.UTCDate(year, month, day,0,0,0,0);
14864                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
14865 //                    this.fill();
14866                     this.setValue(this.formatDate(this.date));
14867                     this.hide();
14868                 }
14869                 break;
14870         }
14871     },
14872     
14873     setStartDate: function(startDate)
14874     {
14875         this.startDate = startDate || -Infinity;
14876         if (this.startDate !== -Infinity) {
14877             this.startDate = this.parseDate(this.startDate);
14878         }
14879         this.update();
14880         this.updateNavArrows();
14881     },
14882
14883     setEndDate: function(endDate)
14884     {
14885         this.endDate = endDate || Infinity;
14886         if (this.endDate !== Infinity) {
14887             this.endDate = this.parseDate(this.endDate);
14888         }
14889         this.update();
14890         this.updateNavArrows();
14891     },
14892     
14893     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
14894     {
14895         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
14896         if (typeof(this.daysOfWeekDisabled) !== 'object') {
14897             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
14898         }
14899         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
14900             return parseInt(d, 10);
14901         });
14902         this.update();
14903         this.updateNavArrows();
14904     },
14905     
14906     updateNavArrows: function() 
14907     {
14908         var d = new Date(this.viewDate),
14909         year = d.getUTCFullYear(),
14910         month = d.getUTCMonth();
14911         
14912         Roo.each(this.picker().select('.prev', true).elements, function(v){
14913             v.show();
14914             switch (this.viewMode) {
14915                 case 0:
14916
14917                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
14918                         v.hide();
14919                     }
14920                     break;
14921                 case 1:
14922                 case 2:
14923                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
14924                         v.hide();
14925                     }
14926                     break;
14927             }
14928         });
14929         
14930         Roo.each(this.picker().select('.next', true).elements, function(v){
14931             v.show();
14932             switch (this.viewMode) {
14933                 case 0:
14934
14935                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
14936                         v.hide();
14937                     }
14938                     break;
14939                 case 1:
14940                 case 2:
14941                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
14942                         v.hide();
14943                     }
14944                     break;
14945             }
14946         })
14947     },
14948     
14949     moveMonth: function(date, dir)
14950     {
14951         if (!dir) return date;
14952         var new_date = new Date(date.valueOf()),
14953         day = new_date.getUTCDate(),
14954         month = new_date.getUTCMonth(),
14955         mag = Math.abs(dir),
14956         new_month, test;
14957         dir = dir > 0 ? 1 : -1;
14958         if (mag == 1){
14959             test = dir == -1
14960             // If going back one month, make sure month is not current month
14961             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
14962             ? function(){
14963                 return new_date.getUTCMonth() == month;
14964             }
14965             // If going forward one month, make sure month is as expected
14966             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
14967             : function(){
14968                 return new_date.getUTCMonth() != new_month;
14969             };
14970             new_month = month + dir;
14971             new_date.setUTCMonth(new_month);
14972             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
14973             if (new_month < 0 || new_month > 11)
14974                 new_month = (new_month + 12) % 12;
14975         } else {
14976             // For magnitudes >1, move one month at a time...
14977             for (var i=0; i<mag; i++)
14978                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
14979                 new_date = this.moveMonth(new_date, dir);
14980             // ...then reset the day, keeping it in the new month
14981             new_month = new_date.getUTCMonth();
14982             new_date.setUTCDate(day);
14983             test = function(){
14984                 return new_month != new_date.getUTCMonth();
14985             };
14986         }
14987         // Common date-resetting loop -- if date is beyond end of month, make it
14988         // end of month
14989         while (test()){
14990             new_date.setUTCDate(--day);
14991             new_date.setUTCMonth(new_month);
14992         }
14993         return new_date;
14994     },
14995
14996     moveYear: function(date, dir)
14997     {
14998         return this.moveMonth(date, dir*12);
14999     },
15000
15001     dateWithinRange: function(date)
15002     {
15003         return date >= this.startDate && date <= this.endDate;
15004     },
15005
15006     
15007     remove: function() 
15008     {
15009         this.picker().remove();
15010     }
15011    
15012 });
15013
15014 Roo.apply(Roo.bootstrap.DateField,  {
15015     
15016     head : {
15017         tag: 'thead',
15018         cn: [
15019         {
15020             tag: 'tr',
15021             cn: [
15022             {
15023                 tag: 'th',
15024                 cls: 'prev',
15025                 html: '<i class="fa fa-arrow-left"/>'
15026             },
15027             {
15028                 tag: 'th',
15029                 cls: 'switch',
15030                 colspan: '5'
15031             },
15032             {
15033                 tag: 'th',
15034                 cls: 'next',
15035                 html: '<i class="fa fa-arrow-right"/>'
15036             }
15037
15038             ]
15039         }
15040         ]
15041     },
15042     
15043     content : {
15044         tag: 'tbody',
15045         cn: [
15046         {
15047             tag: 'tr',
15048             cn: [
15049             {
15050                 tag: 'td',
15051                 colspan: '7'
15052             }
15053             ]
15054         }
15055         ]
15056     },
15057     
15058     footer : {
15059         tag: 'tfoot',
15060         cn: [
15061         {
15062             tag: 'tr',
15063             cn: [
15064             {
15065                 tag: 'th',
15066                 colspan: '7',
15067                 cls: 'today'
15068             }
15069                     
15070             ]
15071         }
15072         ]
15073     },
15074     
15075     dates:{
15076         en: {
15077             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
15078             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
15079             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
15080             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
15081             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
15082             today: "Today"
15083         }
15084     },
15085     
15086     modes: [
15087     {
15088         clsName: 'days',
15089         navFnc: 'Month',
15090         navStep: 1
15091     },
15092     {
15093         clsName: 'months',
15094         navFnc: 'FullYear',
15095         navStep: 1
15096     },
15097     {
15098         clsName: 'years',
15099         navFnc: 'FullYear',
15100         navStep: 10
15101     }]
15102 });
15103
15104 Roo.apply(Roo.bootstrap.DateField,  {
15105   
15106     template : {
15107         tag: 'div',
15108         cls: 'datepicker dropdown-menu',
15109         cn: [
15110         {
15111             tag: 'div',
15112             cls: 'datepicker-days',
15113             cn: [
15114             {
15115                 tag: 'table',
15116                 cls: 'table-condensed',
15117                 cn:[
15118                 Roo.bootstrap.DateField.head,
15119                 {
15120                     tag: 'tbody'
15121                 },
15122                 Roo.bootstrap.DateField.footer
15123                 ]
15124             }
15125             ]
15126         },
15127         {
15128             tag: 'div',
15129             cls: 'datepicker-months',
15130             cn: [
15131             {
15132                 tag: 'table',
15133                 cls: 'table-condensed',
15134                 cn:[
15135                 Roo.bootstrap.DateField.head,
15136                 Roo.bootstrap.DateField.content,
15137                 Roo.bootstrap.DateField.footer
15138                 ]
15139             }
15140             ]
15141         },
15142         {
15143             tag: 'div',
15144             cls: 'datepicker-years',
15145             cn: [
15146             {
15147                 tag: 'table',
15148                 cls: 'table-condensed',
15149                 cn:[
15150                 Roo.bootstrap.DateField.head,
15151                 Roo.bootstrap.DateField.content,
15152                 Roo.bootstrap.DateField.footer
15153                 ]
15154             }
15155             ]
15156         }
15157         ]
15158     }
15159 });
15160
15161  
15162
15163  /*
15164  * - LGPL
15165  *
15166  * TimeField
15167  * 
15168  */
15169
15170 /**
15171  * @class Roo.bootstrap.TimeField
15172  * @extends Roo.bootstrap.Input
15173  * Bootstrap DateField class
15174  * 
15175  * 
15176  * @constructor
15177  * Create a new TimeField
15178  * @param {Object} config The config object
15179  */
15180
15181 Roo.bootstrap.TimeField = function(config){
15182     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
15183     this.addEvents({
15184             /**
15185              * @event show
15186              * Fires when this field show.
15187              * @param {Roo.bootstrap.DateField} this
15188              * @param {Mixed} date The date value
15189              */
15190             show : true,
15191             /**
15192              * @event show
15193              * Fires when this field hide.
15194              * @param {Roo.bootstrap.DateField} this
15195              * @param {Mixed} date The date value
15196              */
15197             hide : true,
15198             /**
15199              * @event select
15200              * Fires when select a date.
15201              * @param {Roo.bootstrap.DateField} this
15202              * @param {Mixed} date The date value
15203              */
15204             select : true
15205         });
15206 };
15207
15208 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
15209     
15210     /**
15211      * @cfg {String} format
15212      * The default time format string which can be overriden for localization support.  The format must be
15213      * valid according to {@link Date#parseDate} (defaults to 'H:i').
15214      */
15215     format : "H:i",
15216        
15217     onRender: function(ct, position)
15218     {
15219         
15220         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
15221                 
15222         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
15223         
15224         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15225         
15226         this.pop = this.picker().select('>.datepicker-time',true).first();
15227         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block' 
15228         
15229         this.picker().on('mousedown', this.onMousedown, this);
15230         this.picker().on('click', this.onClick, this);
15231         
15232         this.picker().addClass('datepicker-dropdown');
15233     
15234         this.fillTime();
15235         this.update();
15236             
15237         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
15238         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
15239         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
15240         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
15241         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
15242         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
15243
15244     },
15245     
15246     fireKey: function(e){
15247         if (!this.picker().isVisible()){
15248             if (e.keyCode == 27) // allow escape to hide and re-show picker
15249                 this.show();
15250             return;
15251         }
15252
15253         e.preventDefault();
15254         
15255         switch(e.keyCode){
15256             case 27: // escape
15257                 this.hide();
15258                 break;
15259             case 37: // left
15260             case 39: // right
15261                 this.onTogglePeriod();
15262                 break;
15263             case 38: // up
15264                 this.onIncrementMinutes();
15265                 break;
15266             case 40: // down
15267                 this.onDecrementMinutes();
15268                 break;
15269             case 13: // enter
15270             case 9: // tab
15271                 this.setTime();
15272                 break;
15273         }
15274     },
15275     
15276     onClick: function(e) {
15277         e.stopPropagation();
15278         e.preventDefault();
15279     },
15280     
15281     picker : function()
15282     {
15283         return this.el.select('.datepicker', true).first();
15284     },
15285     
15286     fillTime: function()
15287     {    
15288         var time = this.pop.select('tbody', true).first();
15289         
15290         time.dom.innerHTML = '';
15291         
15292         time.createChild({
15293             tag: 'tr',
15294             cn: [
15295                 {
15296                     tag: 'td',
15297                     cn: [
15298                         {
15299                             tag: 'a',
15300                             href: '#',
15301                             cls: 'btn',
15302                             cn: [
15303                                 {
15304                                     tag: 'span',
15305                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
15306                                 }
15307                             ]
15308                         } 
15309                     ]
15310                 },
15311                 {
15312                     tag: 'td',
15313                     cls: 'separator'
15314                 },
15315                 {
15316                     tag: 'td',
15317                     cn: [
15318                         {
15319                             tag: 'a',
15320                             href: '#',
15321                             cls: 'btn',
15322                             cn: [
15323                                 {
15324                                     tag: 'span',
15325                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
15326                                 }
15327                             ]
15328                         }
15329                     ]
15330                 },
15331                 {
15332                     tag: 'td',
15333                     cls: 'separator'
15334                 }
15335             ]
15336         });
15337         
15338         time.createChild({
15339             tag: 'tr',
15340             cn: [
15341                 {
15342                     tag: 'td',
15343                     cn: [
15344                         {
15345                             tag: 'span',
15346                             cls: 'timepicker-hour',
15347                             html: '00'
15348                         }  
15349                     ]
15350                 },
15351                 {
15352                     tag: 'td',
15353                     cls: 'separator',
15354                     html: ':'
15355                 },
15356                 {
15357                     tag: 'td',
15358                     cn: [
15359                         {
15360                             tag: 'span',
15361                             cls: 'timepicker-minute',
15362                             html: '00'
15363                         }  
15364                     ]
15365                 },
15366                 {
15367                     tag: 'td',
15368                     cls: 'separator'
15369                 },
15370                 {
15371                     tag: 'td',
15372                     cn: [
15373                         {
15374                             tag: 'button',
15375                             type: 'button',
15376                             cls: 'btn btn-primary period',
15377                             html: 'AM'
15378                             
15379                         }
15380                     ]
15381                 }
15382             ]
15383         });
15384         
15385         time.createChild({
15386             tag: 'tr',
15387             cn: [
15388                 {
15389                     tag: 'td',
15390                     cn: [
15391                         {
15392                             tag: 'a',
15393                             href: '#',
15394                             cls: 'btn',
15395                             cn: [
15396                                 {
15397                                     tag: 'span',
15398                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
15399                                 }
15400                             ]
15401                         }
15402                     ]
15403                 },
15404                 {
15405                     tag: 'td',
15406                     cls: 'separator'
15407                 },
15408                 {
15409                     tag: 'td',
15410                     cn: [
15411                         {
15412                             tag: 'a',
15413                             href: '#',
15414                             cls: 'btn',
15415                             cn: [
15416                                 {
15417                                     tag: 'span',
15418                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
15419                                 }
15420                             ]
15421                         }
15422                     ]
15423                 },
15424                 {
15425                     tag: 'td',
15426                     cls: 'separator'
15427                 }
15428             ]
15429         });
15430         
15431     },
15432     
15433     update: function()
15434     {
15435         
15436         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
15437         
15438         this.fill();
15439     },
15440     
15441     fill: function() 
15442     {
15443         var hours = this.time.getHours();
15444         var minutes = this.time.getMinutes();
15445         var period = 'AM';
15446         
15447         if(hours > 11){
15448             period = 'PM';
15449         }
15450         
15451         if(hours == 0){
15452             hours = 12;
15453         }
15454         
15455         
15456         if(hours > 12){
15457             hours = hours - 12;
15458         }
15459         
15460         if(hours < 10){
15461             hours = '0' + hours;
15462         }
15463         
15464         if(minutes < 10){
15465             minutes = '0' + minutes;
15466         }
15467         
15468         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
15469         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
15470         this.pop.select('button', true).first().dom.innerHTML = period;
15471         
15472     },
15473     
15474     place: function()
15475     {   
15476         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
15477         
15478         var cls = ['bottom'];
15479         
15480         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
15481             cls.pop();
15482             cls.push('top');
15483         }
15484         
15485         cls.push('right');
15486         
15487         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
15488             cls.pop();
15489             cls.push('left');
15490         }
15491         
15492         this.picker().addClass(cls.join('-'));
15493         
15494         var _this = this;
15495         
15496         Roo.each(cls, function(c){
15497             if(c == 'bottom'){
15498                 _this.picker().setTop(_this.inputEl().getHeight());
15499                 return;
15500             }
15501             if(c == 'top'){
15502                 _this.picker().setTop(0 - _this.picker().getHeight());
15503                 return;
15504             }
15505             
15506             if(c == 'left'){
15507                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
15508                 return;
15509             }
15510             if(c == 'right'){
15511                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
15512                 return;
15513             }
15514         });
15515         
15516     },
15517   
15518     onFocus : function()
15519     {
15520         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
15521         this.show();
15522     },
15523     
15524     onBlur : function()
15525     {
15526         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
15527         this.hide();
15528     },
15529     
15530     show : function()
15531     {
15532         this.picker().show();
15533         this.pop.show();
15534         this.update();
15535         this.place();
15536         
15537         this.fireEvent('show', this, this.date);
15538     },
15539     
15540     hide : function()
15541     {
15542         this.picker().hide();
15543         this.pop.hide();
15544         
15545         this.fireEvent('hide', this, this.date);
15546     },
15547     
15548     setTime : function()
15549     {
15550         this.hide();
15551         this.setValue(this.time.format(this.format));
15552         
15553         this.fireEvent('select', this, this.date);
15554         
15555         
15556     },
15557     
15558     onMousedown: function(e){
15559         e.stopPropagation();
15560         e.preventDefault();
15561     },
15562     
15563     onIncrementHours: function()
15564     {
15565         Roo.log('onIncrementHours');
15566         this.time = this.time.add(Date.HOUR, 1);
15567         this.update();
15568         
15569     },
15570     
15571     onDecrementHours: function()
15572     {
15573         Roo.log('onDecrementHours');
15574         this.time = this.time.add(Date.HOUR, -1);
15575         this.update();
15576     },
15577     
15578     onIncrementMinutes: function()
15579     {
15580         Roo.log('onIncrementMinutes');
15581         this.time = this.time.add(Date.MINUTE, 1);
15582         this.update();
15583     },
15584     
15585     onDecrementMinutes: function()
15586     {
15587         Roo.log('onDecrementMinutes');
15588         this.time = this.time.add(Date.MINUTE, -1);
15589         this.update();
15590     },
15591     
15592     onTogglePeriod: function()
15593     {
15594         Roo.log('onTogglePeriod');
15595         this.time = this.time.add(Date.HOUR, 12);
15596         this.update();
15597     }
15598     
15599    
15600 });
15601
15602 Roo.apply(Roo.bootstrap.TimeField,  {
15603     
15604     content : {
15605         tag: 'tbody',
15606         cn: [
15607             {
15608                 tag: 'tr',
15609                 cn: [
15610                 {
15611                     tag: 'td',
15612                     colspan: '7'
15613                 }
15614                 ]
15615             }
15616         ]
15617     },
15618     
15619     footer : {
15620         tag: 'tfoot',
15621         cn: [
15622             {
15623                 tag: 'tr',
15624                 cn: [
15625                 {
15626                     tag: 'th',
15627                     colspan: '7',
15628                     cls: '',
15629                     cn: [
15630                         {
15631                             tag: 'button',
15632                             cls: 'btn btn-info ok',
15633                             html: 'OK'
15634                         }
15635                     ]
15636                 }
15637
15638                 ]
15639             }
15640         ]
15641     }
15642 });
15643
15644 Roo.apply(Roo.bootstrap.TimeField,  {
15645   
15646     template : {
15647         tag: 'div',
15648         cls: 'datepicker dropdown-menu',
15649         cn: [
15650             {
15651                 tag: 'div',
15652                 cls: 'datepicker-time',
15653                 cn: [
15654                 {
15655                     tag: 'table',
15656                     cls: 'table-condensed',
15657                     cn:[
15658                     Roo.bootstrap.TimeField.content,
15659                     Roo.bootstrap.TimeField.footer
15660                     ]
15661                 }
15662                 ]
15663             }
15664         ]
15665     }
15666 });
15667
15668  
15669
15670  /*
15671  * - LGPL
15672  *
15673  * CheckBox
15674  * 
15675  */
15676
15677 /**
15678  * @class Roo.bootstrap.CheckBox
15679  * @extends Roo.bootstrap.Input
15680  * Bootstrap CheckBox class
15681  * 
15682  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
15683  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
15684  * @cfg {String} boxLabel The text that appears beside the checkbox
15685  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
15686  * @cfg {Boolean} checked initnal the element
15687  * 
15688  * 
15689  * @constructor
15690  * Create a new CheckBox
15691  * @param {Object} config The config object
15692  */
15693
15694 Roo.bootstrap.CheckBox = function(config){
15695     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
15696    
15697         this.addEvents({
15698             /**
15699             * @event check
15700             * Fires when the element is checked or unchecked.
15701             * @param {Roo.bootstrap.CheckBox} this This input
15702             * @param {Boolean} checked The new checked value
15703             */
15704            check : true
15705         });
15706 };
15707
15708 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
15709     
15710     inputType: 'checkbox',
15711     inputValue: 1,
15712     valueOff: 0,
15713     boxLabel: false,
15714     checked: false,
15715     weight : false,
15716     
15717     getAutoCreate : function()
15718     {
15719         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
15720         
15721         var id = Roo.id();
15722         
15723         var cfg = {};
15724         
15725         cfg.cls = 'form-group checkbox' //input-group
15726         
15727         
15728         
15729         
15730         var input =  {
15731             tag: 'input',
15732             id : id,
15733             type : this.inputType,
15734             value : (!this.checked) ? this.valueOff : this.inputValue,
15735             cls : 'roo-checkbox', //'form-box',
15736             placeholder : this.placeholder || ''
15737             
15738         };
15739         
15740         if (this.weight) { // Validity check?
15741             cfg.cls += " checkbox-" + this.weight;
15742         }
15743         
15744         if (this.disabled) {
15745             input.disabled=true;
15746         }
15747         
15748         if(this.checked){
15749             input.checked = this.checked;
15750         }
15751         
15752         if (this.name) {
15753             input.name = this.name;
15754         }
15755         
15756         if (this.size) {
15757             input.cls += ' input-' + this.size;
15758         }
15759         
15760         var settings=this;
15761         ['xs','sm','md','lg'].map(function(size){
15762             if (settings[size]) {
15763                 cfg.cls += ' col-' + size + '-' + settings[size];
15764             }
15765         });
15766         
15767        
15768         
15769         var inputblock = input;
15770         
15771         
15772         
15773         
15774         if (this.before || this.after) {
15775             
15776             inputblock = {
15777                 cls : 'input-group',
15778                 cn :  [] 
15779             };
15780             if (this.before) {
15781                 inputblock.cn.push({
15782                     tag :'span',
15783                     cls : 'input-group-addon',
15784                     html : this.before
15785                 });
15786             }
15787             inputblock.cn.push(input);
15788             if (this.after) {
15789                 inputblock.cn.push({
15790                     tag :'span',
15791                     cls : 'input-group-addon',
15792                     html : this.after
15793                 });
15794             }
15795             
15796         };
15797         
15798         if (align ==='left' && this.fieldLabel.length) {
15799                 Roo.log("left and has label");
15800                 cfg.cn = [
15801                     
15802                     {
15803                         tag: 'label',
15804                         'for' :  id,
15805                         cls : 'control-label col-md-' + this.labelWidth,
15806                         html : this.fieldLabel
15807                         
15808                     },
15809                     {
15810                         cls : "col-md-" + (12 - this.labelWidth), 
15811                         cn: [
15812                             inputblock
15813                         ]
15814                     }
15815                     
15816                 ];
15817         } else if ( this.fieldLabel.length) {
15818                 Roo.log(" label");
15819                 cfg.cn = [
15820                    
15821                     {
15822                         tag: this.boxLabel ? 'span' : 'label',
15823                         'for': id,
15824                         cls: 'control-label box-input-label',
15825                         //cls : 'input-group-addon',
15826                         html : this.fieldLabel
15827                         
15828                     },
15829                     
15830                     inputblock
15831                     
15832                 ];
15833
15834         } else {
15835             
15836                 Roo.log(" no label && no align");
15837                 cfg.cn = [  inputblock ] ;
15838                 
15839                 
15840         };
15841          if(this.boxLabel){
15842             cfg.cn.push( {
15843                 tag: 'label',
15844                 'for': id,
15845                 cls: 'box-label',
15846                 html: this.boxLabel
15847                 
15848             });
15849         }
15850         
15851         
15852        
15853         return cfg;
15854         
15855     },
15856     
15857     /**
15858      * return the real input element.
15859      */
15860     inputEl: function ()
15861     {
15862         return this.el.select('input.roo-checkbox',true).first();
15863     },
15864     
15865     label: function()
15866     {
15867         return this.el.select('label.control-label',true).first();
15868     },
15869     
15870     initEvents : function()
15871     {
15872 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
15873         
15874         this.inputEl().on('click', this.onClick,  this);
15875         
15876     },
15877     
15878     onClick : function()
15879     {   
15880         this.setChecked(!this.checked);
15881     },
15882     
15883     setChecked : function(state,suppressEvent)
15884     {
15885         this.checked = state;
15886         
15887         this.inputEl().dom.checked = state;
15888         
15889         if(suppressEvent !== true){
15890             this.fireEvent('check', this, state);
15891         }
15892         
15893         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
15894         
15895     },
15896     
15897     setValue : function(v,suppressEvent)
15898     {
15899         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
15900     }
15901     
15902 });
15903
15904  
15905 /*
15906  * - LGPL
15907  *
15908  * Radio
15909  * 
15910  */
15911
15912 /**
15913  * @class Roo.bootstrap.Radio
15914  * @extends Roo.bootstrap.CheckBox
15915  * Bootstrap Radio class
15916
15917  * @constructor
15918  * Create a new Radio
15919  * @param {Object} config The config object
15920  */
15921
15922 Roo.bootstrap.Radio = function(config){
15923     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
15924    
15925 };
15926
15927 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
15928     
15929     inputType: 'radio',
15930     inputValue: '',
15931     valueOff: '',
15932     
15933     getAutoCreate : function()
15934     {
15935         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
15936         
15937         var id = Roo.id();
15938         
15939         var cfg = {};
15940         
15941         cfg.cls = 'form-group radio' //input-group
15942         
15943         var input =  {
15944             tag: 'input',
15945             id : id,
15946             type : this.inputType,
15947             value : (!this.checked) ? this.valueOff : this.inputValue,
15948             cls : 'roo-radio',
15949             placeholder : this.placeholder || ''
15950             
15951         };
15952           if (this.weight) { // Validity check?
15953             cfg.cls += " radio-" + this.weight;
15954         }
15955         if (this.disabled) {
15956             input.disabled=true;
15957         }
15958         
15959         if(this.checked){
15960             input.checked = this.checked;
15961         }
15962         
15963         if (this.name) {
15964             input.name = this.name;
15965         }
15966         
15967         if (this.size) {
15968             input.cls += ' input-' + this.size;
15969         }
15970         
15971         var settings=this;
15972         ['xs','sm','md','lg'].map(function(size){
15973             if (settings[size]) {
15974                 cfg.cls += ' col-' + size + '-' + settings[size];
15975             }
15976         });
15977         
15978         var inputblock = input;
15979         
15980         if (this.before || this.after) {
15981             
15982             inputblock = {
15983                 cls : 'input-group',
15984                 cn :  [] 
15985             };
15986             if (this.before) {
15987                 inputblock.cn.push({
15988                     tag :'span',
15989                     cls : 'input-group-addon',
15990                     html : this.before
15991                 });
15992             }
15993             inputblock.cn.push(input);
15994             if (this.after) {
15995                 inputblock.cn.push({
15996                     tag :'span',
15997                     cls : 'input-group-addon',
15998                     html : this.after
15999                 });
16000             }
16001             
16002         };
16003         
16004         if (align ==='left' && this.fieldLabel.length) {
16005                 Roo.log("left and has label");
16006                 cfg.cn = [
16007                     
16008                     {
16009                         tag: 'label',
16010                         'for' :  id,
16011                         cls : 'control-label col-md-' + this.labelWidth,
16012                         html : this.fieldLabel
16013                         
16014                     },
16015                     {
16016                         cls : "col-md-" + (12 - this.labelWidth), 
16017                         cn: [
16018                             inputblock
16019                         ]
16020                     }
16021                     
16022                 ];
16023         } else if ( this.fieldLabel.length) {
16024                 Roo.log(" label");
16025                  cfg.cn = [
16026                    
16027                     {
16028                         tag: 'label',
16029                         'for': id,
16030                         cls: 'control-label box-input-label',
16031                         //cls : 'input-group-addon',
16032                         html : this.fieldLabel
16033                         
16034                     },
16035                     
16036                     inputblock
16037                     
16038                 ];
16039
16040         } else {
16041             
16042                    Roo.log(" no label && no align");
16043                 cfg.cn = [
16044                     
16045                         inputblock
16046                     
16047                 ];
16048                 
16049                 
16050         };
16051         
16052         if(this.boxLabel){
16053             cfg.cn.push({
16054                 tag: 'label',
16055                 'for': id,
16056                 cls: 'box-label',
16057                 html: this.boxLabel
16058             })
16059         }
16060         
16061         return cfg;
16062         
16063     },
16064     inputEl: function ()
16065     {
16066         return this.el.select('input.roo-radio',true).first();
16067     },
16068     onClick : function()
16069     {   
16070         this.setChecked(true);
16071     },
16072     
16073     setChecked : function(state,suppressEvent)
16074     {
16075         if(state){
16076             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
16077                 v.dom.checked = false;
16078             });
16079         }
16080         
16081         this.checked = state;
16082         this.inputEl().dom.checked = state;
16083         
16084         if(suppressEvent !== true){
16085             this.fireEvent('check', this, state);
16086         }
16087         
16088         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
16089         
16090     },
16091     
16092     getGroupValue : function()
16093     {
16094         var value = ''
16095         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
16096             if(v.dom.checked == true){
16097                 value = v.dom.value;
16098             }
16099         });
16100         
16101         return value;
16102     },
16103     
16104     /**
16105      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
16106      * @return {Mixed} value The field value
16107      */
16108     getValue : function(){
16109         return this.getGroupValue();
16110     }
16111     
16112 });
16113
16114  
16115 //<script type="text/javascript">
16116
16117 /*
16118  * Based  Ext JS Library 1.1.1
16119  * Copyright(c) 2006-2007, Ext JS, LLC.
16120  * LGPL
16121  *
16122  */
16123  
16124 /**
16125  * @class Roo.HtmlEditorCore
16126  * @extends Roo.Component
16127  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
16128  *
16129  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
16130  */
16131
16132 Roo.HtmlEditorCore = function(config){
16133     
16134     
16135     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
16136     this.addEvents({
16137         /**
16138          * @event initialize
16139          * Fires when the editor is fully initialized (including the iframe)
16140          * @param {Roo.HtmlEditorCore} this
16141          */
16142         initialize: true,
16143         /**
16144          * @event activate
16145          * Fires when the editor is first receives the focus. Any insertion must wait
16146          * until after this event.
16147          * @param {Roo.HtmlEditorCore} this
16148          */
16149         activate: true,
16150          /**
16151          * @event beforesync
16152          * Fires before the textarea is updated with content from the editor iframe. Return false
16153          * to cancel the sync.
16154          * @param {Roo.HtmlEditorCore} this
16155          * @param {String} html
16156          */
16157         beforesync: true,
16158          /**
16159          * @event beforepush
16160          * Fires before the iframe editor is updated with content from the textarea. Return false
16161          * to cancel the push.
16162          * @param {Roo.HtmlEditorCore} this
16163          * @param {String} html
16164          */
16165         beforepush: true,
16166          /**
16167          * @event sync
16168          * Fires when the textarea is updated with content from the editor iframe.
16169          * @param {Roo.HtmlEditorCore} this
16170          * @param {String} html
16171          */
16172         sync: true,
16173          /**
16174          * @event push
16175          * Fires when the iframe editor is updated with content from the textarea.
16176          * @param {Roo.HtmlEditorCore} this
16177          * @param {String} html
16178          */
16179         push: true,
16180         
16181         /**
16182          * @event editorevent
16183          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
16184          * @param {Roo.HtmlEditorCore} this
16185          */
16186         editorevent: true
16187     });
16188      
16189 };
16190
16191
16192 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
16193
16194
16195      /**
16196      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
16197      */
16198     
16199     owner : false,
16200     
16201      /**
16202      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
16203      *                        Roo.resizable.
16204      */
16205     resizable : false,
16206      /**
16207      * @cfg {Number} height (in pixels)
16208      */   
16209     height: 300,
16210    /**
16211      * @cfg {Number} width (in pixels)
16212      */   
16213     width: 500,
16214     
16215     /**
16216      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
16217      * 
16218      */
16219     stylesheets: false,
16220     
16221     // id of frame..
16222     frameId: false,
16223     
16224     // private properties
16225     validationEvent : false,
16226     deferHeight: true,
16227     initialized : false,
16228     activated : false,
16229     sourceEditMode : false,
16230     onFocus : Roo.emptyFn,
16231     iframePad:3,
16232     hideMode:'offsets',
16233     
16234     clearUp: true,
16235     
16236      
16237     
16238
16239     /**
16240      * Protected method that will not generally be called directly. It
16241      * is called when the editor initializes the iframe with HTML contents. Override this method if you
16242      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
16243      */
16244     getDocMarkup : function(){
16245         // body styles..
16246         var st = '';
16247         Roo.log(this.stylesheets);
16248         
16249         // inherit styels from page...?? 
16250         if (this.stylesheets === false) {
16251             
16252             Roo.get(document.head).select('style').each(function(node) {
16253                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16254             });
16255             
16256             Roo.get(document.head).select('link').each(function(node) { 
16257                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16258             });
16259             
16260         } else if (!this.stylesheets.length) {
16261                 // simple..
16262                 st = '<style type="text/css">' +
16263                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16264                    '</style>';
16265         } else {
16266             Roo.each(this.stylesheets, function(s) {
16267                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
16268             });
16269             
16270         }
16271         
16272         st +=  '<style type="text/css">' +
16273             'IMG { cursor: pointer } ' +
16274         '</style>';
16275
16276         
16277         return '<html><head>' + st  +
16278             //<style type="text/css">' +
16279             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16280             //'</style>' +
16281             ' </head><body class="roo-htmleditor-body"></body></html>';
16282     },
16283
16284     // private
16285     onRender : function(ct, position)
16286     {
16287         var _t = this;
16288         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
16289         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
16290         
16291         
16292         this.el.dom.style.border = '0 none';
16293         this.el.dom.setAttribute('tabIndex', -1);
16294         this.el.addClass('x-hidden hide');
16295         
16296         
16297         
16298         if(Roo.isIE){ // fix IE 1px bogus margin
16299             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
16300         }
16301        
16302         
16303         this.frameId = Roo.id();
16304         
16305          
16306         
16307         var iframe = this.owner.wrap.createChild({
16308             tag: 'iframe',
16309             cls: 'form-control', // bootstrap..
16310             id: this.frameId,
16311             name: this.frameId,
16312             frameBorder : 'no',
16313             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
16314         }, this.el
16315         );
16316         
16317         
16318         this.iframe = iframe.dom;
16319
16320          this.assignDocWin();
16321         
16322         this.doc.designMode = 'on';
16323        
16324         this.doc.open();
16325         this.doc.write(this.getDocMarkup());
16326         this.doc.close();
16327
16328         
16329         var task = { // must defer to wait for browser to be ready
16330             run : function(){
16331                 //console.log("run task?" + this.doc.readyState);
16332                 this.assignDocWin();
16333                 if(this.doc.body || this.doc.readyState == 'complete'){
16334                     try {
16335                         this.doc.designMode="on";
16336                     } catch (e) {
16337                         return;
16338                     }
16339                     Roo.TaskMgr.stop(task);
16340                     this.initEditor.defer(10, this);
16341                 }
16342             },
16343             interval : 10,
16344             duration: 10000,
16345             scope: this
16346         };
16347         Roo.TaskMgr.start(task);
16348
16349         
16350          
16351     },
16352
16353     // private
16354     onResize : function(w, h)
16355     {
16356          Roo.log('resize: ' +w + ',' + h );
16357         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
16358         if(!this.iframe){
16359             return;
16360         }
16361         if(typeof w == 'number'){
16362             
16363             this.iframe.style.width = w + 'px';
16364         }
16365         if(typeof h == 'number'){
16366             
16367             this.iframe.style.height = h + 'px';
16368             if(this.doc){
16369                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
16370             }
16371         }
16372         
16373     },
16374
16375     /**
16376      * Toggles the editor between standard and source edit mode.
16377      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
16378      */
16379     toggleSourceEdit : function(sourceEditMode){
16380         
16381         this.sourceEditMode = sourceEditMode === true;
16382         
16383         if(this.sourceEditMode){
16384  
16385             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
16386             
16387         }else{
16388             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
16389             //this.iframe.className = '';
16390             this.deferFocus();
16391         }
16392         //this.setSize(this.owner.wrap.getSize());
16393         //this.fireEvent('editmodechange', this, this.sourceEditMode);
16394     },
16395
16396     
16397   
16398
16399     /**
16400      * Protected method that will not generally be called directly. If you need/want
16401      * custom HTML cleanup, this is the method you should override.
16402      * @param {String} html The HTML to be cleaned
16403      * return {String} The cleaned HTML
16404      */
16405     cleanHtml : function(html){
16406         html = String(html);
16407         if(html.length > 5){
16408             if(Roo.isSafari){ // strip safari nonsense
16409                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
16410             }
16411         }
16412         if(html == '&nbsp;'){
16413             html = '';
16414         }
16415         return html;
16416     },
16417
16418     /**
16419      * HTML Editor -> Textarea
16420      * Protected method that will not generally be called directly. Syncs the contents
16421      * of the editor iframe with the textarea.
16422      */
16423     syncValue : function(){
16424         if(this.initialized){
16425             var bd = (this.doc.body || this.doc.documentElement);
16426             //this.cleanUpPaste(); -- this is done else where and causes havoc..
16427             var html = bd.innerHTML;
16428             if(Roo.isSafari){
16429                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
16430                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
16431                 if(m && m[1]){
16432                     html = '<div style="'+m[0]+'">' + html + '</div>';
16433                 }
16434             }
16435             html = this.cleanHtml(html);
16436             // fix up the special chars.. normaly like back quotes in word...
16437             // however we do not want to do this with chinese..
16438             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
16439                 var cc = b.charCodeAt();
16440                 if (
16441                     (cc >= 0x4E00 && cc < 0xA000 ) ||
16442                     (cc >= 0x3400 && cc < 0x4E00 ) ||
16443                     (cc >= 0xf900 && cc < 0xfb00 )
16444                 ) {
16445                         return b;
16446                 }
16447                 return "&#"+cc+";" 
16448             });
16449             if(this.owner.fireEvent('beforesync', this, html) !== false){
16450                 this.el.dom.value = html;
16451                 this.owner.fireEvent('sync', this, html);
16452             }
16453         }
16454     },
16455
16456     /**
16457      * Protected method that will not generally be called directly. Pushes the value of the textarea
16458      * into the iframe editor.
16459      */
16460     pushValue : function(){
16461         if(this.initialized){
16462             var v = this.el.dom.value.trim();
16463             
16464 //            if(v.length < 1){
16465 //                v = '&#160;';
16466 //            }
16467             
16468             if(this.owner.fireEvent('beforepush', this, v) !== false){
16469                 var d = (this.doc.body || this.doc.documentElement);
16470                 d.innerHTML = v;
16471                 this.cleanUpPaste();
16472                 this.el.dom.value = d.innerHTML;
16473                 this.owner.fireEvent('push', this, v);
16474             }
16475         }
16476     },
16477
16478     // private
16479     deferFocus : function(){
16480         this.focus.defer(10, this);
16481     },
16482
16483     // doc'ed in Field
16484     focus : function(){
16485         if(this.win && !this.sourceEditMode){
16486             this.win.focus();
16487         }else{
16488             this.el.focus();
16489         }
16490     },
16491     
16492     assignDocWin: function()
16493     {
16494         var iframe = this.iframe;
16495         
16496          if(Roo.isIE){
16497             this.doc = iframe.contentWindow.document;
16498             this.win = iframe.contentWindow;
16499         } else {
16500             if (!Roo.get(this.frameId)) {
16501                 return;
16502             }
16503             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
16504             this.win = Roo.get(this.frameId).dom.contentWindow;
16505         }
16506     },
16507     
16508     // private
16509     initEditor : function(){
16510         //console.log("INIT EDITOR");
16511         this.assignDocWin();
16512         
16513         
16514         
16515         this.doc.designMode="on";
16516         this.doc.open();
16517         this.doc.write(this.getDocMarkup());
16518         this.doc.close();
16519         
16520         var dbody = (this.doc.body || this.doc.documentElement);
16521         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
16522         // this copies styles from the containing element into thsi one..
16523         // not sure why we need all of this..
16524         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
16525         
16526         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
16527         //ss['background-attachment'] = 'fixed'; // w3c
16528         dbody.bgProperties = 'fixed'; // ie
16529         //Roo.DomHelper.applyStyles(dbody, ss);
16530         Roo.EventManager.on(this.doc, {
16531             //'mousedown': this.onEditorEvent,
16532             'mouseup': this.onEditorEvent,
16533             'dblclick': this.onEditorEvent,
16534             'click': this.onEditorEvent,
16535             'keyup': this.onEditorEvent,
16536             buffer:100,
16537             scope: this
16538         });
16539         if(Roo.isGecko){
16540             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
16541         }
16542         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
16543             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
16544         }
16545         this.initialized = true;
16546
16547         this.owner.fireEvent('initialize', this);
16548         this.pushValue();
16549     },
16550
16551     // private
16552     onDestroy : function(){
16553         
16554         
16555         
16556         if(this.rendered){
16557             
16558             //for (var i =0; i < this.toolbars.length;i++) {
16559             //    // fixme - ask toolbars for heights?
16560             //    this.toolbars[i].onDestroy();
16561            // }
16562             
16563             //this.wrap.dom.innerHTML = '';
16564             //this.wrap.remove();
16565         }
16566     },
16567
16568     // private
16569     onFirstFocus : function(){
16570         
16571         this.assignDocWin();
16572         
16573         
16574         this.activated = true;
16575          
16576     
16577         if(Roo.isGecko){ // prevent silly gecko errors
16578             this.win.focus();
16579             var s = this.win.getSelection();
16580             if(!s.focusNode || s.focusNode.nodeType != 3){
16581                 var r = s.getRangeAt(0);
16582                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
16583                 r.collapse(true);
16584                 this.deferFocus();
16585             }
16586             try{
16587                 this.execCmd('useCSS', true);
16588                 this.execCmd('styleWithCSS', false);
16589             }catch(e){}
16590         }
16591         this.owner.fireEvent('activate', this);
16592     },
16593
16594     // private
16595     adjustFont: function(btn){
16596         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
16597         //if(Roo.isSafari){ // safari
16598         //    adjust *= 2;
16599        // }
16600         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
16601         if(Roo.isSafari){ // safari
16602             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
16603             v =  (v < 10) ? 10 : v;
16604             v =  (v > 48) ? 48 : v;
16605             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
16606             
16607         }
16608         
16609         
16610         v = Math.max(1, v+adjust);
16611         
16612         this.execCmd('FontSize', v  );
16613     },
16614
16615     onEditorEvent : function(e){
16616         this.owner.fireEvent('editorevent', this, e);
16617       //  this.updateToolbar();
16618         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
16619     },
16620
16621     insertTag : function(tg)
16622     {
16623         // could be a bit smarter... -> wrap the current selected tRoo..
16624         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
16625             
16626             range = this.createRange(this.getSelection());
16627             var wrappingNode = this.doc.createElement(tg.toLowerCase());
16628             wrappingNode.appendChild(range.extractContents());
16629             range.insertNode(wrappingNode);
16630
16631             return;
16632             
16633             
16634             
16635         }
16636         this.execCmd("formatblock",   tg);
16637         
16638     },
16639     
16640     insertText : function(txt)
16641     {
16642         
16643         
16644         var range = this.createRange();
16645         range.deleteContents();
16646                //alert(Sender.getAttribute('label'));
16647                
16648         range.insertNode(this.doc.createTextNode(txt));
16649     } ,
16650     
16651      
16652
16653     /**
16654      * Executes a Midas editor command on the editor document and performs necessary focus and
16655      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
16656      * @param {String} cmd The Midas command
16657      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16658      */
16659     relayCmd : function(cmd, value){
16660         this.win.focus();
16661         this.execCmd(cmd, value);
16662         this.owner.fireEvent('editorevent', this);
16663         //this.updateToolbar();
16664         this.owner.deferFocus();
16665     },
16666
16667     /**
16668      * Executes a Midas editor command directly on the editor document.
16669      * For visual commands, you should use {@link #relayCmd} instead.
16670      * <b>This should only be called after the editor is initialized.</b>
16671      * @param {String} cmd The Midas command
16672      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16673      */
16674     execCmd : function(cmd, value){
16675         this.doc.execCommand(cmd, false, value === undefined ? null : value);
16676         this.syncValue();
16677     },
16678  
16679  
16680    
16681     /**
16682      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
16683      * to insert tRoo.
16684      * @param {String} text | dom node.. 
16685      */
16686     insertAtCursor : function(text)
16687     {
16688         
16689         
16690         
16691         if(!this.activated){
16692             return;
16693         }
16694         /*
16695         if(Roo.isIE){
16696             this.win.focus();
16697             var r = this.doc.selection.createRange();
16698             if(r){
16699                 r.collapse(true);
16700                 r.pasteHTML(text);
16701                 this.syncValue();
16702                 this.deferFocus();
16703             
16704             }
16705             return;
16706         }
16707         */
16708         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
16709             this.win.focus();
16710             
16711             
16712             // from jquery ui (MIT licenced)
16713             var range, node;
16714             var win = this.win;
16715             
16716             if (win.getSelection && win.getSelection().getRangeAt) {
16717                 range = win.getSelection().getRangeAt(0);
16718                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
16719                 range.insertNode(node);
16720             } else if (win.document.selection && win.document.selection.createRange) {
16721                 // no firefox support
16722                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
16723                 win.document.selection.createRange().pasteHTML(txt);
16724             } else {
16725                 // no firefox support
16726                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
16727                 this.execCmd('InsertHTML', txt);
16728             } 
16729             
16730             this.syncValue();
16731             
16732             this.deferFocus();
16733         }
16734     },
16735  // private
16736     mozKeyPress : function(e){
16737         if(e.ctrlKey){
16738             var c = e.getCharCode(), cmd;
16739           
16740             if(c > 0){
16741                 c = String.fromCharCode(c).toLowerCase();
16742                 switch(c){
16743                     case 'b':
16744                         cmd = 'bold';
16745                         break;
16746                     case 'i':
16747                         cmd = 'italic';
16748                         break;
16749                     
16750                     case 'u':
16751                         cmd = 'underline';
16752                         break;
16753                     
16754                     case 'v':
16755                         this.cleanUpPaste.defer(100, this);
16756                         return;
16757                         
16758                 }
16759                 if(cmd){
16760                     this.win.focus();
16761                     this.execCmd(cmd);
16762                     this.deferFocus();
16763                     e.preventDefault();
16764                 }
16765                 
16766             }
16767         }
16768     },
16769
16770     // private
16771     fixKeys : function(){ // load time branching for fastest keydown performance
16772         if(Roo.isIE){
16773             return function(e){
16774                 var k = e.getKey(), r;
16775                 if(k == e.TAB){
16776                     e.stopEvent();
16777                     r = this.doc.selection.createRange();
16778                     if(r){
16779                         r.collapse(true);
16780                         r.pasteHTML('&#160;&#160;&#160;&#160;');
16781                         this.deferFocus();
16782                     }
16783                     return;
16784                 }
16785                 
16786                 if(k == e.ENTER){
16787                     r = this.doc.selection.createRange();
16788                     if(r){
16789                         var target = r.parentElement();
16790                         if(!target || target.tagName.toLowerCase() != 'li'){
16791                             e.stopEvent();
16792                             r.pasteHTML('<br />');
16793                             r.collapse(false);
16794                             r.select();
16795                         }
16796                     }
16797                 }
16798                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
16799                     this.cleanUpPaste.defer(100, this);
16800                     return;
16801                 }
16802                 
16803                 
16804             };
16805         }else if(Roo.isOpera){
16806             return function(e){
16807                 var k = e.getKey();
16808                 if(k == e.TAB){
16809                     e.stopEvent();
16810                     this.win.focus();
16811                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
16812                     this.deferFocus();
16813                 }
16814                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
16815                     this.cleanUpPaste.defer(100, this);
16816                     return;
16817                 }
16818                 
16819             };
16820         }else if(Roo.isSafari){
16821             return function(e){
16822                 var k = e.getKey();
16823                 
16824                 if(k == e.TAB){
16825                     e.stopEvent();
16826                     this.execCmd('InsertText','\t');
16827                     this.deferFocus();
16828                     return;
16829                 }
16830                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
16831                     this.cleanUpPaste.defer(100, this);
16832                     return;
16833                 }
16834                 
16835              };
16836         }
16837     }(),
16838     
16839     getAllAncestors: function()
16840     {
16841         var p = this.getSelectedNode();
16842         var a = [];
16843         if (!p) {
16844             a.push(p); // push blank onto stack..
16845             p = this.getParentElement();
16846         }
16847         
16848         
16849         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
16850             a.push(p);
16851             p = p.parentNode;
16852         }
16853         a.push(this.doc.body);
16854         return a;
16855     },
16856     lastSel : false,
16857     lastSelNode : false,
16858     
16859     
16860     getSelection : function() 
16861     {
16862         this.assignDocWin();
16863         return Roo.isIE ? this.doc.selection : this.win.getSelection();
16864     },
16865     
16866     getSelectedNode: function() 
16867     {
16868         // this may only work on Gecko!!!
16869         
16870         // should we cache this!!!!
16871         
16872         
16873         
16874          
16875         var range = this.createRange(this.getSelection()).cloneRange();
16876         
16877         if (Roo.isIE) {
16878             var parent = range.parentElement();
16879             while (true) {
16880                 var testRange = range.duplicate();
16881                 testRange.moveToElementText(parent);
16882                 if (testRange.inRange(range)) {
16883                     break;
16884                 }
16885                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
16886                     break;
16887                 }
16888                 parent = parent.parentElement;
16889             }
16890             return parent;
16891         }
16892         
16893         // is ancestor a text element.
16894         var ac =  range.commonAncestorContainer;
16895         if (ac.nodeType == 3) {
16896             ac = ac.parentNode;
16897         }
16898         
16899         var ar = ac.childNodes;
16900          
16901         var nodes = [];
16902         var other_nodes = [];
16903         var has_other_nodes = false;
16904         for (var i=0;i<ar.length;i++) {
16905             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
16906                 continue;
16907             }
16908             // fullly contained node.
16909             
16910             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
16911                 nodes.push(ar[i]);
16912                 continue;
16913             }
16914             
16915             // probably selected..
16916             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
16917                 other_nodes.push(ar[i]);
16918                 continue;
16919             }
16920             // outer..
16921             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
16922                 continue;
16923             }
16924             
16925             
16926             has_other_nodes = true;
16927         }
16928         if (!nodes.length && other_nodes.length) {
16929             nodes= other_nodes;
16930         }
16931         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
16932             return false;
16933         }
16934         
16935         return nodes[0];
16936     },
16937     createRange: function(sel)
16938     {
16939         // this has strange effects when using with 
16940         // top toolbar - not sure if it's a great idea.
16941         //this.editor.contentWindow.focus();
16942         if (typeof sel != "undefined") {
16943             try {
16944                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
16945             } catch(e) {
16946                 return this.doc.createRange();
16947             }
16948         } else {
16949             return this.doc.createRange();
16950         }
16951     },
16952     getParentElement: function()
16953     {
16954         
16955         this.assignDocWin();
16956         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
16957         
16958         var range = this.createRange(sel);
16959          
16960         try {
16961             var p = range.commonAncestorContainer;
16962             while (p.nodeType == 3) { // text node
16963                 p = p.parentNode;
16964             }
16965             return p;
16966         } catch (e) {
16967             return null;
16968         }
16969     
16970     },
16971     /***
16972      *
16973      * Range intersection.. the hard stuff...
16974      *  '-1' = before
16975      *  '0' = hits..
16976      *  '1' = after.
16977      *         [ -- selected range --- ]
16978      *   [fail]                        [fail]
16979      *
16980      *    basically..
16981      *      if end is before start or  hits it. fail.
16982      *      if start is after end or hits it fail.
16983      *
16984      *   if either hits (but other is outside. - then it's not 
16985      *   
16986      *    
16987      **/
16988     
16989     
16990     // @see http://www.thismuchiknow.co.uk/?p=64.
16991     rangeIntersectsNode : function(range, node)
16992     {
16993         var nodeRange = node.ownerDocument.createRange();
16994         try {
16995             nodeRange.selectNode(node);
16996         } catch (e) {
16997             nodeRange.selectNodeContents(node);
16998         }
16999     
17000         var rangeStartRange = range.cloneRange();
17001         rangeStartRange.collapse(true);
17002     
17003         var rangeEndRange = range.cloneRange();
17004         rangeEndRange.collapse(false);
17005     
17006         var nodeStartRange = nodeRange.cloneRange();
17007         nodeStartRange.collapse(true);
17008     
17009         var nodeEndRange = nodeRange.cloneRange();
17010         nodeEndRange.collapse(false);
17011     
17012         return rangeStartRange.compareBoundaryPoints(
17013                  Range.START_TO_START, nodeEndRange) == -1 &&
17014                rangeEndRange.compareBoundaryPoints(
17015                  Range.START_TO_START, nodeStartRange) == 1;
17016         
17017          
17018     },
17019     rangeCompareNode : function(range, node)
17020     {
17021         var nodeRange = node.ownerDocument.createRange();
17022         try {
17023             nodeRange.selectNode(node);
17024         } catch (e) {
17025             nodeRange.selectNodeContents(node);
17026         }
17027         
17028         
17029         range.collapse(true);
17030     
17031         nodeRange.collapse(true);
17032      
17033         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
17034         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
17035          
17036         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
17037         
17038         var nodeIsBefore   =  ss == 1;
17039         var nodeIsAfter    = ee == -1;
17040         
17041         if (nodeIsBefore && nodeIsAfter)
17042             return 0; // outer
17043         if (!nodeIsBefore && nodeIsAfter)
17044             return 1; //right trailed.
17045         
17046         if (nodeIsBefore && !nodeIsAfter)
17047             return 2;  // left trailed.
17048         // fully contined.
17049         return 3;
17050     },
17051
17052     // private? - in a new class?
17053     cleanUpPaste :  function()
17054     {
17055         // cleans up the whole document..
17056         Roo.log('cleanuppaste');
17057         
17058         this.cleanUpChildren(this.doc.body);
17059         var clean = this.cleanWordChars(this.doc.body.innerHTML);
17060         if (clean != this.doc.body.innerHTML) {
17061             this.doc.body.innerHTML = clean;
17062         }
17063         
17064     },
17065     
17066     cleanWordChars : function(input) {// change the chars to hex code
17067         var he = Roo.HtmlEditorCore;
17068         
17069         var output = input;
17070         Roo.each(he.swapCodes, function(sw) { 
17071             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
17072             
17073             output = output.replace(swapper, sw[1]);
17074         });
17075         
17076         return output;
17077     },
17078     
17079     
17080     cleanUpChildren : function (n)
17081     {
17082         if (!n.childNodes.length) {
17083             return;
17084         }
17085         for (var i = n.childNodes.length-1; i > -1 ; i--) {
17086            this.cleanUpChild(n.childNodes[i]);
17087         }
17088     },
17089     
17090     
17091         
17092     
17093     cleanUpChild : function (node)
17094     {
17095         var ed = this;
17096         //console.log(node);
17097         if (node.nodeName == "#text") {
17098             // clean up silly Windows -- stuff?
17099             return; 
17100         }
17101         if (node.nodeName == "#comment") {
17102             node.parentNode.removeChild(node);
17103             // clean up silly Windows -- stuff?
17104             return; 
17105         }
17106         
17107         if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1 && this.clearUp) {
17108             // remove node.
17109             node.parentNode.removeChild(node);
17110             return;
17111             
17112         }
17113         
17114         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
17115         
17116         // remove <a name=....> as rendering on yahoo mailer is borked with this.
17117         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
17118         
17119         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
17120         //    remove_keep_children = true;
17121         //}
17122         
17123         if (remove_keep_children) {
17124             this.cleanUpChildren(node);
17125             // inserts everything just before this node...
17126             while (node.childNodes.length) {
17127                 var cn = node.childNodes[0];
17128                 node.removeChild(cn);
17129                 node.parentNode.insertBefore(cn, node);
17130             }
17131             node.parentNode.removeChild(node);
17132             return;
17133         }
17134         
17135         if (!node.attributes || !node.attributes.length) {
17136             this.cleanUpChildren(node);
17137             return;
17138         }
17139         
17140         function cleanAttr(n,v)
17141         {
17142             
17143             if (v.match(/^\./) || v.match(/^\//)) {
17144                 return;
17145             }
17146             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
17147                 return;
17148             }
17149             if (v.match(/^#/)) {
17150                 return;
17151             }
17152 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
17153             node.removeAttribute(n);
17154             
17155         }
17156         
17157         function cleanStyle(n,v)
17158         {
17159             if (v.match(/expression/)) { //XSS?? should we even bother..
17160                 node.removeAttribute(n);
17161                 return;
17162             }
17163             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
17164             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
17165             
17166             
17167             var parts = v.split(/;/);
17168             var clean = [];
17169             
17170             Roo.each(parts, function(p) {
17171                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
17172                 if (!p.length) {
17173                     return true;
17174                 }
17175                 var l = p.split(':').shift().replace(/\s+/g,'');
17176                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
17177                 
17178                 if ( cblack.indexOf(l) > -1) {
17179 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17180                     //node.removeAttribute(n);
17181                     return true;
17182                 }
17183                 //Roo.log()
17184                 // only allow 'c whitelisted system attributes'
17185                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
17186 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17187                     //node.removeAttribute(n);
17188                     return true;
17189                 }
17190                 
17191                 
17192                  
17193                 
17194                 clean.push(p);
17195                 return true;
17196             });
17197             if (clean.length) { 
17198                 node.setAttribute(n, clean.join(';'));
17199             } else {
17200                 node.removeAttribute(n);
17201             }
17202             
17203         }
17204         
17205         
17206         for (var i = node.attributes.length-1; i > -1 ; i--) {
17207             var a = node.attributes[i];
17208             //console.log(a);
17209             
17210             if (a.name.toLowerCase().substr(0,2)=='on')  {
17211                 node.removeAttribute(a.name);
17212                 continue;
17213             }
17214             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
17215                 node.removeAttribute(a.name);
17216                 continue;
17217             }
17218             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
17219                 cleanAttr(a.name,a.value); // fixme..
17220                 continue;
17221             }
17222             if (a.name == 'style') {
17223                 cleanStyle(a.name,a.value);
17224                 continue;
17225             }
17226             /// clean up MS crap..
17227             // tecnically this should be a list of valid class'es..
17228             
17229             
17230             if (a.name == 'class') {
17231                 if (a.value.match(/^Mso/)) {
17232                     node.className = '';
17233                 }
17234                 
17235                 if (a.value.match(/body/)) {
17236                     node.className = '';
17237                 }
17238                 continue;
17239             }
17240             
17241             // style cleanup!?
17242             // class cleanup?
17243             
17244         }
17245         
17246         
17247         this.cleanUpChildren(node);
17248         
17249         
17250     },
17251     /**
17252      * Clean up MS wordisms...
17253      */
17254     cleanWord : function(node)
17255     {
17256         var _t = this;
17257         var cleanWordChildren = function()
17258         {
17259             if (!node.childNodes.length) {
17260                 return;
17261             }
17262             for (var i = node.childNodes.length-1; i > -1 ; i--) {
17263                _t.cleanWord(node.childNodes[i]);
17264             }
17265         }
17266         
17267         
17268         if (!node) {
17269             this.cleanWord(this.doc.body);
17270             return;
17271         }
17272         if (node.nodeName == "#text") {
17273             // clean up silly Windows -- stuff?
17274             return; 
17275         }
17276         if (node.nodeName == "#comment") {
17277             node.parentNode.removeChild(node);
17278             // clean up silly Windows -- stuff?
17279             return; 
17280         }
17281         
17282         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
17283             node.parentNode.removeChild(node);
17284             return;
17285         }
17286         
17287         // remove - but keep children..
17288         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
17289             while (node.childNodes.length) {
17290                 var cn = node.childNodes[0];
17291                 node.removeChild(cn);
17292                 node.parentNode.insertBefore(cn, node);
17293             }
17294             node.parentNode.removeChild(node);
17295             cleanWordChildren();
17296             return;
17297         }
17298         // clean styles
17299         if (node.className.length) {
17300             
17301             var cn = node.className.split(/\W+/);
17302             var cna = [];
17303             Roo.each(cn, function(cls) {
17304                 if (cls.match(/Mso[a-zA-Z]+/)) {
17305                     return;
17306                 }
17307                 cna.push(cls);
17308             });
17309             node.className = cna.length ? cna.join(' ') : '';
17310             if (!cna.length) {
17311                 node.removeAttribute("class");
17312             }
17313         }
17314         
17315         if (node.hasAttribute("lang")) {
17316             node.removeAttribute("lang");
17317         }
17318         
17319         if (node.hasAttribute("style")) {
17320             
17321             var styles = node.getAttribute("style").split(";");
17322             var nstyle = [];
17323             Roo.each(styles, function(s) {
17324                 if (!s.match(/:/)) {
17325                     return;
17326                 }
17327                 var kv = s.split(":");
17328                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
17329                     return;
17330                 }
17331                 // what ever is left... we allow.
17332                 nstyle.push(s);
17333             });
17334             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
17335             if (!nstyle.length) {
17336                 node.removeAttribute('style');
17337             }
17338         }
17339         
17340         cleanWordChildren();
17341         
17342         
17343     },
17344     domToHTML : function(currentElement, depth, nopadtext) {
17345         
17346             depth = depth || 0;
17347             nopadtext = nopadtext || false;
17348         
17349             if (!currentElement) {
17350                 return this.domToHTML(this.doc.body);
17351             }
17352             
17353             //Roo.log(currentElement);
17354             var j;
17355             var allText = false;
17356             var nodeName = currentElement.nodeName;
17357             var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
17358             
17359             if  (nodeName == '#text') {
17360                 return currentElement.nodeValue;
17361             }
17362             
17363             
17364             var ret = '';
17365             if (nodeName != 'BODY') {
17366                  
17367                 var i = 0;
17368                 // Prints the node tagName, such as <A>, <IMG>, etc
17369                 if (tagName) {
17370                     var attr = [];
17371                     for(i = 0; i < currentElement.attributes.length;i++) {
17372                         // quoting?
17373                         var aname = currentElement.attributes.item(i).name;
17374                         if (!currentElement.attributes.item(i).value.length) {
17375                             continue;
17376                         }
17377                         attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
17378                     }
17379                     
17380                     ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
17381                 } 
17382                 else {
17383                     
17384                     // eack
17385                 }
17386             } else {
17387                 tagName = false;
17388             }
17389             if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
17390                 return ret;
17391             }
17392             if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
17393                 nopadtext = true;
17394             }
17395             
17396             
17397             // Traverse the tree
17398             i = 0;
17399             var currentElementChild = currentElement.childNodes.item(i);
17400             var allText = true;
17401             var innerHTML  = '';
17402             lastnode = '';
17403             while (currentElementChild) {
17404                 // Formatting code (indent the tree so it looks nice on the screen)
17405                 var nopad = nopadtext;
17406                 if (lastnode == 'SPAN') {
17407                     nopad  = true;
17408                 }
17409                 // text
17410                 if  (currentElementChild.nodeName == '#text') {
17411                     var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
17412                     if (!nopad && toadd.length > 80) {
17413                         innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
17414                     }
17415                     innerHTML  += toadd;
17416                     
17417                     i++;
17418                     currentElementChild = currentElement.childNodes.item(i);
17419                     lastNode = '';
17420                     continue;
17421                 }
17422                 allText = false;
17423                 
17424                 innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
17425                     
17426                 // Recursively traverse the tree structure of the child node
17427                 innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
17428                 lastnode = currentElementChild.nodeName;
17429                 i++;
17430                 currentElementChild=currentElement.childNodes.item(i);
17431             }
17432             
17433             ret += innerHTML;
17434             
17435             if (!allText) {
17436                     // The remaining code is mostly for formatting the tree
17437                 ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
17438             }
17439             
17440             
17441             if (tagName) {
17442                 ret+= "</"+tagName+">";
17443             }
17444             return ret;
17445             
17446         }
17447     
17448     // hide stuff that is not compatible
17449     /**
17450      * @event blur
17451      * @hide
17452      */
17453     /**
17454      * @event change
17455      * @hide
17456      */
17457     /**
17458      * @event focus
17459      * @hide
17460      */
17461     /**
17462      * @event specialkey
17463      * @hide
17464      */
17465     /**
17466      * @cfg {String} fieldClass @hide
17467      */
17468     /**
17469      * @cfg {String} focusClass @hide
17470      */
17471     /**
17472      * @cfg {String} autoCreate @hide
17473      */
17474     /**
17475      * @cfg {String} inputType @hide
17476      */
17477     /**
17478      * @cfg {String} invalidClass @hide
17479      */
17480     /**
17481      * @cfg {String} invalidText @hide
17482      */
17483     /**
17484      * @cfg {String} msgFx @hide
17485      */
17486     /**
17487      * @cfg {String} validateOnBlur @hide
17488      */
17489 });
17490
17491 Roo.HtmlEditorCore.white = [
17492         'area', 'br', 'img', 'input', 'hr', 'wbr',
17493         
17494        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
17495        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
17496        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
17497        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
17498        'table',   'ul',         'xmp', 
17499        
17500        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
17501       'thead',   'tr', 
17502      
17503       'dir', 'menu', 'ol', 'ul', 'dl',
17504        
17505       'embed',  'object'
17506 ];
17507
17508
17509 Roo.HtmlEditorCore.black = [
17510     //    'embed',  'object', // enable - backend responsiblity to clean thiese
17511         'applet', // 
17512         'base',   'basefont', 'bgsound', 'blink',  'body', 
17513         'frame',  'frameset', 'head',    'html',   'ilayer', 
17514         'iframe', 'layer',  'link',     'meta',    'object',   
17515         'script', 'style' ,'title',  'xml' // clean later..
17516 ];
17517 Roo.HtmlEditorCore.clean = [
17518     'script', 'style', 'title', 'xml'
17519 ];
17520 Roo.HtmlEditorCore.remove = [
17521     'font'
17522 ];
17523 // attributes..
17524
17525 Roo.HtmlEditorCore.ablack = [
17526     'on'
17527 ];
17528     
17529 Roo.HtmlEditorCore.aclean = [ 
17530     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
17531 ];
17532
17533 // protocols..
17534 Roo.HtmlEditorCore.pwhite= [
17535         'http',  'https',  'mailto'
17536 ];
17537
17538 // white listed style attributes.
17539 Roo.HtmlEditorCore.cwhite= [
17540       //  'text-align', /// default is to allow most things..
17541       
17542          
17543 //        'font-size'//??
17544 ];
17545
17546 // black listed style attributes.
17547 Roo.HtmlEditorCore.cblack= [
17548       //  'font-size' -- this can be set by the project 
17549 ];
17550
17551
17552 Roo.HtmlEditorCore.swapCodes   =[ 
17553     [    8211, "--" ], 
17554     [    8212, "--" ], 
17555     [    8216,  "'" ],  
17556     [    8217, "'" ],  
17557     [    8220, '"' ],  
17558     [    8221, '"' ],  
17559     [    8226, "*" ],  
17560     [    8230, "..." ]
17561 ]; 
17562
17563     /*
17564  * - LGPL
17565  *
17566  * HtmlEditor
17567  * 
17568  */
17569
17570 /**
17571  * @class Roo.bootstrap.HtmlEditor
17572  * @extends Roo.bootstrap.TextArea
17573  * Bootstrap HtmlEditor class
17574
17575  * @constructor
17576  * Create a new HtmlEditor
17577  * @param {Object} config The config object
17578  */
17579
17580 Roo.bootstrap.HtmlEditor = function(config){
17581     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
17582     if (!this.toolbars) {
17583         this.toolbars = [];
17584     }
17585     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
17586     this.addEvents({
17587             /**
17588              * @event initialize
17589              * Fires when the editor is fully initialized (including the iframe)
17590              * @param {HtmlEditor} this
17591              */
17592             initialize: true,
17593             /**
17594              * @event activate
17595              * Fires when the editor is first receives the focus. Any insertion must wait
17596              * until after this event.
17597              * @param {HtmlEditor} this
17598              */
17599             activate: true,
17600              /**
17601              * @event beforesync
17602              * Fires before the textarea is updated with content from the editor iframe. Return false
17603              * to cancel the sync.
17604              * @param {HtmlEditor} this
17605              * @param {String} html
17606              */
17607             beforesync: true,
17608              /**
17609              * @event beforepush
17610              * Fires before the iframe editor is updated with content from the textarea. Return false
17611              * to cancel the push.
17612              * @param {HtmlEditor} this
17613              * @param {String} html
17614              */
17615             beforepush: true,
17616              /**
17617              * @event sync
17618              * Fires when the textarea is updated with content from the editor iframe.
17619              * @param {HtmlEditor} this
17620              * @param {String} html
17621              */
17622             sync: true,
17623              /**
17624              * @event push
17625              * Fires when the iframe editor is updated with content from the textarea.
17626              * @param {HtmlEditor} this
17627              * @param {String} html
17628              */
17629             push: true,
17630              /**
17631              * @event editmodechange
17632              * Fires when the editor switches edit modes
17633              * @param {HtmlEditor} this
17634              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
17635              */
17636             editmodechange: true,
17637             /**
17638              * @event editorevent
17639              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
17640              * @param {HtmlEditor} this
17641              */
17642             editorevent: true,
17643             /**
17644              * @event firstfocus
17645              * Fires when on first focus - needed by toolbars..
17646              * @param {HtmlEditor} this
17647              */
17648             firstfocus: true,
17649             /**
17650              * @event autosave
17651              * Auto save the htmlEditor value as a file into Events
17652              * @param {HtmlEditor} this
17653              */
17654             autosave: true,
17655             /**
17656              * @event savedpreview
17657              * preview the saved version of htmlEditor
17658              * @param {HtmlEditor} this
17659              */
17660             savedpreview: true
17661         });
17662 };
17663
17664
17665 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
17666     
17667     
17668       /**
17669      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
17670      */
17671     toolbars : false,
17672    
17673      /**
17674      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
17675      *                        Roo.resizable.
17676      */
17677     resizable : false,
17678      /**
17679      * @cfg {Number} height (in pixels)
17680      */   
17681     height: 300,
17682    /**
17683      * @cfg {Number} width (in pixels)
17684      */   
17685     width: false,
17686     
17687     /**
17688      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
17689      * 
17690      */
17691     stylesheets: false,
17692     
17693     // id of frame..
17694     frameId: false,
17695     
17696     // private properties
17697     validationEvent : false,
17698     deferHeight: true,
17699     initialized : false,
17700     activated : false,
17701     
17702     onFocus : Roo.emptyFn,
17703     iframePad:3,
17704     hideMode:'offsets',
17705     
17706     
17707     tbContainer : false,
17708     
17709     toolbarContainer :function() {
17710         return this.wrap.select('.x-html-editor-tb',true).first();
17711     },
17712
17713     /**
17714      * Protected method that will not generally be called directly. It
17715      * is called when the editor creates its toolbar. Override this method if you need to
17716      * add custom toolbar buttons.
17717      * @param {HtmlEditor} editor
17718      */
17719     createToolbar : function(){
17720         
17721         Roo.log("create toolbars");
17722         
17723         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
17724         this.toolbars[0].render(this.toolbarContainer());
17725         
17726         return;
17727         
17728 //        if (!editor.toolbars || !editor.toolbars.length) {
17729 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
17730 //        }
17731 //        
17732 //        for (var i =0 ; i < editor.toolbars.length;i++) {
17733 //            editor.toolbars[i] = Roo.factory(
17734 //                    typeof(editor.toolbars[i]) == 'string' ?
17735 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
17736 //                Roo.bootstrap.HtmlEditor);
17737 //            editor.toolbars[i].init(editor);
17738 //        }
17739     },
17740
17741      
17742     // private
17743     onRender : function(ct, position)
17744     {
17745        // Roo.log("Call onRender: " + this.xtype);
17746         var _t = this;
17747         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
17748       
17749         this.wrap = this.inputEl().wrap({
17750             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
17751         });
17752         
17753         this.editorcore.onRender(ct, position);
17754          
17755         if (this.resizable) {
17756             this.resizeEl = new Roo.Resizable(this.wrap, {
17757                 pinned : true,
17758                 wrap: true,
17759                 dynamic : true,
17760                 minHeight : this.height,
17761                 height: this.height,
17762                 handles : this.resizable,
17763                 width: this.width,
17764                 listeners : {
17765                     resize : function(r, w, h) {
17766                         _t.onResize(w,h); // -something
17767                     }
17768                 }
17769             });
17770             
17771         }
17772         this.createToolbar(this);
17773        
17774         
17775         if(!this.width && this.resizable){
17776             this.setSize(this.wrap.getSize());
17777         }
17778         if (this.resizeEl) {
17779             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
17780             // should trigger onReize..
17781         }
17782         
17783     },
17784
17785     // private
17786     onResize : function(w, h)
17787     {
17788         Roo.log('resize: ' +w + ',' + h );
17789         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
17790         var ew = false;
17791         var eh = false;
17792         
17793         if(this.inputEl() ){
17794             if(typeof w == 'number'){
17795                 var aw = w - this.wrap.getFrameWidth('lr');
17796                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
17797                 ew = aw;
17798             }
17799             if(typeof h == 'number'){
17800                  var tbh = -11;  // fixme it needs to tool bar size!
17801                 for (var i =0; i < this.toolbars.length;i++) {
17802                     // fixme - ask toolbars for heights?
17803                     tbh += this.toolbars[i].el.getHeight();
17804                     //if (this.toolbars[i].footer) {
17805                     //    tbh += this.toolbars[i].footer.el.getHeight();
17806                     //}
17807                 }
17808               
17809                 
17810                 
17811                 
17812                 
17813                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
17814                 ah -= 5; // knock a few pixes off for look..
17815                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
17816                 var eh = ah;
17817             }
17818         }
17819         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
17820         this.editorcore.onResize(ew,eh);
17821         
17822     },
17823
17824     /**
17825      * Toggles the editor between standard and source edit mode.
17826      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
17827      */
17828     toggleSourceEdit : function(sourceEditMode)
17829     {
17830         this.editorcore.toggleSourceEdit(sourceEditMode);
17831         
17832         if(this.editorcore.sourceEditMode){
17833             Roo.log('editor - showing textarea');
17834             
17835 //            Roo.log('in');
17836 //            Roo.log(this.syncValue());
17837             this.syncValue();
17838             this.inputEl().removeClass(['hide', 'x-hidden']);
17839             this.inputEl().dom.removeAttribute('tabIndex');
17840             this.inputEl().focus();
17841         }else{
17842             Roo.log('editor - hiding textarea');
17843 //            Roo.log('out')
17844 //            Roo.log(this.pushValue()); 
17845             this.pushValue();
17846             
17847             this.inputEl().addClass(['hide', 'x-hidden']);
17848             this.inputEl().dom.setAttribute('tabIndex', -1);
17849             //this.deferFocus();
17850         }
17851          
17852         if(this.resizable){
17853             this.setSize(this.wrap.getSize());
17854         }
17855         
17856         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
17857     },
17858  
17859     // private (for BoxComponent)
17860     adjustSize : Roo.BoxComponent.prototype.adjustSize,
17861
17862     // private (for BoxComponent)
17863     getResizeEl : function(){
17864         return this.wrap;
17865     },
17866
17867     // private (for BoxComponent)
17868     getPositionEl : function(){
17869         return this.wrap;
17870     },
17871
17872     // private
17873     initEvents : function(){
17874         this.originalValue = this.getValue();
17875     },
17876
17877 //    /**
17878 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
17879 //     * @method
17880 //     */
17881 //    markInvalid : Roo.emptyFn,
17882 //    /**
17883 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
17884 //     * @method
17885 //     */
17886 //    clearInvalid : Roo.emptyFn,
17887
17888     setValue : function(v){
17889         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
17890         this.editorcore.pushValue();
17891     },
17892
17893      
17894     // private
17895     deferFocus : function(){
17896         this.focus.defer(10, this);
17897     },
17898
17899     // doc'ed in Field
17900     focus : function(){
17901         this.editorcore.focus();
17902         
17903     },
17904       
17905
17906     // private
17907     onDestroy : function(){
17908         
17909         
17910         
17911         if(this.rendered){
17912             
17913             for (var i =0; i < this.toolbars.length;i++) {
17914                 // fixme - ask toolbars for heights?
17915                 this.toolbars[i].onDestroy();
17916             }
17917             
17918             this.wrap.dom.innerHTML = '';
17919             this.wrap.remove();
17920         }
17921     },
17922
17923     // private
17924     onFirstFocus : function(){
17925         //Roo.log("onFirstFocus");
17926         this.editorcore.onFirstFocus();
17927          for (var i =0; i < this.toolbars.length;i++) {
17928             this.toolbars[i].onFirstFocus();
17929         }
17930         
17931     },
17932     
17933     // private
17934     syncValue : function()
17935     {   
17936         this.editorcore.syncValue();
17937     },
17938     
17939     pushValue : function()
17940     {   
17941         this.editorcore.pushValue();
17942     }
17943      
17944     
17945     // hide stuff that is not compatible
17946     /**
17947      * @event blur
17948      * @hide
17949      */
17950     /**
17951      * @event change
17952      * @hide
17953      */
17954     /**
17955      * @event focus
17956      * @hide
17957      */
17958     /**
17959      * @event specialkey
17960      * @hide
17961      */
17962     /**
17963      * @cfg {String} fieldClass @hide
17964      */
17965     /**
17966      * @cfg {String} focusClass @hide
17967      */
17968     /**
17969      * @cfg {String} autoCreate @hide
17970      */
17971     /**
17972      * @cfg {String} inputType @hide
17973      */
17974     /**
17975      * @cfg {String} invalidClass @hide
17976      */
17977     /**
17978      * @cfg {String} invalidText @hide
17979      */
17980     /**
17981      * @cfg {String} msgFx @hide
17982      */
17983     /**
17984      * @cfg {String} validateOnBlur @hide
17985      */
17986 });
17987  
17988     
17989    
17990    
17991    
17992       
17993 Roo.namespace('Roo.bootstrap.htmleditor');
17994 /**
17995  * @class Roo.bootstrap.HtmlEditorToolbar1
17996  * Basic Toolbar
17997  * 
17998  * Usage:
17999  *
18000  new Roo.bootstrap.HtmlEditor({
18001     ....
18002     toolbars : [
18003         new Roo.bootstrap.HtmlEditorToolbar1({
18004             disable : { fonts: 1 , format: 1, ..., ... , ...],
18005             btns : [ .... ]
18006         })
18007     }
18008      
18009  * 
18010  * @cfg {Object} disable List of elements to disable..
18011  * @cfg {Array} btns List of additional buttons.
18012  * 
18013  * 
18014  * NEEDS Extra CSS? 
18015  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
18016  */
18017  
18018 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
18019 {
18020     
18021     Roo.apply(this, config);
18022     
18023     // default disabled, based on 'good practice'..
18024     this.disable = this.disable || {};
18025     Roo.applyIf(this.disable, {
18026         fontSize : true,
18027         colors : true,
18028         specialElements : true
18029     });
18030     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
18031     
18032     this.editor = config.editor;
18033     this.editorcore = config.editor.editorcore;
18034     
18035     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
18036     
18037     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
18038     // dont call parent... till later.
18039 }
18040 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
18041      
18042     bar : true,
18043     
18044     editor : false,
18045     editorcore : false,
18046     
18047     
18048     formats : [
18049         "p" ,  
18050         "h1","h2","h3","h4","h5","h6", 
18051         "pre", "code", 
18052         "abbr", "acronym", "address", "cite", "samp", "var",
18053         'div','span'
18054     ],
18055     
18056     onRender : function(ct, position)
18057     {
18058        // Roo.log("Call onRender: " + this.xtype);
18059         
18060        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
18061        Roo.log(this.el);
18062        this.el.dom.style.marginBottom = '0';
18063        var _this = this;
18064        var editorcore = this.editorcore;
18065        var editor= this.editor;
18066        
18067        var children = [];
18068        var btn = function(id,cmd , toggle, handler){
18069        
18070             var  event = toggle ? 'toggle' : 'click';
18071        
18072             var a = {
18073                 size : 'sm',
18074                 xtype: 'Button',
18075                 xns: Roo.bootstrap,
18076                 glyphicon : id,
18077                 cmd : id || cmd,
18078                 enableToggle:toggle !== false,
18079                 //html : 'submit'
18080                 pressed : toggle ? false : null,
18081                 listeners : {}
18082             }
18083             a.listeners[toggle ? 'toggle' : 'click'] = function() {
18084                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
18085             }
18086             children.push(a);
18087             return a;
18088        }
18089         
18090         var style = {
18091                 xtype: 'Button',
18092                 size : 'sm',
18093                 xns: Roo.bootstrap,
18094                 glyphicon : 'font',
18095                 //html : 'submit'
18096                 menu : {
18097                     xtype: 'Menu',
18098                     xns: Roo.bootstrap,
18099                     items:  []
18100                 }
18101         };
18102         Roo.each(this.formats, function(f) {
18103             style.menu.items.push({
18104                 xtype :'MenuItem',
18105                 xns: Roo.bootstrap,
18106                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
18107                 tagname : f,
18108                 listeners : {
18109                     click : function()
18110                     {
18111                         editorcore.insertTag(this.tagname);
18112                         editor.focus();
18113                     }
18114                 }
18115                 
18116             });
18117         });
18118          children.push(style);   
18119             
18120             
18121         btn('bold',false,true);
18122         btn('italic',false,true);
18123         btn('align-left', 'justifyleft',true);
18124         btn('align-center', 'justifycenter',true);
18125         btn('align-right' , 'justifyright',true);
18126         btn('link', false, false, function(btn) {
18127             //Roo.log("create link?");
18128             var url = prompt(this.createLinkText, this.defaultLinkValue);
18129             if(url && url != 'http:/'+'/'){
18130                 this.editorcore.relayCmd('createlink', url);
18131             }
18132         }),
18133         btn('list','insertunorderedlist',true);
18134         btn('pencil', false,true, function(btn){
18135                 Roo.log(this);
18136                 
18137                 this.toggleSourceEdit(btn.pressed);
18138         });
18139         /*
18140         var cog = {
18141                 xtype: 'Button',
18142                 size : 'sm',
18143                 xns: Roo.bootstrap,
18144                 glyphicon : 'cog',
18145                 //html : 'submit'
18146                 menu : {
18147                     xtype: 'Menu',
18148                     xns: Roo.bootstrap,
18149                     items:  []
18150                 }
18151         };
18152         
18153         cog.menu.items.push({
18154             xtype :'MenuItem',
18155             xns: Roo.bootstrap,
18156             html : Clean styles,
18157             tagname : f,
18158             listeners : {
18159                 click : function()
18160                 {
18161                     editorcore.insertTag(this.tagname);
18162                     editor.focus();
18163                 }
18164             }
18165             
18166         });
18167        */
18168         
18169          
18170        this.xtype = 'NavSimplebar';
18171         
18172         for(var i=0;i< children.length;i++) {
18173             
18174             this.buttons.add(this.addxtypeChild(children[i]));
18175             
18176         }
18177         
18178         editor.on('editorevent', this.updateToolbar, this);
18179     },
18180     onBtnClick : function(id)
18181     {
18182        this.editorcore.relayCmd(id);
18183        this.editorcore.focus();
18184     },
18185     
18186     /**
18187      * Protected method that will not generally be called directly. It triggers
18188      * a toolbar update by reading the markup state of the current selection in the editor.
18189      */
18190     updateToolbar: function(){
18191
18192         if(!this.editorcore.activated){
18193             this.editor.onFirstFocus(); // is this neeed?
18194             return;
18195         }
18196
18197         var btns = this.buttons; 
18198         var doc = this.editorcore.doc;
18199         btns.get('bold').setActive(doc.queryCommandState('bold'));
18200         btns.get('italic').setActive(doc.queryCommandState('italic'));
18201         //btns.get('underline').setActive(doc.queryCommandState('underline'));
18202         
18203         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
18204         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
18205         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
18206         
18207         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
18208         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
18209          /*
18210         
18211         var ans = this.editorcore.getAllAncestors();
18212         if (this.formatCombo) {
18213             
18214             
18215             var store = this.formatCombo.store;
18216             this.formatCombo.setValue("");
18217             for (var i =0; i < ans.length;i++) {
18218                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
18219                     // select it..
18220                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
18221                     break;
18222                 }
18223             }
18224         }
18225         
18226         
18227         
18228         // hides menus... - so this cant be on a menu...
18229         Roo.bootstrap.MenuMgr.hideAll();
18230         */
18231         Roo.bootstrap.MenuMgr.hideAll();
18232         //this.editorsyncValue();
18233     },
18234     onFirstFocus: function() {
18235         this.buttons.each(function(item){
18236            item.enable();
18237         });
18238     },
18239     toggleSourceEdit : function(sourceEditMode){
18240         
18241           
18242         if(sourceEditMode){
18243             Roo.log("disabling buttons");
18244            this.buttons.each( function(item){
18245                 if(item.cmd != 'pencil'){
18246                     item.disable();
18247                 }
18248             });
18249           
18250         }else{
18251             Roo.log("enabling buttons");
18252             if(this.editorcore.initialized){
18253                 this.buttons.each( function(item){
18254                     item.enable();
18255                 });
18256             }
18257             
18258         }
18259         Roo.log("calling toggole on editor");
18260         // tell the editor that it's been pressed..
18261         this.editor.toggleSourceEdit(sourceEditMode);
18262        
18263     }
18264 });
18265
18266
18267
18268
18269
18270 /**
18271  * @class Roo.bootstrap.Table.AbstractSelectionModel
18272  * @extends Roo.util.Observable
18273  * Abstract base class for grid SelectionModels.  It provides the interface that should be
18274  * implemented by descendant classes.  This class should not be directly instantiated.
18275  * @constructor
18276  */
18277 Roo.bootstrap.Table.AbstractSelectionModel = function(){
18278     this.locked = false;
18279     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
18280 };
18281
18282
18283 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
18284     /** @ignore Called by the grid automatically. Do not call directly. */
18285     init : function(grid){
18286         this.grid = grid;
18287         this.initEvents();
18288     },
18289
18290     /**
18291      * Locks the selections.
18292      */
18293     lock : function(){
18294         this.locked = true;
18295     },
18296
18297     /**
18298      * Unlocks the selections.
18299      */
18300     unlock : function(){
18301         this.locked = false;
18302     },
18303
18304     /**
18305      * Returns true if the selections are locked.
18306      * @return {Boolean}
18307      */
18308     isLocked : function(){
18309         return this.locked;
18310     }
18311 });
18312 /**
18313  * @extends Roo.bootstrap.Table.AbstractSelectionModel
18314  * @class Roo.bootstrap.Table.RowSelectionModel
18315  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
18316  * It supports multiple selections and keyboard selection/navigation. 
18317  * @constructor
18318  * @param {Object} config
18319  */
18320
18321 Roo.bootstrap.Table.RowSelectionModel = function(config){
18322     Roo.apply(this, config);
18323     this.selections = new Roo.util.MixedCollection(false, function(o){
18324         return o.id;
18325     });
18326
18327     this.last = false;
18328     this.lastActive = false;
18329
18330     this.addEvents({
18331         /**
18332              * @event selectionchange
18333              * Fires when the selection changes
18334              * @param {SelectionModel} this
18335              */
18336             "selectionchange" : true,
18337         /**
18338              * @event afterselectionchange
18339              * Fires after the selection changes (eg. by key press or clicking)
18340              * @param {SelectionModel} this
18341              */
18342             "afterselectionchange" : true,
18343         /**
18344              * @event beforerowselect
18345              * Fires when a row is selected being selected, return false to cancel.
18346              * @param {SelectionModel} this
18347              * @param {Number} rowIndex The selected index
18348              * @param {Boolean} keepExisting False if other selections will be cleared
18349              */
18350             "beforerowselect" : true,
18351         /**
18352              * @event rowselect
18353              * Fires when a row is selected.
18354              * @param {SelectionModel} this
18355              * @param {Number} rowIndex The selected index
18356              * @param {Roo.data.Record} r The record
18357              */
18358             "rowselect" : true,
18359         /**
18360              * @event rowdeselect
18361              * Fires when a row is deselected.
18362              * @param {SelectionModel} this
18363              * @param {Number} rowIndex The selected index
18364              */
18365         "rowdeselect" : true
18366     });
18367     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
18368     this.locked = false;
18369 };
18370
18371 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
18372     /**
18373      * @cfg {Boolean} singleSelect
18374      * True to allow selection of only one row at a time (defaults to false)
18375      */
18376     singleSelect : false,
18377
18378     // private
18379     initEvents : function(){
18380
18381         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
18382             this.grid.on("mousedown", this.handleMouseDown, this);
18383         }else{ // allow click to work like normal
18384             this.grid.on("rowclick", this.handleDragableRowClick, this);
18385         }
18386
18387         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
18388             "up" : function(e){
18389                 if(!e.shiftKey){
18390                     this.selectPrevious(e.shiftKey);
18391                 }else if(this.last !== false && this.lastActive !== false){
18392                     var last = this.last;
18393                     this.selectRange(this.last,  this.lastActive-1);
18394                     this.grid.getView().focusRow(this.lastActive);
18395                     if(last !== false){
18396                         this.last = last;
18397                     }
18398                 }else{
18399                     this.selectFirstRow();
18400                 }
18401                 this.fireEvent("afterselectionchange", this);
18402             },
18403             "down" : function(e){
18404                 if(!e.shiftKey){
18405                     this.selectNext(e.shiftKey);
18406                 }else if(this.last !== false && this.lastActive !== false){
18407                     var last = this.last;
18408                     this.selectRange(this.last,  this.lastActive+1);
18409                     this.grid.getView().focusRow(this.lastActive);
18410                     if(last !== false){
18411                         this.last = last;
18412                     }
18413                 }else{
18414                     this.selectFirstRow();
18415                 }
18416                 this.fireEvent("afterselectionchange", this);
18417             },
18418             scope: this
18419         });
18420
18421         var view = this.grid.view;
18422         view.on("refresh", this.onRefresh, this);
18423         view.on("rowupdated", this.onRowUpdated, this);
18424         view.on("rowremoved", this.onRemove, this);
18425     },
18426
18427     // private
18428     onRefresh : function(){
18429         var ds = this.grid.dataSource, i, v = this.grid.view;
18430         var s = this.selections;
18431         s.each(function(r){
18432             if((i = ds.indexOfId(r.id)) != -1){
18433                 v.onRowSelect(i);
18434             }else{
18435                 s.remove(r);
18436             }
18437         });
18438     },
18439
18440     // private
18441     onRemove : function(v, index, r){
18442         this.selections.remove(r);
18443     },
18444
18445     // private
18446     onRowUpdated : function(v, index, r){
18447         if(this.isSelected(r)){
18448             v.onRowSelect(index);
18449         }
18450     },
18451
18452     /**
18453      * Select records.
18454      * @param {Array} records The records to select
18455      * @param {Boolean} keepExisting (optional) True to keep existing selections
18456      */
18457     selectRecords : function(records, keepExisting){
18458         if(!keepExisting){
18459             this.clearSelections();
18460         }
18461         var ds = this.grid.dataSource;
18462         for(var i = 0, len = records.length; i < len; i++){
18463             this.selectRow(ds.indexOf(records[i]), true);
18464         }
18465     },
18466
18467     /**
18468      * Gets the number of selected rows.
18469      * @return {Number}
18470      */
18471     getCount : function(){
18472         return this.selections.length;
18473     },
18474
18475     /**
18476      * Selects the first row in the grid.
18477      */
18478     selectFirstRow : function(){
18479         this.selectRow(0);
18480     },
18481
18482     /**
18483      * Select the last row.
18484      * @param {Boolean} keepExisting (optional) True to keep existing selections
18485      */
18486     selectLastRow : function(keepExisting){
18487         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
18488     },
18489
18490     /**
18491      * Selects the row immediately following the last selected row.
18492      * @param {Boolean} keepExisting (optional) True to keep existing selections
18493      */
18494     selectNext : function(keepExisting){
18495         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
18496             this.selectRow(this.last+1, keepExisting);
18497             this.grid.getView().focusRow(this.last);
18498         }
18499     },
18500
18501     /**
18502      * Selects the row that precedes the last selected row.
18503      * @param {Boolean} keepExisting (optional) True to keep existing selections
18504      */
18505     selectPrevious : function(keepExisting){
18506         if(this.last){
18507             this.selectRow(this.last-1, keepExisting);
18508             this.grid.getView().focusRow(this.last);
18509         }
18510     },
18511
18512     /**
18513      * Returns the selected records
18514      * @return {Array} Array of selected records
18515      */
18516     getSelections : function(){
18517         return [].concat(this.selections.items);
18518     },
18519
18520     /**
18521      * Returns the first selected record.
18522      * @return {Record}
18523      */
18524     getSelected : function(){
18525         return this.selections.itemAt(0);
18526     },
18527
18528
18529     /**
18530      * Clears all selections.
18531      */
18532     clearSelections : function(fast){
18533         if(this.locked) return;
18534         if(fast !== true){
18535             var ds = this.grid.dataSource;
18536             var s = this.selections;
18537             s.each(function(r){
18538                 this.deselectRow(ds.indexOfId(r.id));
18539             }, this);
18540             s.clear();
18541         }else{
18542             this.selections.clear();
18543         }
18544         this.last = false;
18545     },
18546
18547
18548     /**
18549      * Selects all rows.
18550      */
18551     selectAll : function(){
18552         if(this.locked) return;
18553         this.selections.clear();
18554         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
18555             this.selectRow(i, true);
18556         }
18557     },
18558
18559     /**
18560      * Returns True if there is a selection.
18561      * @return {Boolean}
18562      */
18563     hasSelection : function(){
18564         return this.selections.length > 0;
18565     },
18566
18567     /**
18568      * Returns True if the specified row is selected.
18569      * @param {Number/Record} record The record or index of the record to check
18570      * @return {Boolean}
18571      */
18572     isSelected : function(index){
18573         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
18574         return (r && this.selections.key(r.id) ? true : false);
18575     },
18576
18577     /**
18578      * Returns True if the specified record id is selected.
18579      * @param {String} id The id of record to check
18580      * @return {Boolean}
18581      */
18582     isIdSelected : function(id){
18583         return (this.selections.key(id) ? true : false);
18584     },
18585
18586     // private
18587     handleMouseDown : function(e, t){
18588         var view = this.grid.getView(), rowIndex;
18589         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
18590             return;
18591         };
18592         if(e.shiftKey && this.last !== false){
18593             var last = this.last;
18594             this.selectRange(last, rowIndex, e.ctrlKey);
18595             this.last = last; // reset the last
18596             view.focusRow(rowIndex);
18597         }else{
18598             var isSelected = this.isSelected(rowIndex);
18599             if(e.button !== 0 && isSelected){
18600                 view.focusRow(rowIndex);
18601             }else if(e.ctrlKey && isSelected){
18602                 this.deselectRow(rowIndex);
18603             }else if(!isSelected){
18604                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
18605                 view.focusRow(rowIndex);
18606             }
18607         }
18608         this.fireEvent("afterselectionchange", this);
18609     },
18610     // private
18611     handleDragableRowClick :  function(grid, rowIndex, e) 
18612     {
18613         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
18614             this.selectRow(rowIndex, false);
18615             grid.view.focusRow(rowIndex);
18616              this.fireEvent("afterselectionchange", this);
18617         }
18618     },
18619     
18620     /**
18621      * Selects multiple rows.
18622      * @param {Array} rows Array of the indexes of the row to select
18623      * @param {Boolean} keepExisting (optional) True to keep existing selections
18624      */
18625     selectRows : function(rows, keepExisting){
18626         if(!keepExisting){
18627             this.clearSelections();
18628         }
18629         for(var i = 0, len = rows.length; i < len; i++){
18630             this.selectRow(rows[i], true);
18631         }
18632     },
18633
18634     /**
18635      * Selects a range of rows. All rows in between startRow and endRow are also selected.
18636      * @param {Number} startRow The index of the first row in the range
18637      * @param {Number} endRow The index of the last row in the range
18638      * @param {Boolean} keepExisting (optional) True to retain existing selections
18639      */
18640     selectRange : function(startRow, endRow, keepExisting){
18641         if(this.locked) return;
18642         if(!keepExisting){
18643             this.clearSelections();
18644         }
18645         if(startRow <= endRow){
18646             for(var i = startRow; i <= endRow; i++){
18647                 this.selectRow(i, true);
18648             }
18649         }else{
18650             for(var i = startRow; i >= endRow; i--){
18651                 this.selectRow(i, true);
18652             }
18653         }
18654     },
18655
18656     /**
18657      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
18658      * @param {Number} startRow The index of the first row in the range
18659      * @param {Number} endRow The index of the last row in the range
18660      */
18661     deselectRange : function(startRow, endRow, preventViewNotify){
18662         if(this.locked) return;
18663         for(var i = startRow; i <= endRow; i++){
18664             this.deselectRow(i, preventViewNotify);
18665         }
18666     },
18667
18668     /**
18669      * Selects a row.
18670      * @param {Number} row The index of the row to select
18671      * @param {Boolean} keepExisting (optional) True to keep existing selections
18672      */
18673     selectRow : function(index, keepExisting, preventViewNotify){
18674         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
18675         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
18676             if(!keepExisting || this.singleSelect){
18677                 this.clearSelections();
18678             }
18679             var r = this.grid.dataSource.getAt(index);
18680             this.selections.add(r);
18681             this.last = this.lastActive = index;
18682             if(!preventViewNotify){
18683                 this.grid.getView().onRowSelect(index);
18684             }
18685             this.fireEvent("rowselect", this, index, r);
18686             this.fireEvent("selectionchange", this);
18687         }
18688     },
18689
18690     /**
18691      * Deselects a row.
18692      * @param {Number} row The index of the row to deselect
18693      */
18694     deselectRow : function(index, preventViewNotify){
18695         if(this.locked) return;
18696         if(this.last == index){
18697             this.last = false;
18698         }
18699         if(this.lastActive == index){
18700             this.lastActive = false;
18701         }
18702         var r = this.grid.dataSource.getAt(index);
18703         this.selections.remove(r);
18704         if(!preventViewNotify){
18705             this.grid.getView().onRowDeselect(index);
18706         }
18707         this.fireEvent("rowdeselect", this, index);
18708         this.fireEvent("selectionchange", this);
18709     },
18710
18711     // private
18712     restoreLast : function(){
18713         if(this._last){
18714             this.last = this._last;
18715         }
18716     },
18717
18718     // private
18719     acceptsNav : function(row, col, cm){
18720         return !cm.isHidden(col) && cm.isCellEditable(col, row);
18721     },
18722
18723     // private
18724     onEditorKey : function(field, e){
18725         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
18726         if(k == e.TAB){
18727             e.stopEvent();
18728             ed.completeEdit();
18729             if(e.shiftKey){
18730                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
18731             }else{
18732                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
18733             }
18734         }else if(k == e.ENTER && !e.ctrlKey){
18735             e.stopEvent();
18736             ed.completeEdit();
18737             if(e.shiftKey){
18738                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
18739             }else{
18740                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
18741             }
18742         }else if(k == e.ESC){
18743             ed.cancelEdit();
18744         }
18745         if(newCell){
18746             g.startEditing(newCell[0], newCell[1]);
18747         }
18748     }
18749 });/*
18750  * Based on:
18751  * Ext JS Library 1.1.1
18752  * Copyright(c) 2006-2007, Ext JS, LLC.
18753  *
18754  * Originally Released Under LGPL - original licence link has changed is not relivant.
18755  *
18756  * Fork - LGPL
18757  * <script type="text/javascript">
18758  */
18759  
18760 /**
18761  * @class Roo.bootstrap.PagingToolbar
18762  * @extends Roo.Row
18763  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
18764  * @constructor
18765  * Create a new PagingToolbar
18766  * @param {Object} config The config object
18767  */
18768 Roo.bootstrap.PagingToolbar = function(config)
18769 {
18770     // old args format still supported... - xtype is prefered..
18771         // created from xtype...
18772     var ds = config.dataSource;
18773     this.toolbarItems = [];
18774     if (config.items) {
18775         this.toolbarItems = config.items;
18776 //        config.items = [];
18777     }
18778     
18779     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
18780     this.ds = ds;
18781     this.cursor = 0;
18782     if (ds) { 
18783         this.bind(ds);
18784     }
18785     
18786     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
18787     
18788 };
18789
18790 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
18791     /**
18792      * @cfg {Roo.data.Store} dataSource
18793      * The underlying data store providing the paged data
18794      */
18795     /**
18796      * @cfg {String/HTMLElement/Element} container
18797      * container The id or element that will contain the toolbar
18798      */
18799     /**
18800      * @cfg {Boolean} displayInfo
18801      * True to display the displayMsg (defaults to false)
18802      */
18803     /**
18804      * @cfg {Number} pageSize
18805      * The number of records to display per page (defaults to 20)
18806      */
18807     pageSize: 20,
18808     /**
18809      * @cfg {String} displayMsg
18810      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
18811      */
18812     displayMsg : 'Displaying {0} - {1} of {2}',
18813     /**
18814      * @cfg {String} emptyMsg
18815      * The message to display when no records are found (defaults to "No data to display")
18816      */
18817     emptyMsg : 'No data to display',
18818     /**
18819      * Customizable piece of the default paging text (defaults to "Page")
18820      * @type String
18821      */
18822     beforePageText : "Page",
18823     /**
18824      * Customizable piece of the default paging text (defaults to "of %0")
18825      * @type String
18826      */
18827     afterPageText : "of {0}",
18828     /**
18829      * Customizable piece of the default paging text (defaults to "First Page")
18830      * @type String
18831      */
18832     firstText : "First Page",
18833     /**
18834      * Customizable piece of the default paging text (defaults to "Previous Page")
18835      * @type String
18836      */
18837     prevText : "Previous Page",
18838     /**
18839      * Customizable piece of the default paging text (defaults to "Next Page")
18840      * @type String
18841      */
18842     nextText : "Next Page",
18843     /**
18844      * Customizable piece of the default paging text (defaults to "Last Page")
18845      * @type String
18846      */
18847     lastText : "Last Page",
18848     /**
18849      * Customizable piece of the default paging text (defaults to "Refresh")
18850      * @type String
18851      */
18852     refreshText : "Refresh",
18853
18854     buttons : false,
18855     // private
18856     onRender : function(ct, position) 
18857     {
18858         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
18859         this.navgroup.parentId = this.id;
18860         this.navgroup.onRender(this.el, null);
18861         // add the buttons to the navgroup
18862         
18863         if(this.displayInfo){
18864             Roo.log(this.el.select('ul.navbar-nav',true).first());
18865             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
18866             this.displayEl = this.el.select('.x-paging-info', true).first();
18867 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
18868 //            this.displayEl = navel.el.select('span',true).first();
18869         }
18870         
18871         var _this = this;
18872         
18873         if(this.buttons){
18874             Roo.each(_this.buttons, function(e){
18875                Roo.factory(e).onRender(_this.el, null);
18876             });
18877         }
18878             
18879         Roo.each(_this.toolbarItems, function(e) {
18880             _this.navgroup.addItem(e);
18881         });
18882         
18883         this.first = this.navgroup.addItem({
18884             tooltip: this.firstText,
18885             cls: "prev",
18886             icon : 'fa fa-backward',
18887             disabled: true,
18888             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
18889         });
18890         
18891         this.prev =  this.navgroup.addItem({
18892             tooltip: this.prevText,
18893             cls: "prev",
18894             icon : 'fa fa-step-backward',
18895             disabled: true,
18896             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
18897         });
18898     //this.addSeparator();
18899         
18900         
18901         var field = this.navgroup.addItem( {
18902             tagtype : 'span',
18903             cls : 'x-paging-position',
18904             
18905             html : this.beforePageText  +
18906                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
18907                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
18908          } ); //?? escaped?
18909         
18910         this.field = field.el.select('input', true).first();
18911         this.field.on("keydown", this.onPagingKeydown, this);
18912         this.field.on("focus", function(){this.dom.select();});
18913     
18914     
18915         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
18916         //this.field.setHeight(18);
18917         //this.addSeparator();
18918         this.next = this.navgroup.addItem({
18919             tooltip: this.nextText,
18920             cls: "next",
18921             html : ' <i class="fa fa-step-forward">',
18922             disabled: true,
18923             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
18924         });
18925         this.last = this.navgroup.addItem({
18926             tooltip: this.lastText,
18927             icon : 'fa fa-forward',
18928             cls: "next",
18929             disabled: true,
18930             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
18931         });
18932     //this.addSeparator();
18933         this.loading = this.navgroup.addItem({
18934             tooltip: this.refreshText,
18935             icon: 'fa fa-refresh',
18936             
18937             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
18938         });
18939
18940     },
18941
18942     // private
18943     updateInfo : function(){
18944         if(this.displayEl){
18945             var count = this.ds.getCount();
18946             var msg = count == 0 ?
18947                 this.emptyMsg :
18948                 String.format(
18949                     this.displayMsg,
18950                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
18951                 );
18952             this.displayEl.update(msg);
18953         }
18954     },
18955
18956     // private
18957     onLoad : function(ds, r, o){
18958        this.cursor = o.params ? o.params.start : 0;
18959        var d = this.getPageData(),
18960             ap = d.activePage,
18961             ps = d.pages;
18962         
18963        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
18964        this.field.dom.value = ap;
18965        this.first.setDisabled(ap == 1);
18966        this.prev.setDisabled(ap == 1);
18967        this.next.setDisabled(ap == ps);
18968        this.last.setDisabled(ap == ps);
18969        this.loading.enable();
18970        this.updateInfo();
18971     },
18972
18973     // private
18974     getPageData : function(){
18975         var total = this.ds.getTotalCount();
18976         return {
18977             total : total,
18978             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
18979             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
18980         };
18981     },
18982
18983     // private
18984     onLoadError : function(){
18985         this.loading.enable();
18986     },
18987
18988     // private
18989     onPagingKeydown : function(e){
18990         var k = e.getKey();
18991         var d = this.getPageData();
18992         if(k == e.RETURN){
18993             var v = this.field.dom.value, pageNum;
18994             if(!v || isNaN(pageNum = parseInt(v, 10))){
18995                 this.field.dom.value = d.activePage;
18996                 return;
18997             }
18998             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
18999             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
19000             e.stopEvent();
19001         }
19002         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))
19003         {
19004           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
19005           this.field.dom.value = pageNum;
19006           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
19007           e.stopEvent();
19008         }
19009         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
19010         {
19011           var v = this.field.dom.value, pageNum; 
19012           var increment = (e.shiftKey) ? 10 : 1;
19013           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
19014             increment *= -1;
19015           if(!v || isNaN(pageNum = parseInt(v, 10))) {
19016             this.field.dom.value = d.activePage;
19017             return;
19018           }
19019           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
19020           {
19021             this.field.dom.value = parseInt(v, 10) + increment;
19022             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
19023             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
19024           }
19025           e.stopEvent();
19026         }
19027     },
19028
19029     // private
19030     beforeLoad : function(){
19031         if(this.loading){
19032             this.loading.disable();
19033         }
19034     },
19035
19036     // private
19037     onClick : function(which){
19038         var ds = this.ds;
19039         if (!ds) {
19040             return;
19041         }
19042         switch(which){
19043             case "first":
19044                 ds.load({params:{start: 0, limit: this.pageSize}});
19045             break;
19046             case "prev":
19047                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
19048             break;
19049             case "next":
19050                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
19051             break;
19052             case "last":
19053                 var total = ds.getTotalCount();
19054                 var extra = total % this.pageSize;
19055                 var lastStart = extra ? (total - extra) : total-this.pageSize;
19056                 ds.load({params:{start: lastStart, limit: this.pageSize}});
19057             break;
19058             case "refresh":
19059                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
19060             break;
19061         }
19062     },
19063
19064     /**
19065      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
19066      * @param {Roo.data.Store} store The data store to unbind
19067      */
19068     unbind : function(ds){
19069         ds.un("beforeload", this.beforeLoad, this);
19070         ds.un("load", this.onLoad, this);
19071         ds.un("loadexception", this.onLoadError, this);
19072         ds.un("remove", this.updateInfo, this);
19073         ds.un("add", this.updateInfo, this);
19074         this.ds = undefined;
19075     },
19076
19077     /**
19078      * Binds the paging toolbar to the specified {@link Roo.data.Store}
19079      * @param {Roo.data.Store} store The data store to bind
19080      */
19081     bind : function(ds){
19082         ds.on("beforeload", this.beforeLoad, this);
19083         ds.on("load", this.onLoad, this);
19084         ds.on("loadexception", this.onLoadError, this);
19085         ds.on("remove", this.updateInfo, this);
19086         ds.on("add", this.updateInfo, this);
19087         this.ds = ds;
19088     }
19089 });/*
19090  * - LGPL
19091  *
19092  * element
19093  * 
19094  */
19095
19096 /**
19097  * @class Roo.bootstrap.MessageBar
19098  * @extends Roo.bootstrap.Component
19099  * Bootstrap MessageBar class
19100  * @cfg {String} html contents of the MessageBar
19101  * @cfg {String} weight (info | success | warning | danger) default info
19102  * @cfg {String} beforeClass insert the bar before the given class
19103  * @cfg {Boolean} closable (true | false) default false
19104  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
19105  * 
19106  * @constructor
19107  * Create a new Element
19108  * @param {Object} config The config object
19109  */
19110
19111 Roo.bootstrap.MessageBar = function(config){
19112     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
19113 };
19114
19115 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
19116     
19117     html: '',
19118     weight: 'info',
19119     closable: false,
19120     fixed: false,
19121     beforeClass: 'bootstrap-sticky-wrap',
19122     
19123     getAutoCreate : function(){
19124         
19125         var cfg = {
19126             tag: 'div',
19127             cls: 'alert alert-dismissable alert-' + this.weight,
19128             cn: [
19129                 {
19130                     tag: 'span',
19131                     cls: 'message',
19132                     html: this.html || ''
19133                 }
19134             ]
19135         }
19136         
19137         if(this.fixed){
19138             cfg.cls += ' alert-messages-fixed';
19139         }
19140         
19141         if(this.closable){
19142             cfg.cn.push({
19143                 tag: 'button',
19144                 cls: 'close',
19145                 html: 'x'
19146             });
19147         }
19148         
19149         return cfg;
19150     },
19151     
19152     onRender : function(ct, position)
19153     {
19154         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19155         
19156         if(!this.el){
19157             var cfg = Roo.apply({},  this.getAutoCreate());
19158             cfg.id = Roo.id();
19159             
19160             if (this.cls) {
19161                 cfg.cls += ' ' + this.cls;
19162             }
19163             if (this.style) {
19164                 cfg.style = this.style;
19165             }
19166             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
19167             
19168             this.el.setVisibilityMode(Roo.Element.DISPLAY);
19169         }
19170         
19171         this.el.select('>button.close').on('click', this.hide, this);
19172         
19173     },
19174     
19175     show : function()
19176     {
19177         if (!this.rendered) {
19178             this.render();
19179         }
19180         
19181         this.el.show();
19182         
19183         this.fireEvent('show', this);
19184         
19185     },
19186     
19187     hide : function()
19188     {
19189         if (!this.rendered) {
19190             this.render();
19191         }
19192         
19193         this.el.hide();
19194         
19195         this.fireEvent('hide', this);
19196     },
19197     
19198     update : function()
19199     {
19200 //        var e = this.el.dom.firstChild;
19201 //        
19202 //        if(this.closable){
19203 //            e = e.nextSibling;
19204 //        }
19205 //        
19206 //        e.data = this.html || '';
19207
19208         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
19209     }
19210    
19211 });
19212
19213  
19214
19215      /*
19216  * - LGPL
19217  *
19218  * Graph
19219  * 
19220  */
19221
19222
19223 /**
19224  * @class Roo.bootstrap.Graph
19225  * @extends Roo.bootstrap.Component
19226  * Bootstrap Graph class
19227 > Prameters
19228  -sm {number} sm 4
19229  -md {number} md 5
19230  @cfg {String} graphtype  bar | vbar | pie
19231  @cfg {number} g_x coodinator | centre x (pie)
19232  @cfg {number} g_y coodinator | centre y (pie)
19233  @cfg {number} g_r radius (pie)
19234  @cfg {number} g_height height of the chart (respected by all elements in the set)
19235  @cfg {number} g_width width of the chart (respected by all elements in the set)
19236  @cfg {Object} title The title of the chart
19237     
19238  -{Array}  values
19239  -opts (object) options for the chart 
19240      o {
19241      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
19242      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
19243      o vgutter (number)
19244      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.
19245      o stacked (boolean) whether or not to tread values as in a stacked bar chart
19246      o to
19247      o stretch (boolean)
19248      o }
19249  -opts (object) options for the pie
19250      o{
19251      o cut
19252      o startAngle (number)
19253      o endAngle (number)
19254      } 
19255  *
19256  * @constructor
19257  * Create a new Input
19258  * @param {Object} config The config object
19259  */
19260
19261 Roo.bootstrap.Graph = function(config){
19262     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
19263     
19264     this.addEvents({
19265         // img events
19266         /**
19267          * @event click
19268          * The img click event for the img.
19269          * @param {Roo.EventObject} e
19270          */
19271         "click" : true
19272     });
19273 };
19274
19275 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
19276     
19277     sm: 4,
19278     md: 5,
19279     graphtype: 'bar',
19280     g_height: 250,
19281     g_width: 400,
19282     g_x: 50,
19283     g_y: 50,
19284     g_r: 30,
19285     opts:{
19286         //g_colors: this.colors,
19287         g_type: 'soft',
19288         g_gutter: '20%'
19289
19290     },
19291     title : false,
19292
19293     getAutoCreate : function(){
19294         
19295         var cfg = {
19296             tag: 'div',
19297             html : null
19298         }
19299         
19300         
19301         return  cfg;
19302     },
19303
19304     onRender : function(ct,position){
19305         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
19306         this.raphael = Raphael(this.el.dom);
19307         
19308                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19309                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19310                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19311                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
19312                 /*
19313                 r.text(160, 10, "Single Series Chart").attr(txtattr);
19314                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
19315                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
19316                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
19317                 
19318                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
19319                 r.barchart(330, 10, 300, 220, data1);
19320                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
19321                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
19322                 */
19323                 
19324                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19325                 // r.barchart(30, 30, 560, 250,  xdata, {
19326                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
19327                 //     axis : "0 0 1 1",
19328                 //     axisxlabels :  xdata
19329                 //     //yvalues : cols,
19330                    
19331                 // });
19332 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19333 //        
19334 //        this.load(null,xdata,{
19335 //                axis : "0 0 1 1",
19336 //                axisxlabels :  xdata
19337 //                });
19338
19339     },
19340
19341     load : function(graphtype,xdata,opts){
19342         this.raphael.clear();
19343         if(!graphtype) {
19344             graphtype = this.graphtype;
19345         }
19346         if(!opts){
19347             opts = this.opts;
19348         }
19349         var r = this.raphael,
19350             fin = function () {
19351                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
19352             },
19353             fout = function () {
19354                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
19355             },
19356             pfin = function() {
19357                 this.sector.stop();
19358                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
19359
19360                 if (this.label) {
19361                     this.label[0].stop();
19362                     this.label[0].attr({ r: 7.5 });
19363                     this.label[1].attr({ "font-weight": 800 });
19364                 }
19365             },
19366             pfout = function() {
19367                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
19368
19369                 if (this.label) {
19370                     this.label[0].animate({ r: 5 }, 500, "bounce");
19371                     this.label[1].attr({ "font-weight": 400 });
19372                 }
19373             };
19374
19375         switch(graphtype){
19376             case 'bar':
19377                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
19378                 break;
19379             case 'hbar':
19380                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
19381                 break;
19382             case 'pie':
19383 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
19384 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
19385 //            
19386                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
19387                 
19388                 break;
19389
19390         }
19391         
19392         if(this.title){
19393             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
19394         }
19395         
19396     },
19397     
19398     setTitle: function(o)
19399     {
19400         this.title = o;
19401     },
19402     
19403     initEvents: function() {
19404         
19405         if(!this.href){
19406             this.el.on('click', this.onClick, this);
19407         }
19408     },
19409     
19410     onClick : function(e)
19411     {
19412         Roo.log('img onclick');
19413         this.fireEvent('click', this, e);
19414     }
19415    
19416 });
19417
19418  
19419 /*
19420  * - LGPL
19421  *
19422  * numberBox
19423  * 
19424  */
19425 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19426
19427 /**
19428  * @class Roo.bootstrap.dash.NumberBox
19429  * @extends Roo.bootstrap.Component
19430  * Bootstrap NumberBox class
19431  * @cfg {String} bgcolor Box background color, such as (aqua | green | yellow | red etc..) Default aqua
19432  * @cfg {String} headline Box headline
19433  * @cfg {String} content Box content
19434  * @cfg {String} icon Box icon
19435  * @cfg {String} footer Footer text
19436  * @cfg {String} fhref Footer href
19437  * 
19438  * @constructor
19439  * Create a new NumberBox
19440  * @param {Object} config The config object
19441  */
19442
19443
19444 Roo.bootstrap.dash.NumberBox = function(config){
19445     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
19446     
19447 };
19448
19449 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
19450     
19451     bgcolor : 'aqua',
19452     headline : '',
19453     content : '',
19454     icon : '',
19455     footer : '',
19456     fhref : '',
19457     ficon : '',
19458     
19459     getAutoCreate : function(){
19460         
19461         var cfg = {
19462             tag : 'div',
19463             cls : 'small-box bg-' + this.bgcolor,
19464             cn : [
19465                 {
19466                     tag : 'div',
19467                     cls : 'inner',
19468                     cn :[
19469                         {
19470                             tag : 'h3',
19471                             cls : 'roo-headline',
19472                             html : this.headline
19473                         },
19474                         {
19475                             tag : 'p',
19476                             cls : 'roo-content',
19477                             html : this.content
19478                         }
19479                     ]
19480                 }
19481             ]
19482         }
19483         
19484         if(this.icon){
19485             cfg.cn.push({
19486                 tag : 'div',
19487                 cls : 'icon',
19488                 cn :[
19489                     {
19490                         tag : 'i',
19491                         cls : 'ion ' + this.icon
19492                     }
19493                 ]
19494             });
19495         }
19496         
19497         if(this.footer){
19498             var footer = {
19499                 tag : 'a',
19500                 cls : 'small-box-footer',
19501                 href : this.fhref || '#',
19502                 html : this.footer
19503             };
19504             
19505             cfg.cn.push(footer);
19506             
19507         }
19508         
19509         return  cfg;
19510     },
19511
19512     onRender : function(ct,position){
19513         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
19514
19515
19516        
19517                 
19518     },
19519
19520     setHeadline: function (value)
19521     {
19522         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
19523     },
19524     
19525     setFooter: function (value, href)
19526     {
19527         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
19528         
19529         if(href){
19530             this.el.select('a.small-box-footer',true).first().attr('href', href);
19531         }
19532         
19533     },
19534
19535     setContent: function (value)
19536     {
19537         this.el.select('.roo-content',true).first().dom.innerHTML = value;
19538     },
19539
19540     initEvents: function() 
19541     {   
19542         
19543     }
19544     
19545 });
19546
19547  
19548 /*
19549  * - LGPL
19550  *
19551  * TabBox
19552  * 
19553  */
19554 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19555
19556 /**
19557  * @class Roo.bootstrap.dash.TabBox
19558  * @extends Roo.bootstrap.Component
19559  * Bootstrap TabBox class
19560  * @cfg {String} title Title of the TabBox
19561  * @cfg {String} icon Icon of the TabBox
19562  * @cfg {Boolean} showtabs (true|false) show the tabs default true
19563  * 
19564  * @constructor
19565  * Create a new TabBox
19566  * @param {Object} config The config object
19567  */
19568
19569
19570 Roo.bootstrap.dash.TabBox = function(config){
19571     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
19572     this.addEvents({
19573         // raw events
19574         /**
19575          * @event addpane
19576          * When a pane is added
19577          * @param {Roo.bootstrap.dash.TabPane} pane
19578          */
19579         "addpane" : true
19580          
19581     });
19582 };
19583
19584 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
19585
19586     title : '',
19587     icon : false,
19588     showtabs : true,
19589     
19590     getChildContainer : function()
19591     {
19592         return this.el.select('.tab-content', true).first();
19593     },
19594     
19595     getAutoCreate : function(){
19596         
19597         var header = {
19598             tag: 'li',
19599             cls: 'pull-left header',
19600             html: this.title,
19601             cn : []
19602         };
19603         
19604         if(this.icon){
19605             header.cn.push({
19606                 tag: 'i',
19607                 cls: 'fa ' + this.icon
19608             });
19609         }
19610         
19611         
19612         var cfg = {
19613             tag: 'div',
19614             cls: 'nav-tabs-custom',
19615             cn: [
19616                 {
19617                     tag: 'ul',
19618                     cls: 'nav nav-tabs pull-right',
19619                     cn: [
19620                         header
19621                     ]
19622                 },
19623                 {
19624                     tag: 'div',
19625                     cls: 'tab-content no-padding',
19626                     cn: []
19627                 }
19628             ]
19629         }
19630
19631         return  cfg;
19632     },
19633     initEvents : function()
19634     {
19635         //Roo.log('add add pane handler');
19636         this.on('addpane', this.onAddPane, this);
19637     },
19638      /**
19639      * Updates the box title
19640      * @param {String} html to set the title to.
19641      */
19642     setTitle : function(value)
19643     {
19644         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
19645     },
19646     onAddPane : function(pane)
19647     {
19648         //Roo.log('addpane');
19649         //Roo.log(pane);
19650         // tabs are rendere left to right..
19651         if(!this.showtabs){
19652             return;
19653         }
19654         
19655         var ctr = this.el.select('.nav-tabs', true).first();
19656          
19657          
19658         var existing = ctr.select('.nav-tab',true);
19659         var qty = existing.getCount();;
19660         
19661         
19662         var tab = ctr.createChild({
19663             tag : 'li',
19664             cls : 'nav-tab' + (qty ? '' : ' active'),
19665             cn : [
19666                 {
19667                     tag : 'a',
19668                     href:'#',
19669                     html : pane.title
19670                 }
19671             ]
19672         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
19673         pane.tab = tab;
19674         
19675         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
19676         if (!qty) {
19677             pane.el.addClass('active');
19678         }
19679         
19680                 
19681     },
19682     onTabClick : function(ev,un,ob,pane)
19683     {
19684         //Roo.log('tab - prev default');
19685         ev.preventDefault();
19686         
19687         
19688         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
19689         pane.tab.addClass('active');
19690         //Roo.log(pane.title);
19691         this.getChildContainer().select('.tab-pane',true).removeClass('active');
19692         // technically we should have a deactivate event.. but maybe add later.
19693         // and it should not de-activate the selected tab...
19694         
19695         pane.el.addClass('active');
19696         pane.fireEvent('activate');
19697         
19698         
19699     }
19700     
19701     
19702 });
19703
19704  
19705 /*
19706  * - LGPL
19707  *
19708  * Tab pane
19709  * 
19710  */
19711 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19712 /**
19713  * @class Roo.bootstrap.TabPane
19714  * @extends Roo.bootstrap.Component
19715  * Bootstrap TabPane class
19716  * @cfg {Boolean} active (false | true) Default false
19717  * @cfg {String} title title of panel
19718
19719  * 
19720  * @constructor
19721  * Create a new TabPane
19722  * @param {Object} config The config object
19723  */
19724
19725 Roo.bootstrap.dash.TabPane = function(config){
19726     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
19727     
19728 };
19729
19730 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
19731     
19732     active : false,
19733     title : '',
19734     
19735     // the tabBox that this is attached to.
19736     tab : false,
19737      
19738     getAutoCreate : function() 
19739     {
19740         var cfg = {
19741             tag: 'div',
19742             cls: 'tab-pane'
19743         }
19744         
19745         if(this.active){
19746             cfg.cls += ' active';
19747         }
19748         
19749         return cfg;
19750     },
19751     initEvents  : function()
19752     {
19753         //Roo.log('trigger add pane handler');
19754         this.parent().fireEvent('addpane', this)
19755     },
19756     
19757      /**
19758      * Updates the tab title 
19759      * @param {String} html to set the title to.
19760      */
19761     setTitle: function(str)
19762     {
19763         if (!this.tab) {
19764             return;
19765         }
19766         this.title = str;
19767         this.tab.select('a'.true).first().dom.innerHTML = str;
19768         
19769     }
19770     
19771     
19772     
19773 });
19774
19775  
19776
19777
19778  /*
19779  * - LGPL
19780  *
19781  * menu
19782  * 
19783  */
19784 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
19785
19786 /**
19787  * @class Roo.bootstrap.menu.Menu
19788  * @extends Roo.bootstrap.Component
19789  * Bootstrap Menu class - container for Menu
19790  * @cfg {String} html Text of the menu
19791  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
19792  * @cfg {String} icon Font awesome icon
19793  * @cfg {String} pos Menu align to (top | bottom) default bottom
19794  * 
19795  * 
19796  * @constructor
19797  * Create a new Menu
19798  * @param {Object} config The config object
19799  */
19800
19801
19802 Roo.bootstrap.menu.Menu = function(config){
19803     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
19804     
19805     this.addEvents({
19806         /**
19807          * @event beforeshow
19808          * Fires before this menu is displayed
19809          * @param {Roo.bootstrap.menu.Menu} this
19810          */
19811         beforeshow : true,
19812         /**
19813          * @event beforehide
19814          * Fires before this menu is hidden
19815          * @param {Roo.bootstrap.menu.Menu} this
19816          */
19817         beforehide : true,
19818         /**
19819          * @event show
19820          * Fires after this menu is displayed
19821          * @param {Roo.bootstrap.menu.Menu} this
19822          */
19823         show : true,
19824         /**
19825          * @event hide
19826          * Fires after this menu is hidden
19827          * @param {Roo.bootstrap.menu.Menu} this
19828          */
19829         hide : true,
19830         /**
19831          * @event click
19832          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19833          * @param {Roo.bootstrap.menu.Menu} this
19834          * @param {Roo.EventObject} e
19835          */
19836         click : true
19837     });
19838     
19839 };
19840
19841 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
19842     
19843     submenu : false,
19844     html : '',
19845     weight : 'default',
19846     icon : false,
19847     pos : 'bottom',
19848     
19849     
19850     getChildContainer : function() {
19851         if(this.isSubMenu){
19852             return this.el;
19853         }
19854         
19855         return this.el.select('ul.dropdown-menu', true).first();  
19856     },
19857     
19858     getAutoCreate : function()
19859     {
19860         var text = [
19861             {
19862                 tag : 'span',
19863                 cls : 'roo-menu-text',
19864                 html : this.html
19865             }
19866         ];
19867         
19868         if(this.icon){
19869             text.unshift({
19870                 tag : 'i',
19871                 cls : 'fa ' + this.icon
19872             })
19873         }
19874         
19875         
19876         var cfg = {
19877             tag : 'div',
19878             cls : 'btn-group',
19879             cn : [
19880                 {
19881                     tag : 'button',
19882                     cls : 'dropdown-button btn btn-' + this.weight,
19883                     cn : text
19884                 },
19885                 {
19886                     tag : 'button',
19887                     cls : 'dropdown-toggle btn btn-' + this.weight,
19888                     cn : [
19889                         {
19890                             tag : 'span',
19891                             cls : 'caret'
19892                         }
19893                     ]
19894                 },
19895                 {
19896                     tag : 'ul',
19897                     cls : 'dropdown-menu'
19898                 }
19899             ]
19900             
19901         };
19902         
19903         if(this.pos == 'top'){
19904             cfg.cls += ' dropup';
19905         }
19906         
19907         if(this.isSubMenu){
19908             cfg = {
19909                 tag : 'ul',
19910                 cls : 'dropdown-menu'
19911             }
19912         }
19913         
19914         return cfg;
19915     },
19916     
19917     onRender : function(ct, position)
19918     {
19919         this.isSubMenu = ct.hasClass('dropdown-submenu');
19920         
19921         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
19922     },
19923     
19924     initEvents : function() 
19925     {
19926         if(this.isSubMenu){
19927             return;
19928         }
19929         
19930         this.hidden = true;
19931         
19932         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
19933         this.triggerEl.on('click', this.onTriggerPress, this);
19934         
19935         this.buttonEl = this.el.select('button.dropdown-button', true).first();
19936         this.buttonEl.on('click', this.onClick, this);
19937         
19938     },
19939     
19940     list : function()
19941     {
19942         if(this.isSubMenu){
19943             return this.el;
19944         }
19945         
19946         return this.el.select('ul.dropdown-menu', true).first();
19947     },
19948     
19949     onClick : function(e)
19950     {
19951         this.fireEvent("click", this, e);
19952     },
19953     
19954     onTriggerPress  : function(e)
19955     {   
19956         if (this.isVisible()) {
19957             this.hide();
19958         } else {
19959             this.show();
19960         }
19961     },
19962     
19963     isVisible : function(){
19964         return !this.hidden;
19965     },
19966     
19967     show : function()
19968     {
19969         this.fireEvent("beforeshow", this);
19970         
19971         this.hidden = false;
19972         this.el.addClass('open');
19973         
19974         Roo.get(document).on("mouseup", this.onMouseUp, this);
19975         
19976         this.fireEvent("show", this);
19977         
19978         
19979     },
19980     
19981     hide : function()
19982     {
19983         this.fireEvent("beforehide", this);
19984         
19985         this.hidden = true;
19986         this.el.removeClass('open');
19987         
19988         Roo.get(document).un("mouseup", this.onMouseUp);
19989         
19990         this.fireEvent("hide", this);
19991     },
19992     
19993     onMouseUp : function()
19994     {
19995         this.hide();
19996     }
19997     
19998 });
19999
20000  
20001  /*
20002  * - LGPL
20003  *
20004  * menu item
20005  * 
20006  */
20007 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20008
20009 /**
20010  * @class Roo.bootstrap.menu.Item
20011  * @extends Roo.bootstrap.Component
20012  * Bootstrap MenuItem class
20013  * @cfg {Boolean} submenu (true | false) default false
20014  * @cfg {String} html text of the item
20015  * @cfg {String} href the link
20016  * @cfg {Boolean} disable (true | false) default false
20017  * @cfg {Boolean} preventDefault (true | false) default true
20018  * @cfg {String} icon Font awesome icon
20019  * @cfg {String} pos Submenu align to (left | right) default right 
20020  * 
20021  * 
20022  * @constructor
20023  * Create a new Item
20024  * @param {Object} config The config object
20025  */
20026
20027
20028 Roo.bootstrap.menu.Item = function(config){
20029     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
20030     this.addEvents({
20031         /**
20032          * @event mouseover
20033          * Fires when the mouse is hovering over this menu
20034          * @param {Roo.bootstrap.menu.Item} this
20035          * @param {Roo.EventObject} e
20036          */
20037         mouseover : true,
20038         /**
20039          * @event mouseout
20040          * Fires when the mouse exits this menu
20041          * @param {Roo.bootstrap.menu.Item} this
20042          * @param {Roo.EventObject} e
20043          */
20044         mouseout : true,
20045         // raw events
20046         /**
20047          * @event click
20048          * The raw click event for the entire grid.
20049          * @param {Roo.EventObject} e
20050          */
20051         click : true
20052     });
20053 };
20054
20055 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
20056     
20057     submenu : false,
20058     href : '',
20059     html : '',
20060     preventDefault: true,
20061     disable : false,
20062     icon : false,
20063     pos : 'right',
20064     
20065     getAutoCreate : function()
20066     {
20067         var text = [
20068             {
20069                 tag : 'span',
20070                 cls : 'roo-menu-item-text',
20071                 html : this.html
20072             }
20073         ];
20074         
20075         if(this.icon){
20076             text.unshift({
20077                 tag : 'i',
20078                 cls : 'fa ' + this.icon
20079             })
20080         }
20081         
20082         var cfg = {
20083             tag : 'li',
20084             cn : [
20085                 {
20086                     tag : 'a',
20087                     href : this.href || '#',
20088                     cn : text
20089                 }
20090             ]
20091         };
20092         
20093         if(this.disable){
20094             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
20095         }
20096         
20097         if(this.submenu){
20098             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
20099             
20100             if(this.pos == 'left'){
20101                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
20102             }
20103         }
20104         
20105         return cfg;
20106     },
20107     
20108     initEvents : function() 
20109     {
20110         this.el.on('mouseover', this.onMouseOver, this);
20111         this.el.on('mouseout', this.onMouseOut, this);
20112         
20113         this.el.select('a', true).first().on('click', this.onClick, this);
20114         
20115     },
20116     
20117     onClick : function(e)
20118     {
20119         if(this.preventDefault){
20120             e.preventDefault();
20121         }
20122         
20123         this.fireEvent("click", this, e);
20124     },
20125     
20126     onMouseOver : function(e)
20127     {
20128         if(this.submenu && this.pos == 'left'){
20129             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
20130         }
20131         
20132         this.fireEvent("mouseover", this, e);
20133     },
20134     
20135     onMouseOut : function(e)
20136     {
20137         this.fireEvent("mouseout", this, e);
20138     }
20139 });
20140
20141  
20142
20143  /*
20144  * - LGPL
20145  *
20146  * menu separator
20147  * 
20148  */
20149 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20150
20151 /**
20152  * @class Roo.bootstrap.menu.Separator
20153  * @extends Roo.bootstrap.Component
20154  * Bootstrap Separator class
20155  * 
20156  * @constructor
20157  * Create a new Separator
20158  * @param {Object} config The config object
20159  */
20160
20161
20162 Roo.bootstrap.menu.Separator = function(config){
20163     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
20164 };
20165
20166 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
20167     
20168     getAutoCreate : function(){
20169         var cfg = {
20170             tag : 'li',
20171             cls: 'divider'
20172         };
20173         
20174         return cfg;
20175     }
20176    
20177 });
20178
20179  
20180
20181