docs/default.css
[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         Roo.log("fire event clicked");
3563         if(this.fireEvent('click', this, e) === false){
3564             return;
3565         };
3566         
3567         if (['tabs','pills'].indexOf(this.parent().type)!==-1) {
3568             if (typeof(this.parent().setActiveItem) !== 'undefined') {
3569                 this.parent().setActiveItem(this);
3570             }
3571         } 
3572     },
3573     
3574     isActive: function () {
3575         return this.active
3576     },
3577     setActive : function(state, fire, is_was_active)
3578     {
3579         if (this.active && !state & this.navId) {
3580             this.was_active = true;
3581             var nv = Roo.bootstrap.NavGroup.get(this.navId);
3582             if (nv) {
3583                 nv.clearWasActive(this);
3584             }
3585             
3586         }
3587         this.active = state;
3588         
3589         if (!state ) {
3590             this.el.removeClass('active');
3591         } else if (!this.el.hasClass('active')) {
3592             this.el.addClass('active');
3593         }
3594         if (fire) {
3595             this.fireEvent('changed', this, state);
3596         }
3597         
3598         // show a panel if it's registered and related..
3599         
3600         if (!this.navId || !this.tabId || !state || is_was_active) {
3601             return;
3602         }
3603         
3604         var tg = Roo.bootstrap.TabGroup.get(this.navId);
3605         if (!tg) {
3606             return;
3607         }
3608         var pan = tg.getPanelByName(this.tabId);
3609         if (!pan) {
3610             return;
3611         }
3612         // if we can not flip to new panel - go back to old nav highlight..
3613         if (false == tg.showPanel(pan)) {
3614             var nv = Roo.bootstrap.NavGroup.get(this.navId);
3615             if (nv) {
3616                 var onav = nv.getWasActive();
3617                 if (onav) {
3618                     onav.setActive(true, false, true);
3619                 }
3620             }
3621             
3622         }
3623         
3624         
3625         
3626     },
3627      // this should not be here...
3628     setDisabled : function(state)
3629     {
3630         this.disabled = state;
3631         if (!state ) {
3632             this.el.removeClass('disabled');
3633         } else if (!this.el.hasClass('disabled')) {
3634             this.el.addClass('disabled');
3635         }
3636         
3637     }
3638 });
3639  
3640
3641  /*
3642  * - LGPL
3643  *
3644  * sidebar item
3645  *
3646  *  li
3647  *    <span> icon </span>
3648  *    <span> text </span>
3649  *    <span>badge </span>
3650  */
3651
3652 /**
3653  * @class Roo.bootstrap.NavSidebarItem
3654  * @extends Roo.bootstrap.NavItem
3655  * Bootstrap Navbar.NavSidebarItem class
3656  * @constructor
3657  * Create a new Navbar Button
3658  * @param {Object} config The config object
3659  */
3660 Roo.bootstrap.NavSidebarItem = function(config){
3661     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
3662     this.addEvents({
3663         // raw events
3664         /**
3665          * @event click
3666          * The raw click event for the entire grid.
3667          * @param {Roo.EventObject} e
3668          */
3669         "click" : true,
3670          /**
3671             * @event changed
3672             * Fires when the active item active state changes
3673             * @param {Roo.bootstrap.NavSidebarItem} this
3674             * @param {boolean} state the new state
3675              
3676          */
3677         'changed': true
3678     });
3679    
3680 };
3681
3682 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
3683     
3684     
3685     getAutoCreate : function(){
3686         
3687         
3688         var a = {
3689                 tag: 'a',
3690                 href : this.href || '#',
3691                 cls: '',
3692                 html : '',
3693                 cn : []
3694         };
3695         var cfg = {
3696             tag: 'li',
3697             cls: '',
3698             cn: [ a ]
3699         }
3700         var span = {
3701             tag: 'span',
3702             html : this.html || ''
3703         }
3704         
3705         
3706         if (this.active) {
3707             cfg.cls += ' active';
3708         }
3709         
3710         // left icon..
3711         if (this.glyphicon || this.icon) {
3712             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
3713             a.cn.push({ tag : 'i', cls : c }) ;
3714         }
3715         // html..
3716         a.cn.push(span);
3717         // then badge..
3718         if (this.badge !== '') {
3719             a.cn.push({ tag: 'span',  cls : 'badge pull-right ' + (this.badgecls || ''), html: this.badge }); 
3720         }
3721         // fi
3722         if (this.menu) {
3723             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
3724             a.cls += 'dropdown-toggle treeview' ;
3725             
3726         }
3727         
3728         
3729         
3730         return cfg;
3731          
3732            
3733     }
3734    
3735      
3736  
3737 });
3738  
3739
3740  /*
3741  * - LGPL
3742  *
3743  * row
3744  * 
3745  */
3746
3747 /**
3748  * @class Roo.bootstrap.Row
3749  * @extends Roo.bootstrap.Component
3750  * Bootstrap Row class (contains columns...)
3751  * 
3752  * @constructor
3753  * Create a new Row
3754  * @param {Object} config The config object
3755  */
3756
3757 Roo.bootstrap.Row = function(config){
3758     Roo.bootstrap.Row.superclass.constructor.call(this, config);
3759 };
3760
3761 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
3762     
3763     getAutoCreate : function(){
3764        return {
3765             cls: 'row clearfix'
3766        };
3767     }
3768     
3769     
3770 });
3771
3772  
3773
3774  /*
3775  * - LGPL
3776  *
3777  * element
3778  * 
3779  */
3780
3781 /**
3782  * @class Roo.bootstrap.Element
3783  * @extends Roo.bootstrap.Component
3784  * Bootstrap Element class
3785  * @cfg {String} html contents of the element
3786  * @cfg {String} tag tag of the element
3787  * @cfg {String} cls class of the element
3788  * 
3789  * @constructor
3790  * Create a new Element
3791  * @param {Object} config The config object
3792  */
3793
3794 Roo.bootstrap.Element = function(config){
3795     Roo.bootstrap.Element.superclass.constructor.call(this, config);
3796 };
3797
3798 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
3799     
3800     tag: 'div',
3801     cls: '',
3802     html: '',
3803      
3804     
3805     getAutoCreate : function(){
3806         
3807         var cfg = {
3808             tag: this.tag,
3809             cls: this.cls,
3810             html: this.html
3811         }
3812         
3813         
3814         
3815         return cfg;
3816     }
3817    
3818 });
3819
3820  
3821
3822  /*
3823  * - LGPL
3824  *
3825  * pagination
3826  * 
3827  */
3828
3829 /**
3830  * @class Roo.bootstrap.Pagination
3831  * @extends Roo.bootstrap.Component
3832  * Bootstrap Pagination class
3833  * @cfg {String} size xs | sm | md | lg
3834  * @cfg {Boolean} inverse false | true
3835  * 
3836  * @constructor
3837  * Create a new Pagination
3838  * @param {Object} config The config object
3839  */
3840
3841 Roo.bootstrap.Pagination = function(config){
3842     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
3843 };
3844
3845 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
3846     
3847     cls: false,
3848     size: false,
3849     inverse: false,
3850     
3851     getAutoCreate : function(){
3852         var cfg = {
3853             tag: 'ul',
3854                 cls: 'pagination'
3855         };
3856         if (this.inverse) {
3857             cfg.cls += ' inverse';
3858         }
3859         if (this.html) {
3860             cfg.html=this.html;
3861         }
3862         if (this.cls) {
3863             cfg.cls += " " + this.cls;
3864         }
3865         return cfg;
3866     }
3867    
3868 });
3869
3870  
3871
3872  /*
3873  * - LGPL
3874  *
3875  * Pagination item
3876  * 
3877  */
3878
3879
3880 /**
3881  * @class Roo.bootstrap.PaginationItem
3882  * @extends Roo.bootstrap.Component
3883  * Bootstrap PaginationItem class
3884  * @cfg {String} html text
3885  * @cfg {String} href the link
3886  * @cfg {Boolean} preventDefault (true | false) default true
3887  * @cfg {Boolean} active (true | false) default false
3888  * 
3889  * 
3890  * @constructor
3891  * Create a new PaginationItem
3892  * @param {Object} config The config object
3893  */
3894
3895
3896 Roo.bootstrap.PaginationItem = function(config){
3897     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
3898     this.addEvents({
3899         // raw events
3900         /**
3901          * @event click
3902          * The raw click event for the entire grid.
3903          * @param {Roo.EventObject} e
3904          */
3905         "click" : true
3906     });
3907 };
3908
3909 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
3910     
3911     href : false,
3912     html : false,
3913     preventDefault: true,
3914     active : false,
3915     cls : false,
3916     
3917     getAutoCreate : function(){
3918         var cfg= {
3919             tag: 'li',
3920             cn: [
3921                 {
3922                     tag : 'a',
3923                     href : this.href ? this.href : '#',
3924                     html : this.html ? this.html : ''
3925                 }
3926             ]
3927         };
3928         
3929         if(this.cls){
3930             cfg.cls = this.cls;
3931         }
3932         
3933         if(this.active){
3934             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
3935         }
3936         
3937         return cfg;
3938     },
3939     
3940     initEvents: function() {
3941         
3942         this.el.on('click', this.onClick, this);
3943         
3944     },
3945     onClick : function(e)
3946     {
3947         Roo.log('PaginationItem on click ');
3948         if(this.preventDefault){
3949             e.preventDefault();
3950         }
3951         
3952         this.fireEvent('click', this, e);
3953     }
3954    
3955 });
3956
3957  
3958
3959  /*
3960  * - LGPL
3961  *
3962  * slider
3963  * 
3964  */
3965
3966
3967 /**
3968  * @class Roo.bootstrap.Slider
3969  * @extends Roo.bootstrap.Component
3970  * Bootstrap Slider class
3971  *    
3972  * @constructor
3973  * Create a new Slider
3974  * @param {Object} config The config object
3975  */
3976
3977 Roo.bootstrap.Slider = function(config){
3978     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
3979 };
3980
3981 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
3982     
3983     getAutoCreate : function(){
3984         
3985         var cfg = {
3986             tag: 'div',
3987             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
3988             cn: [
3989                 {
3990                     tag: 'a',
3991                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
3992                 }
3993             ]
3994         }
3995         
3996         return cfg;
3997     }
3998    
3999 });
4000
4001  /*
4002  * Based on:
4003  * Ext JS Library 1.1.1
4004  * Copyright(c) 2006-2007, Ext JS, LLC.
4005  *
4006  * Originally Released Under LGPL - original licence link has changed is not relivant.
4007  *
4008  * Fork - LGPL
4009  * <script type="text/javascript">
4010  */
4011  
4012
4013 /**
4014  * @class Roo.grid.ColumnModel
4015  * @extends Roo.util.Observable
4016  * This is the default implementation of a ColumnModel used by the Grid. It defines
4017  * the columns in the grid.
4018  * <br>Usage:<br>
4019  <pre><code>
4020  var colModel = new Roo.grid.ColumnModel([
4021         {header: "Ticker", width: 60, sortable: true, locked: true},
4022         {header: "Company Name", width: 150, sortable: true},
4023         {header: "Market Cap.", width: 100, sortable: true},
4024         {header: "$ Sales", width: 100, sortable: true, renderer: money},
4025         {header: "Employees", width: 100, sortable: true, resizable: false}
4026  ]);
4027  </code></pre>
4028  * <p>
4029  
4030  * The config options listed for this class are options which may appear in each
4031  * individual column definition.
4032  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4033  * @constructor
4034  * @param {Object} config An Array of column config objects. See this class's
4035  * config objects for details.
4036 */
4037 Roo.grid.ColumnModel = function(config){
4038         /**
4039      * The config passed into the constructor
4040      */
4041     this.config = config;
4042     this.lookup = {};
4043
4044     // if no id, create one
4045     // if the column does not have a dataIndex mapping,
4046     // map it to the order it is in the config
4047     for(var i = 0, len = config.length; i < len; i++){
4048         var c = config[i];
4049         if(typeof c.dataIndex == "undefined"){
4050             c.dataIndex = i;
4051         }
4052         if(typeof c.renderer == "string"){
4053             c.renderer = Roo.util.Format[c.renderer];
4054         }
4055         if(typeof c.id == "undefined"){
4056             c.id = Roo.id();
4057         }
4058         if(c.editor && c.editor.xtype){
4059             c.editor  = Roo.factory(c.editor, Roo.grid);
4060         }
4061         if(c.editor && c.editor.isFormField){
4062             c.editor = new Roo.grid.GridEditor(c.editor);
4063         }
4064         this.lookup[c.id] = c;
4065     }
4066
4067     /**
4068      * The width of columns which have no width specified (defaults to 100)
4069      * @type Number
4070      */
4071     this.defaultWidth = 100;
4072
4073     /**
4074      * Default sortable of columns which have no sortable specified (defaults to false)
4075      * @type Boolean
4076      */
4077     this.defaultSortable = false;
4078
4079     this.addEvents({
4080         /**
4081              * @event widthchange
4082              * Fires when the width of a column changes.
4083              * @param {ColumnModel} this
4084              * @param {Number} columnIndex The column index
4085              * @param {Number} newWidth The new width
4086              */
4087             "widthchange": true,
4088         /**
4089              * @event headerchange
4090              * Fires when the text of a header changes.
4091              * @param {ColumnModel} this
4092              * @param {Number} columnIndex The column index
4093              * @param {Number} newText The new header text
4094              */
4095             "headerchange": true,
4096         /**
4097              * @event hiddenchange
4098              * Fires when a column is hidden or "unhidden".
4099              * @param {ColumnModel} this
4100              * @param {Number} columnIndex The column index
4101              * @param {Boolean} hidden true if hidden, false otherwise
4102              */
4103             "hiddenchange": true,
4104             /**
4105          * @event columnmoved
4106          * Fires when a column is moved.
4107          * @param {ColumnModel} this
4108          * @param {Number} oldIndex
4109          * @param {Number} newIndex
4110          */
4111         "columnmoved" : true,
4112         /**
4113          * @event columlockchange
4114          * Fires when a column's locked state is changed
4115          * @param {ColumnModel} this
4116          * @param {Number} colIndex
4117          * @param {Boolean} locked true if locked
4118          */
4119         "columnlockchange" : true
4120     });
4121     Roo.grid.ColumnModel.superclass.constructor.call(this);
4122 };
4123 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4124     /**
4125      * @cfg {String} header The header text to display in the Grid view.
4126      */
4127     /**
4128      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4129      * {@link Roo.data.Record} definition from which to draw the column's value. If not
4130      * specified, the column's index is used as an index into the Record's data Array.
4131      */
4132     /**
4133      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4134      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4135      */
4136     /**
4137      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4138      * Defaults to the value of the {@link #defaultSortable} property.
4139      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4140      */
4141     /**
4142      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
4143      */
4144     /**
4145      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
4146      */
4147     /**
4148      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4149      */
4150     /**
4151      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4152      */
4153     /**
4154      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4155      * given the cell's data value. See {@link #setRenderer}. If not specified, the
4156      * default renderer uses the raw data value. If an object is returned (bootstrap only)
4157      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4158      */
4159        /**
4160      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
4161      */
4162     /**
4163      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
4164      */
4165
4166     /**
4167      * Returns the id of the column at the specified index.
4168      * @param {Number} index The column index
4169      * @return {String} the id
4170      */
4171     getColumnId : function(index){
4172         return this.config[index].id;
4173     },
4174
4175     /**
4176      * Returns the column for a specified id.
4177      * @param {String} id The column id
4178      * @return {Object} the column
4179      */
4180     getColumnById : function(id){
4181         return this.lookup[id];
4182     },
4183
4184     
4185     /**
4186      * Returns the column for a specified dataIndex.
4187      * @param {String} dataIndex The column dataIndex
4188      * @return {Object|Boolean} the column or false if not found
4189      */
4190     getColumnByDataIndex: function(dataIndex){
4191         var index = this.findColumnIndex(dataIndex);
4192         return index > -1 ? this.config[index] : false;
4193     },
4194     
4195     /**
4196      * Returns the index for a specified column id.
4197      * @param {String} id The column id
4198      * @return {Number} the index, or -1 if not found
4199      */
4200     getIndexById : function(id){
4201         for(var i = 0, len = this.config.length; i < len; i++){
4202             if(this.config[i].id == id){
4203                 return i;
4204             }
4205         }
4206         return -1;
4207     },
4208     
4209     /**
4210      * Returns the index for a specified column dataIndex.
4211      * @param {String} dataIndex The column dataIndex
4212      * @return {Number} the index, or -1 if not found
4213      */
4214     
4215     findColumnIndex : function(dataIndex){
4216         for(var i = 0, len = this.config.length; i < len; i++){
4217             if(this.config[i].dataIndex == dataIndex){
4218                 return i;
4219             }
4220         }
4221         return -1;
4222     },
4223     
4224     
4225     moveColumn : function(oldIndex, newIndex){
4226         var c = this.config[oldIndex];
4227         this.config.splice(oldIndex, 1);
4228         this.config.splice(newIndex, 0, c);
4229         this.dataMap = null;
4230         this.fireEvent("columnmoved", this, oldIndex, newIndex);
4231     },
4232
4233     isLocked : function(colIndex){
4234         return this.config[colIndex].locked === true;
4235     },
4236
4237     setLocked : function(colIndex, value, suppressEvent){
4238         if(this.isLocked(colIndex) == value){
4239             return;
4240         }
4241         this.config[colIndex].locked = value;
4242         if(!suppressEvent){
4243             this.fireEvent("columnlockchange", this, colIndex, value);
4244         }
4245     },
4246
4247     getTotalLockedWidth : function(){
4248         var totalWidth = 0;
4249         for(var i = 0; i < this.config.length; i++){
4250             if(this.isLocked(i) && !this.isHidden(i)){
4251                 this.totalWidth += this.getColumnWidth(i);
4252             }
4253         }
4254         return totalWidth;
4255     },
4256
4257     getLockedCount : function(){
4258         for(var i = 0, len = this.config.length; i < len; i++){
4259             if(!this.isLocked(i)){
4260                 return i;
4261             }
4262         }
4263     },
4264
4265     /**
4266      * Returns the number of columns.
4267      * @return {Number}
4268      */
4269     getColumnCount : function(visibleOnly){
4270         if(visibleOnly === true){
4271             var c = 0;
4272             for(var i = 0, len = this.config.length; i < len; i++){
4273                 if(!this.isHidden(i)){
4274                     c++;
4275                 }
4276             }
4277             return c;
4278         }
4279         return this.config.length;
4280     },
4281
4282     /**
4283      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
4284      * @param {Function} fn
4285      * @param {Object} scope (optional)
4286      * @return {Array} result
4287      */
4288     getColumnsBy : function(fn, scope){
4289         var r = [];
4290         for(var i = 0, len = this.config.length; i < len; i++){
4291             var c = this.config[i];
4292             if(fn.call(scope||this, c, i) === true){
4293                 r[r.length] = c;
4294             }
4295         }
4296         return r;
4297     },
4298
4299     /**
4300      * Returns true if the specified column is sortable.
4301      * @param {Number} col The column index
4302      * @return {Boolean}
4303      */
4304     isSortable : function(col){
4305         if(typeof this.config[col].sortable == "undefined"){
4306             return this.defaultSortable;
4307         }
4308         return this.config[col].sortable;
4309     },
4310
4311     /**
4312      * Returns the rendering (formatting) function defined for the column.
4313      * @param {Number} col The column index.
4314      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
4315      */
4316     getRenderer : function(col){
4317         if(!this.config[col].renderer){
4318             return Roo.grid.ColumnModel.defaultRenderer;
4319         }
4320         return this.config[col].renderer;
4321     },
4322
4323     /**
4324      * Sets the rendering (formatting) function for a column.
4325      * @param {Number} col The column index
4326      * @param {Function} fn The function to use to process the cell's raw data
4327      * to return HTML markup for the grid view. The render function is called with
4328      * the following parameters:<ul>
4329      * <li>Data value.</li>
4330      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
4331      * <li>css A CSS style string to apply to the table cell.</li>
4332      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
4333      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
4334      * <li>Row index</li>
4335      * <li>Column index</li>
4336      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
4337      */
4338     setRenderer : function(col, fn){
4339         this.config[col].renderer = fn;
4340     },
4341
4342     /**
4343      * Returns the width for the specified column.
4344      * @param {Number} col The column index
4345      * @return {Number}
4346      */
4347     getColumnWidth : function(col){
4348         return this.config[col].width * 1 || this.defaultWidth;
4349     },
4350
4351     /**
4352      * Sets the width for a column.
4353      * @param {Number} col The column index
4354      * @param {Number} width The new width
4355      */
4356     setColumnWidth : function(col, width, suppressEvent){
4357         this.config[col].width = width;
4358         this.totalWidth = null;
4359         if(!suppressEvent){
4360              this.fireEvent("widthchange", this, col, width);
4361         }
4362     },
4363
4364     /**
4365      * Returns the total width of all columns.
4366      * @param {Boolean} includeHidden True to include hidden column widths
4367      * @return {Number}
4368      */
4369     getTotalWidth : function(includeHidden){
4370         if(!this.totalWidth){
4371             this.totalWidth = 0;
4372             for(var i = 0, len = this.config.length; i < len; i++){
4373                 if(includeHidden || !this.isHidden(i)){
4374                     this.totalWidth += this.getColumnWidth(i);
4375                 }
4376             }
4377         }
4378         return this.totalWidth;
4379     },
4380
4381     /**
4382      * Returns the header for the specified column.
4383      * @param {Number} col The column index
4384      * @return {String}
4385      */
4386     getColumnHeader : function(col){
4387         return this.config[col].header;
4388     },
4389
4390     /**
4391      * Sets the header for a column.
4392      * @param {Number} col The column index
4393      * @param {String} header The new header
4394      */
4395     setColumnHeader : function(col, header){
4396         this.config[col].header = header;
4397         this.fireEvent("headerchange", this, col, header);
4398     },
4399
4400     /**
4401      * Returns the tooltip for the specified column.
4402      * @param {Number} col The column index
4403      * @return {String}
4404      */
4405     getColumnTooltip : function(col){
4406             return this.config[col].tooltip;
4407     },
4408     /**
4409      * Sets the tooltip for a column.
4410      * @param {Number} col The column index
4411      * @param {String} tooltip The new tooltip
4412      */
4413     setColumnTooltip : function(col, tooltip){
4414             this.config[col].tooltip = tooltip;
4415     },
4416
4417     /**
4418      * Returns the dataIndex for the specified column.
4419      * @param {Number} col The column index
4420      * @return {Number}
4421      */
4422     getDataIndex : function(col){
4423         return this.config[col].dataIndex;
4424     },
4425
4426     /**
4427      * Sets the dataIndex for a column.
4428      * @param {Number} col The column index
4429      * @param {Number} dataIndex The new dataIndex
4430      */
4431     setDataIndex : function(col, dataIndex){
4432         this.config[col].dataIndex = dataIndex;
4433     },
4434
4435     
4436     
4437     /**
4438      * Returns true if the cell is editable.
4439      * @param {Number} colIndex The column index
4440      * @param {Number} rowIndex The row index
4441      * @return {Boolean}
4442      */
4443     isCellEditable : function(colIndex, rowIndex){
4444         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
4445     },
4446
4447     /**
4448      * Returns the editor defined for the cell/column.
4449      * return false or null to disable editing.
4450      * @param {Number} colIndex The column index
4451      * @param {Number} rowIndex The row index
4452      * @return {Object}
4453      */
4454     getCellEditor : function(colIndex, rowIndex){
4455         return this.config[colIndex].editor;
4456     },
4457
4458     /**
4459      * Sets if a column is editable.
4460      * @param {Number} col The column index
4461      * @param {Boolean} editable True if the column is editable
4462      */
4463     setEditable : function(col, editable){
4464         this.config[col].editable = editable;
4465     },
4466
4467
4468     /**
4469      * Returns true if the column is hidden.
4470      * @param {Number} colIndex The column index
4471      * @return {Boolean}
4472      */
4473     isHidden : function(colIndex){
4474         return this.config[colIndex].hidden;
4475     },
4476
4477
4478     /**
4479      * Returns true if the column width cannot be changed
4480      */
4481     isFixed : function(colIndex){
4482         return this.config[colIndex].fixed;
4483     },
4484
4485     /**
4486      * Returns true if the column can be resized
4487      * @return {Boolean}
4488      */
4489     isResizable : function(colIndex){
4490         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
4491     },
4492     /**
4493      * Sets if a column is hidden.
4494      * @param {Number} colIndex The column index
4495      * @param {Boolean} hidden True if the column is hidden
4496      */
4497     setHidden : function(colIndex, hidden){
4498         this.config[colIndex].hidden = hidden;
4499         this.totalWidth = null;
4500         this.fireEvent("hiddenchange", this, colIndex, hidden);
4501     },
4502
4503     /**
4504      * Sets the editor for a column.
4505      * @param {Number} col The column index
4506      * @param {Object} editor The editor object
4507      */
4508     setEditor : function(col, editor){
4509         this.config[col].editor = editor;
4510     }
4511 });
4512
4513 Roo.grid.ColumnModel.defaultRenderer = function(value){
4514         if(typeof value == "string" && value.length < 1){
4515             return "&#160;";
4516         }
4517         return value;
4518 };
4519
4520 // Alias for backwards compatibility
4521 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
4522 /*
4523  * Based on:
4524  * Ext JS Library 1.1.1
4525  * Copyright(c) 2006-2007, Ext JS, LLC.
4526  *
4527  * Originally Released Under LGPL - original licence link has changed is not relivant.
4528  *
4529  * Fork - LGPL
4530  * <script type="text/javascript">
4531  */
4532  
4533 /**
4534  * @class Roo.LoadMask
4535  * A simple utility class for generically masking elements while loading data.  If the element being masked has
4536  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
4537  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
4538  * element's UpdateManager load indicator and will be destroyed after the initial load.
4539  * @constructor
4540  * Create a new LoadMask
4541  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
4542  * @param {Object} config The config object
4543  */
4544 Roo.LoadMask = function(el, config){
4545     this.el = Roo.get(el);
4546     Roo.apply(this, config);
4547     if(this.store){
4548         this.store.on('beforeload', this.onBeforeLoad, this);
4549         this.store.on('load', this.onLoad, this);
4550         this.store.on('loadexception', this.onLoadException, this);
4551         this.removeMask = false;
4552     }else{
4553         var um = this.el.getUpdateManager();
4554         um.showLoadIndicator = false; // disable the default indicator
4555         um.on('beforeupdate', this.onBeforeLoad, this);
4556         um.on('update', this.onLoad, this);
4557         um.on('failure', this.onLoad, this);
4558         this.removeMask = true;
4559     }
4560 };
4561
4562 Roo.LoadMask.prototype = {
4563     /**
4564      * @cfg {Boolean} removeMask
4565      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
4566      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
4567      */
4568     /**
4569      * @cfg {String} msg
4570      * The text to display in a centered loading message box (defaults to 'Loading...')
4571      */
4572     msg : 'Loading...',
4573     /**
4574      * @cfg {String} msgCls
4575      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
4576      */
4577     msgCls : 'x-mask-loading',
4578
4579     /**
4580      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
4581      * @type Boolean
4582      */
4583     disabled: false,
4584
4585     /**
4586      * Disables the mask to prevent it from being displayed
4587      */
4588     disable : function(){
4589        this.disabled = true;
4590     },
4591
4592     /**
4593      * Enables the mask so that it can be displayed
4594      */
4595     enable : function(){
4596         this.disabled = false;
4597     },
4598     
4599     onLoadException : function()
4600     {
4601         Roo.log(arguments);
4602         
4603         if (typeof(arguments[3]) != 'undefined') {
4604             Roo.MessageBox.alert("Error loading",arguments[3]);
4605         } 
4606         /*
4607         try {
4608             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
4609                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
4610             }   
4611         } catch(e) {
4612             
4613         }
4614         */
4615     
4616         
4617         
4618         this.el.unmask(this.removeMask);
4619     },
4620     // private
4621     onLoad : function()
4622     {
4623         this.el.unmask(this.removeMask);
4624     },
4625
4626     // private
4627     onBeforeLoad : function(){
4628         if(!this.disabled){
4629             this.el.mask(this.msg, this.msgCls);
4630         }
4631     },
4632
4633     // private
4634     destroy : function(){
4635         if(this.store){
4636             this.store.un('beforeload', this.onBeforeLoad, this);
4637             this.store.un('load', this.onLoad, this);
4638             this.store.un('loadexception', this.onLoadException, this);
4639         }else{
4640             var um = this.el.getUpdateManager();
4641             um.un('beforeupdate', this.onBeforeLoad, this);
4642             um.un('update', this.onLoad, this);
4643             um.un('failure', this.onLoad, this);
4644         }
4645     }
4646 };/*
4647  * - LGPL
4648  *
4649  * table
4650  * 
4651  */
4652
4653 /**
4654  * @class Roo.bootstrap.Table
4655  * @extends Roo.bootstrap.Component
4656  * Bootstrap Table class
4657  * @cfg {String} cls table class
4658  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
4659  * @cfg {String} bgcolor Specifies the background color for a table
4660  * @cfg {Number} border Specifies whether the table cells should have borders or not
4661  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
4662  * @cfg {Number} cellspacing Specifies the space between cells
4663  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
4664  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
4665  * @cfg {String} sortable Specifies that the table should be sortable
4666  * @cfg {String} summary Specifies a summary of the content of a table
4667  * @cfg {Number} width Specifies the width of a table
4668  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
4669  * 
4670  * @cfg {boolean} striped Should the rows be alternative striped
4671  * @cfg {boolean} bordered Add borders to the table
4672  * @cfg {boolean} hover Add hover highlighting
4673  * @cfg {boolean} condensed Format condensed
4674  * @cfg {boolean} responsive Format condensed
4675  * @cfg {Boolean} loadMask (true|false) default false
4676  * @cfg {Boolean} tfoot (true|false) generate tfoot, default true
4677  * @cfg {Boolean} thead (true|false) generate thead, default true
4678  * @cfg {Boolean} RowSelection (true|false) default false
4679  * @cfg {Boolean} CellSelection (true|false) default false
4680  *
4681  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
4682  
4683  * 
4684  * @constructor
4685  * Create a new Table
4686  * @param {Object} config The config object
4687  */
4688
4689 Roo.bootstrap.Table = function(config){
4690     Roo.bootstrap.Table.superclass.constructor.call(this, config);
4691     
4692     if (this.sm) {
4693         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
4694         this.sm = this.selModel;
4695         this.sm.xmodule = this.xmodule || false;
4696     }
4697     if (this.cm && typeof(this.cm.config) == 'undefined') {
4698         this.colModel = new Roo.grid.ColumnModel(this.cm);
4699         this.cm = this.colModel;
4700         this.cm.xmodule = this.xmodule || false;
4701     }
4702     if (this.store) {
4703         this.store= Roo.factory(this.store, Roo.data);
4704         this.ds = this.store;
4705         this.ds.xmodule = this.xmodule || false;
4706          
4707     }
4708     if (this.footer && this.store) {
4709         this.footer.dataSource = this.ds;
4710         this.footer = Roo.factory(this.footer);
4711     }
4712     
4713     /** @private */
4714     this.addEvents({
4715         /**
4716          * @event cellclick
4717          * Fires when a cell is clicked
4718          * @param {Roo.bootstrap.Table} this
4719          * @param {Roo.Element} el
4720          * @param {Number} rowIndex
4721          * @param {Number} columnIndex
4722          * @param {Roo.EventObject} e
4723          */
4724         "cellclick" : true,
4725         /**
4726          * @event celldblclick
4727          * Fires when a cell is double clicked
4728          * @param {Roo.bootstrap.Table} this
4729          * @param {Roo.Element} el
4730          * @param {Number} rowIndex
4731          * @param {Number} columnIndex
4732          * @param {Roo.EventObject} e
4733          */
4734         "celldblclick" : true,
4735         /**
4736          * @event rowclick
4737          * Fires when a row is clicked
4738          * @param {Roo.bootstrap.Table} this
4739          * @param {Roo.Element} el
4740          * @param {Number} rowIndex
4741          * @param {Roo.EventObject} e
4742          */
4743         "rowclick" : true,
4744         /**
4745          * @event rowdblclick
4746          * Fires when a row is double clicked
4747          * @param {Roo.bootstrap.Table} this
4748          * @param {Roo.Element} el
4749          * @param {Number} rowIndex
4750          * @param {Roo.EventObject} e
4751          */
4752         "rowdblclick" : true,
4753         /**
4754          * @event mouseover
4755          * Fires when a mouseover occur
4756          * @param {Roo.bootstrap.Table} this
4757          * @param {Roo.Element} el
4758          * @param {Number} rowIndex
4759          * @param {Number} columnIndex
4760          * @param {Roo.EventObject} e
4761          */
4762         "mouseover" : true,
4763         /**
4764          * @event mouseout
4765          * Fires when a mouseout occur
4766          * @param {Roo.bootstrap.Table} this
4767          * @param {Roo.Element} el
4768          * @param {Number} rowIndex
4769          * @param {Number} columnIndex
4770          * @param {Roo.EventObject} e
4771          */
4772         "mouseout" : true,
4773         /**
4774          * @event rowclass
4775          * Fires when a row is rendered, so you can change add a style to it.
4776          * @param {Roo.bootstrap.Table} this
4777          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
4778          */
4779         'rowclass' : true
4780         
4781     });
4782 };
4783
4784 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
4785     
4786     cls: false,
4787     align: false,
4788     bgcolor: false,
4789     border: false,
4790     cellpadding: false,
4791     cellspacing: false,
4792     frame: false,
4793     rules: false,
4794     sortable: false,
4795     summary: false,
4796     width: false,
4797     striped : false,
4798     bordered: false,
4799     hover:  false,
4800     condensed : false,
4801     responsive : false,
4802     sm : false,
4803     cm : false,
4804     store : false,
4805     loadMask : false,
4806     tfoot : true,
4807     thead : true,
4808     RowSelection : false,
4809     CellSelection : false,
4810     layout : false,
4811     
4812     // Roo.Element - the tbody
4813     mainBody: false, 
4814     
4815     getAutoCreate : function(){
4816         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
4817         
4818         cfg = {
4819             tag: 'table',
4820             cls : 'table',
4821             cn : []
4822         }
4823             
4824         if (this.striped) {
4825             cfg.cls += ' table-striped';
4826         }
4827         
4828         if (this.hover) {
4829             cfg.cls += ' table-hover';
4830         }
4831         if (this.bordered) {
4832             cfg.cls += ' table-bordered';
4833         }
4834         if (this.condensed) {
4835             cfg.cls += ' table-condensed';
4836         }
4837         if (this.responsive) {
4838             cfg.cls += ' table-responsive';
4839         }
4840         
4841         if (this.cls) {
4842             cfg.cls+=  ' ' +this.cls;
4843         }
4844         
4845         // this lot should be simplifed...
4846         
4847         if (this.align) {
4848             cfg.align=this.align;
4849         }
4850         if (this.bgcolor) {
4851             cfg.bgcolor=this.bgcolor;
4852         }
4853         if (this.border) {
4854             cfg.border=this.border;
4855         }
4856         if (this.cellpadding) {
4857             cfg.cellpadding=this.cellpadding;
4858         }
4859         if (this.cellspacing) {
4860             cfg.cellspacing=this.cellspacing;
4861         }
4862         if (this.frame) {
4863             cfg.frame=this.frame;
4864         }
4865         if (this.rules) {
4866             cfg.rules=this.rules;
4867         }
4868         if (this.sortable) {
4869             cfg.sortable=this.sortable;
4870         }
4871         if (this.summary) {
4872             cfg.summary=this.summary;
4873         }
4874         if (this.width) {
4875             cfg.width=this.width;
4876         }
4877         if (this.layout) {
4878             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
4879         }
4880         
4881         if(this.store || this.cm){
4882             if(this.thead){
4883                 cfg.cn.push(this.renderHeader());
4884             }
4885             
4886             cfg.cn.push(this.renderBody());
4887             
4888             if(this.tfoot){
4889                 cfg.cn.push(this.renderFooter());
4890             }
4891             
4892             cfg.cls+=  ' TableGrid';
4893         }
4894         
4895         return { cn : [ cfg ] };
4896     },
4897     
4898     initEvents : function()
4899     {   
4900         if(!this.store || !this.cm){
4901             return;
4902         }
4903         
4904         //Roo.log('initEvents with ds!!!!');
4905         
4906         this.mainBody = this.el.select('tbody', true).first();
4907         
4908         
4909         var _this = this;
4910         
4911         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
4912             e.on('click', _this.sort, _this);
4913         });
4914         
4915         this.el.on("click", this.onClick, this);
4916         this.el.on("dblclick", this.onDblClick, this);
4917         
4918         this.parent().el.setStyle('position', 'relative');
4919         if (this.footer) {
4920             this.footer.parentId = this.id;
4921             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
4922         }
4923         
4924         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
4925         
4926         this.store.on('load', this.onLoad, this);
4927         this.store.on('beforeload', this.onBeforeLoad, this);
4928         this.store.on('update', this.onUpdate, this);
4929         
4930     },
4931     
4932     onMouseover : function(e, el)
4933     {
4934         var cell = Roo.get(el);
4935         
4936         if(!cell){
4937             return;
4938         }
4939         
4940         if(e.getTarget().nodeName.toLowerCase() != 'td'){
4941             cell = cell.findParent('td', false, true);
4942         }
4943         
4944         var row = cell.findParent('tr', false, true);
4945         var cellIndex = cell.dom.cellIndex;
4946         var rowIndex = row.dom.rowIndex - 1; // start from 0
4947         
4948         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
4949         
4950     },
4951     
4952     onMouseout : function(e, el)
4953     {
4954         var cell = Roo.get(el);
4955         
4956         if(!cell){
4957             return;
4958         }
4959         
4960         if(e.getTarget().nodeName.toLowerCase() != 'td'){
4961             cell = cell.findParent('td', false, true);
4962         }
4963         
4964         var row = cell.findParent('tr', false, true);
4965         var cellIndex = cell.dom.cellIndex;
4966         var rowIndex = row.dom.rowIndex - 1; // start from 0
4967         
4968         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
4969         
4970     },
4971     
4972     onClick : function(e, el)
4973     {
4974         var cell = Roo.get(el);
4975         
4976         if(!cell || (!this.CellSelection && !this.RowSelection)){
4977             return;
4978         }
4979         
4980         
4981         if(e.getTarget().nodeName.toLowerCase() != 'td'){
4982             cell = cell.findParent('td', false, true);
4983         }
4984         
4985         var row = cell.findParent('tr', false, true);
4986         var cellIndex = cell.dom.cellIndex;
4987         var rowIndex = row.dom.rowIndex - 1;
4988         
4989         if(this.CellSelection){
4990             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
4991         }
4992         
4993         if(this.RowSelection){
4994             this.fireEvent('rowclick', this, row, rowIndex, e);
4995         }
4996         
4997         
4998     },
4999     
5000     onDblClick : function(e,el)
5001     {
5002         var cell = Roo.get(el);
5003         
5004         if(!cell || (!this.CellSelection && !this.RowSelection)){
5005             return;
5006         }
5007         
5008         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5009             cell = cell.findParent('td', false, true);
5010         }
5011         
5012         var row = cell.findParent('tr', false, true);
5013         var cellIndex = cell.dom.cellIndex;
5014         var rowIndex = row.dom.rowIndex - 1;
5015         
5016         if(this.CellSelection){
5017             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5018         }
5019         
5020         if(this.RowSelection){
5021             this.fireEvent('rowdblclick', this, row, rowIndex, e);
5022         }
5023     },
5024     
5025     sort : function(e,el)
5026     {
5027         var col = Roo.get(el)
5028         
5029         if(!col.hasClass('sortable')){
5030             return;
5031         }
5032         
5033         var sort = col.attr('sort');
5034         var dir = 'ASC';
5035         
5036         if(col.hasClass('glyphicon-arrow-up')){
5037             dir = 'DESC';
5038         }
5039         
5040         this.store.sortInfo = {field : sort, direction : dir};
5041         
5042         if (this.footer) {
5043             Roo.log("calling footer first");
5044             this.footer.onClick('first');
5045         } else {
5046         
5047             this.store.load({ params : { start : 0 } });
5048         }
5049     },
5050     
5051     renderHeader : function()
5052     {
5053         var header = {
5054             tag: 'thead',
5055             cn : []
5056         };
5057         
5058         var cm = this.cm;
5059         
5060         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5061             
5062             var config = cm.config[i];
5063                     
5064             var c = {
5065                 tag: 'th',
5066                 style : '',
5067                 html: cm.getColumnHeader(i)
5068             };
5069             
5070             if(typeof(config.hidden) != 'undefined' && config.hidden){
5071                 c.style += ' display:none;';
5072             }
5073             
5074             if(typeof(config.dataIndex) != 'undefined'){
5075                 c.sort = config.dataIndex;
5076             }
5077             
5078             if(typeof(config.sortable) != 'undefined' && config.sortable){
5079                 c.cls = 'sortable';
5080             }
5081             
5082             if(typeof(config.align) != 'undefined' && config.align.length){
5083                 c.style += ' text-align:' + config.align + ';';
5084             }
5085             
5086             if(typeof(config.width) != 'undefined'){
5087                 c.style += ' width:' + config.width + 'px;';
5088             }
5089             
5090             header.cn.push(c)
5091         }
5092         
5093         return header;
5094     },
5095     
5096     renderBody : function()
5097     {
5098         var body = {
5099             tag: 'tbody',
5100             cn : [
5101                 {
5102                     tag: 'tr',
5103                     cn : [
5104                         {
5105                             tag : 'td',
5106                             colspan :  this.cm.getColumnCount()
5107                         }
5108                     ]
5109                 }
5110             ]
5111         };
5112         
5113         return body;
5114     },
5115     
5116     renderFooter : function()
5117     {
5118         var footer = {
5119             tag: 'tfoot',
5120             cn : [
5121                 {
5122                     tag: 'tr',
5123                     cn : [
5124                         {
5125                             tag : 'td',
5126                             colspan :  this.cm.getColumnCount()
5127                         }
5128                     ]
5129                 }
5130             ]
5131         };
5132         
5133         return footer;
5134     },
5135     
5136     
5137     
5138     onLoad : function()
5139     {
5140         Roo.log('ds onload');
5141         this.clear();
5142         
5143         var _this = this;
5144         var cm = this.cm;
5145         var ds = this.store;
5146         
5147         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5148             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
5149             
5150             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
5151                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
5152             }
5153             
5154             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
5155                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
5156             }
5157         });
5158         
5159         var tbody =  this.mainBody;
5160         
5161         var renders = [];
5162                     
5163         if(ds.getCount() > 0){
5164             ds.data.each(function(d,rowIndex){
5165                 var row =  this.renderRow(cm, ds, rowIndex);
5166                 
5167                 tbody.createChild(row);
5168                 
5169                 var _this = this;
5170                 
5171                 if(row.cellObjects.length){
5172                     Roo.each(row.cellObjects, function(r){
5173                         _this.renderCellObject(r);
5174                     })
5175                 }
5176                 
5177             }, this);
5178         }
5179         
5180         Roo.each(this.el.select('tbody td', true).elements, function(e){
5181             e.on('mouseover', _this.onMouseover, _this);
5182         });
5183         
5184         Roo.each(this.el.select('tbody td', true).elements, function(e){
5185             e.on('mouseout', _this.onMouseout, _this);
5186         });
5187
5188         //if(this.loadMask){
5189         //    this.maskEl.hide();
5190         //}
5191     },
5192     
5193     
5194     onUpdate : function(ds,record)
5195     {
5196         this.refreshRow(record);
5197     },
5198     onRemove : function(ds, record, index, isUpdate){
5199         if(isUpdate !== true){
5200             this.fireEvent("beforerowremoved", this, index, record);
5201         }
5202         var bt = this.mainBody.dom;
5203         if(bt.rows[index]){
5204             bt.removeChild(bt.rows[index]);
5205         }
5206         
5207         if(isUpdate !== true){
5208             //this.stripeRows(index);
5209             //this.syncRowHeights(index, index);
5210             //this.layout();
5211             this.fireEvent("rowremoved", this, index, record);
5212         }
5213     },
5214     
5215     
5216     refreshRow : function(record){
5217         var ds = this.store, index;
5218         if(typeof record == 'number'){
5219             index = record;
5220             record = ds.getAt(index);
5221         }else{
5222             index = ds.indexOf(record);
5223         }
5224         this.insertRow(ds, index, true);
5225         this.onRemove(ds, record, index+1, true);
5226         //this.syncRowHeights(index, index);
5227         //this.layout();
5228         this.fireEvent("rowupdated", this, index, record);
5229     },
5230     
5231     insertRow : function(dm, rowIndex, isUpdate){
5232         
5233         if(!isUpdate){
5234             this.fireEvent("beforerowsinserted", this, rowIndex);
5235         }
5236             //var s = this.getScrollState();
5237         var row = this.renderRow(this.cm, this.store, rowIndex);
5238         // insert before rowIndex..
5239         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
5240         Roo.log(e);
5241         
5242         var _this = this;
5243                 
5244         if(row.cellObjects.length){
5245             Roo.each(row.cellObjects, function(r){
5246                 _this.renderCellObject(r);
5247             })
5248         }
5249             
5250         if(!isUpdate){
5251             this.fireEvent("rowsinserted", this, rowIndex);
5252             //this.syncRowHeights(firstRow, lastRow);
5253             //this.stripeRows(firstRow);
5254             //this.layout();
5255         }
5256         
5257     },
5258     
5259     
5260     getRowDom : function(rowIndex)
5261     {
5262         // not sure if I need to check this.. but let's do it anyway..
5263         return (this.mainBody.dom.rows && (rowIndex-1) < this.mainBody.dom.rows.length ) ?
5264                 this.mainBody.dom.rows[rowIndex] : false
5265     },
5266     // returns the object tree for a tr..
5267   
5268     
5269     renderRow : function(cm, ds, rowIndex) {
5270         
5271         var d = ds.getAt(rowIndex);
5272         
5273         var row = {
5274             tag : 'tr',
5275             cn : []
5276         };
5277             
5278         var cellObjects = [];
5279         
5280         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5281             var config = cm.config[i];
5282             
5283             var renderer = cm.getRenderer(i);
5284             var value = '';
5285             var id = false;
5286             
5287             if(typeof(renderer) !== 'undefined'){
5288                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
5289             }
5290             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
5291             // and are rendered into the cells after the row is rendered - using the id for the element.
5292             
5293             if(typeof(value) === 'object'){
5294                 id = Roo.id();
5295                 cellObjects.push({
5296                     container : id,
5297                     cfg : value 
5298                 })
5299             }
5300             
5301             var rowcfg = {
5302                 record: d,
5303                 rowIndex : rowIndex,
5304                 colIndex : i,
5305                 rowClass : ''
5306             }
5307
5308             this.fireEvent('rowclass', this, rowcfg);
5309             
5310             var td = {
5311                 tag: 'td',
5312                 cls : rowcfg.rowClass,
5313                 style: '',
5314                 html: (typeof(value) === 'object') ? '' : value
5315             };
5316             
5317             if (id) {
5318                 td.id = id;
5319             }
5320             
5321             if(typeof(config.hidden) != 'undefined' && config.hidden){
5322                 td.style += ' display:none;';
5323             }
5324             
5325             if(typeof(config.align) != 'undefined' && config.align.length){
5326                 td.style += ' text-align:' + config.align + ';';
5327             }
5328             
5329             if(typeof(config.width) != 'undefined'){
5330                 td.style += ' width:' +  config.width + 'px;';
5331             }
5332              
5333             row.cn.push(td);
5334            
5335         }
5336         
5337         row.cellObjects = cellObjects;
5338         
5339         return row;
5340           
5341     },
5342     
5343     
5344     
5345     onBeforeLoad : function()
5346     {
5347         //Roo.log('ds onBeforeLoad');
5348         
5349         //this.clear();
5350         
5351         //if(this.loadMask){
5352         //    this.maskEl.show();
5353         //}
5354     },
5355     
5356     clear : function()
5357     {
5358         this.el.select('tbody', true).first().dom.innerHTML = '';
5359     },
5360     
5361     getSelectionModel : function(){
5362         if(!this.selModel){
5363             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
5364         }
5365         return this.selModel;
5366     },
5367     /*
5368      * Render the Roo.bootstrap object from renderder
5369      */
5370     renderCellObject : function(r)
5371     {
5372         var _this = this;
5373         
5374         var t = r.cfg.render(r.container);
5375         
5376         if(r.cfg.cn){
5377             Roo.each(r.cfg.cn, function(c){
5378                 var child = {
5379                     container: t.getChildContainer(),
5380                     cfg: c
5381                 }
5382                 _this.renderCellObject(child);
5383             })
5384         }
5385     }
5386    
5387 });
5388
5389  
5390
5391  /*
5392  * - LGPL
5393  *
5394  * table cell
5395  * 
5396  */
5397
5398 /**
5399  * @class Roo.bootstrap.TableCell
5400  * @extends Roo.bootstrap.Component
5401  * Bootstrap TableCell class
5402  * @cfg {String} html cell contain text
5403  * @cfg {String} cls cell class
5404  * @cfg {String} tag cell tag (td|th) default td
5405  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
5406  * @cfg {String} align Aligns the content in a cell
5407  * @cfg {String} axis Categorizes cells
5408  * @cfg {String} bgcolor Specifies the background color of a cell
5409  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5410  * @cfg {Number} colspan Specifies the number of columns a cell should span
5411  * @cfg {String} headers Specifies one or more header cells a cell is related to
5412  * @cfg {Number} height Sets the height of a cell
5413  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
5414  * @cfg {Number} rowspan Sets the number of rows a cell should span
5415  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
5416  * @cfg {String} valign Vertical aligns the content in a cell
5417  * @cfg {Number} width Specifies the width of a cell
5418  * 
5419  * @constructor
5420  * Create a new TableCell
5421  * @param {Object} config The config object
5422  */
5423
5424 Roo.bootstrap.TableCell = function(config){
5425     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
5426 };
5427
5428 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
5429     
5430     html: false,
5431     cls: false,
5432     tag: false,
5433     abbr: false,
5434     align: false,
5435     axis: false,
5436     bgcolor: false,
5437     charoff: false,
5438     colspan: false,
5439     headers: false,
5440     height: false,
5441     nowrap: false,
5442     rowspan: false,
5443     scope: false,
5444     valign: false,
5445     width: false,
5446     
5447     
5448     getAutoCreate : function(){
5449         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
5450         
5451         cfg = {
5452             tag: 'td'
5453         }
5454         
5455         if(this.tag){
5456             cfg.tag = this.tag;
5457         }
5458         
5459         if (this.html) {
5460             cfg.html=this.html
5461         }
5462         if (this.cls) {
5463             cfg.cls=this.cls
5464         }
5465         if (this.abbr) {
5466             cfg.abbr=this.abbr
5467         }
5468         if (this.align) {
5469             cfg.align=this.align
5470         }
5471         if (this.axis) {
5472             cfg.axis=this.axis
5473         }
5474         if (this.bgcolor) {
5475             cfg.bgcolor=this.bgcolor
5476         }
5477         if (this.charoff) {
5478             cfg.charoff=this.charoff
5479         }
5480         if (this.colspan) {
5481             cfg.colspan=this.colspan
5482         }
5483         if (this.headers) {
5484             cfg.headers=this.headers
5485         }
5486         if (this.height) {
5487             cfg.height=this.height
5488         }
5489         if (this.nowrap) {
5490             cfg.nowrap=this.nowrap
5491         }
5492         if (this.rowspan) {
5493             cfg.rowspan=this.rowspan
5494         }
5495         if (this.scope) {
5496             cfg.scope=this.scope
5497         }
5498         if (this.valign) {
5499             cfg.valign=this.valign
5500         }
5501         if (this.width) {
5502             cfg.width=this.width
5503         }
5504         
5505         
5506         return cfg;
5507     }
5508    
5509 });
5510
5511  
5512
5513  /*
5514  * - LGPL
5515  *
5516  * table row
5517  * 
5518  */
5519
5520 /**
5521  * @class Roo.bootstrap.TableRow
5522  * @extends Roo.bootstrap.Component
5523  * Bootstrap TableRow class
5524  * @cfg {String} cls row class
5525  * @cfg {String} align Aligns the content in a table row
5526  * @cfg {String} bgcolor Specifies a background color for a table row
5527  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5528  * @cfg {String} valign Vertical aligns the content in a table row
5529  * 
5530  * @constructor
5531  * Create a new TableRow
5532  * @param {Object} config The config object
5533  */
5534
5535 Roo.bootstrap.TableRow = function(config){
5536     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
5537 };
5538
5539 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
5540     
5541     cls: false,
5542     align: false,
5543     bgcolor: false,
5544     charoff: false,
5545     valign: false,
5546     
5547     getAutoCreate : function(){
5548         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
5549         
5550         cfg = {
5551             tag: 'tr'
5552         }
5553             
5554         if(this.cls){
5555             cfg.cls = this.cls;
5556         }
5557         if(this.align){
5558             cfg.align = this.align;
5559         }
5560         if(this.bgcolor){
5561             cfg.bgcolor = this.bgcolor;
5562         }
5563         if(this.charoff){
5564             cfg.charoff = this.charoff;
5565         }
5566         if(this.valign){
5567             cfg.valign = this.valign;
5568         }
5569         
5570         return cfg;
5571     }
5572    
5573 });
5574
5575  
5576
5577  /*
5578  * - LGPL
5579  *
5580  * table body
5581  * 
5582  */
5583
5584 /**
5585  * @class Roo.bootstrap.TableBody
5586  * @extends Roo.bootstrap.Component
5587  * Bootstrap TableBody class
5588  * @cfg {String} cls element class
5589  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
5590  * @cfg {String} align Aligns the content inside the element
5591  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
5592  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
5593  * 
5594  * @constructor
5595  * Create a new TableBody
5596  * @param {Object} config The config object
5597  */
5598
5599 Roo.bootstrap.TableBody = function(config){
5600     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
5601 };
5602
5603 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
5604     
5605     cls: false,
5606     tag: false,
5607     align: false,
5608     charoff: false,
5609     valign: false,
5610     
5611     getAutoCreate : function(){
5612         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
5613         
5614         cfg = {
5615             tag: 'tbody'
5616         }
5617             
5618         if (this.cls) {
5619             cfg.cls=this.cls
5620         }
5621         if(this.tag){
5622             cfg.tag = this.tag;
5623         }
5624         
5625         if(this.align){
5626             cfg.align = this.align;
5627         }
5628         if(this.charoff){
5629             cfg.charoff = this.charoff;
5630         }
5631         if(this.valign){
5632             cfg.valign = this.valign;
5633         }
5634         
5635         return cfg;
5636     }
5637     
5638     
5639 //    initEvents : function()
5640 //    {
5641 //        
5642 //        if(!this.store){
5643 //            return;
5644 //        }
5645 //        
5646 //        this.store = Roo.factory(this.store, Roo.data);
5647 //        this.store.on('load', this.onLoad, this);
5648 //        
5649 //        this.store.load();
5650 //        
5651 //    },
5652 //    
5653 //    onLoad: function () 
5654 //    {   
5655 //        this.fireEvent('load', this);
5656 //    }
5657 //    
5658 //   
5659 });
5660
5661  
5662
5663  /*
5664  * Based on:
5665  * Ext JS Library 1.1.1
5666  * Copyright(c) 2006-2007, Ext JS, LLC.
5667  *
5668  * Originally Released Under LGPL - original licence link has changed is not relivant.
5669  *
5670  * Fork - LGPL
5671  * <script type="text/javascript">
5672  */
5673
5674 // as we use this in bootstrap.
5675 Roo.namespace('Roo.form');
5676  /**
5677  * @class Roo.form.Action
5678  * Internal Class used to handle form actions
5679  * @constructor
5680  * @param {Roo.form.BasicForm} el The form element or its id
5681  * @param {Object} config Configuration options
5682  */
5683
5684  
5685  
5686 // define the action interface
5687 Roo.form.Action = function(form, options){
5688     this.form = form;
5689     this.options = options || {};
5690 };
5691 /**
5692  * Client Validation Failed
5693  * @const 
5694  */
5695 Roo.form.Action.CLIENT_INVALID = 'client';
5696 /**
5697  * Server Validation Failed
5698  * @const 
5699  */
5700 Roo.form.Action.SERVER_INVALID = 'server';
5701  /**
5702  * Connect to Server Failed
5703  * @const 
5704  */
5705 Roo.form.Action.CONNECT_FAILURE = 'connect';
5706 /**
5707  * Reading Data from Server Failed
5708  * @const 
5709  */
5710 Roo.form.Action.LOAD_FAILURE = 'load';
5711
5712 Roo.form.Action.prototype = {
5713     type : 'default',
5714     failureType : undefined,
5715     response : undefined,
5716     result : undefined,
5717
5718     // interface method
5719     run : function(options){
5720
5721     },
5722
5723     // interface method
5724     success : function(response){
5725
5726     },
5727
5728     // interface method
5729     handleResponse : function(response){
5730
5731     },
5732
5733     // default connection failure
5734     failure : function(response){
5735         
5736         this.response = response;
5737         this.failureType = Roo.form.Action.CONNECT_FAILURE;
5738         this.form.afterAction(this, false);
5739     },
5740
5741     processResponse : function(response){
5742         this.response = response;
5743         if(!response.responseText){
5744             return true;
5745         }
5746         this.result = this.handleResponse(response);
5747         return this.result;
5748     },
5749
5750     // utility functions used internally
5751     getUrl : function(appendParams){
5752         var url = this.options.url || this.form.url || this.form.el.dom.action;
5753         if(appendParams){
5754             var p = this.getParams();
5755             if(p){
5756                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
5757             }
5758         }
5759         return url;
5760     },
5761
5762     getMethod : function(){
5763         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
5764     },
5765
5766     getParams : function(){
5767         var bp = this.form.baseParams;
5768         var p = this.options.params;
5769         if(p){
5770             if(typeof p == "object"){
5771                 p = Roo.urlEncode(Roo.applyIf(p, bp));
5772             }else if(typeof p == 'string' && bp){
5773                 p += '&' + Roo.urlEncode(bp);
5774             }
5775         }else if(bp){
5776             p = Roo.urlEncode(bp);
5777         }
5778         return p;
5779     },
5780
5781     createCallback : function(){
5782         return {
5783             success: this.success,
5784             failure: this.failure,
5785             scope: this,
5786             timeout: (this.form.timeout*1000),
5787             upload: this.form.fileUpload ? this.success : undefined
5788         };
5789     }
5790 };
5791
5792 Roo.form.Action.Submit = function(form, options){
5793     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
5794 };
5795
5796 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
5797     type : 'submit',
5798
5799     haveProgress : false,
5800     uploadComplete : false,
5801     
5802     // uploadProgress indicator.
5803     uploadProgress : function()
5804     {
5805         if (!this.form.progressUrl) {
5806             return;
5807         }
5808         
5809         if (!this.haveProgress) {
5810             Roo.MessageBox.progress("Uploading", "Uploading");
5811         }
5812         if (this.uploadComplete) {
5813            Roo.MessageBox.hide();
5814            return;
5815         }
5816         
5817         this.haveProgress = true;
5818    
5819         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
5820         
5821         var c = new Roo.data.Connection();
5822         c.request({
5823             url : this.form.progressUrl,
5824             params: {
5825                 id : uid
5826             },
5827             method: 'GET',
5828             success : function(req){
5829                //console.log(data);
5830                 var rdata = false;
5831                 var edata;
5832                 try  {
5833                    rdata = Roo.decode(req.responseText)
5834                 } catch (e) {
5835                     Roo.log("Invalid data from server..");
5836                     Roo.log(edata);
5837                     return;
5838                 }
5839                 if (!rdata || !rdata.success) {
5840                     Roo.log(rdata);
5841                     Roo.MessageBox.alert(Roo.encode(rdata));
5842                     return;
5843                 }
5844                 var data = rdata.data;
5845                 
5846                 if (this.uploadComplete) {
5847                    Roo.MessageBox.hide();
5848                    return;
5849                 }
5850                    
5851                 if (data){
5852                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
5853                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
5854                     );
5855                 }
5856                 this.uploadProgress.defer(2000,this);
5857             },
5858        
5859             failure: function(data) {
5860                 Roo.log('progress url failed ');
5861                 Roo.log(data);
5862             },
5863             scope : this
5864         });
5865            
5866     },
5867     
5868     
5869     run : function()
5870     {
5871         // run get Values on the form, so it syncs any secondary forms.
5872         this.form.getValues();
5873         
5874         var o = this.options;
5875         var method = this.getMethod();
5876         var isPost = method == 'POST';
5877         if(o.clientValidation === false || this.form.isValid()){
5878             
5879             if (this.form.progressUrl) {
5880                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
5881                     (new Date() * 1) + '' + Math.random());
5882                     
5883             } 
5884             
5885             
5886             Roo.Ajax.request(Roo.apply(this.createCallback(), {
5887                 form:this.form.el.dom,
5888                 url:this.getUrl(!isPost),
5889                 method: method,
5890                 params:isPost ? this.getParams() : null,
5891                 isUpload: this.form.fileUpload
5892             }));
5893             
5894             this.uploadProgress();
5895
5896         }else if (o.clientValidation !== false){ // client validation failed
5897             this.failureType = Roo.form.Action.CLIENT_INVALID;
5898             this.form.afterAction(this, false);
5899         }
5900     },
5901
5902     success : function(response)
5903     {
5904         this.uploadComplete= true;
5905         if (this.haveProgress) {
5906             Roo.MessageBox.hide();
5907         }
5908         
5909         
5910         var result = this.processResponse(response);
5911         if(result === true || result.success){
5912             this.form.afterAction(this, true);
5913             return;
5914         }
5915         if(result.errors){
5916             this.form.markInvalid(result.errors);
5917             this.failureType = Roo.form.Action.SERVER_INVALID;
5918         }
5919         this.form.afterAction(this, false);
5920     },
5921     failure : function(response)
5922     {
5923         this.uploadComplete= true;
5924         if (this.haveProgress) {
5925             Roo.MessageBox.hide();
5926         }
5927         
5928         this.response = response;
5929         this.failureType = Roo.form.Action.CONNECT_FAILURE;
5930         this.form.afterAction(this, false);
5931     },
5932     
5933     handleResponse : function(response){
5934         if(this.form.errorReader){
5935             var rs = this.form.errorReader.read(response);
5936             var errors = [];
5937             if(rs.records){
5938                 for(var i = 0, len = rs.records.length; i < len; i++) {
5939                     var r = rs.records[i];
5940                     errors[i] = r.data;
5941                 }
5942             }
5943             if(errors.length < 1){
5944                 errors = null;
5945             }
5946             return {
5947                 success : rs.success,
5948                 errors : errors
5949             };
5950         }
5951         var ret = false;
5952         try {
5953             ret = Roo.decode(response.responseText);
5954         } catch (e) {
5955             ret = {
5956                 success: false,
5957                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
5958                 errors : []
5959             };
5960         }
5961         return ret;
5962         
5963     }
5964 });
5965
5966
5967 Roo.form.Action.Load = function(form, options){
5968     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
5969     this.reader = this.form.reader;
5970 };
5971
5972 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
5973     type : 'load',
5974
5975     run : function(){
5976         
5977         Roo.Ajax.request(Roo.apply(
5978                 this.createCallback(), {
5979                     method:this.getMethod(),
5980                     url:this.getUrl(false),
5981                     params:this.getParams()
5982         }));
5983     },
5984
5985     success : function(response){
5986         
5987         var result = this.processResponse(response);
5988         if(result === true || !result.success || !result.data){
5989             this.failureType = Roo.form.Action.LOAD_FAILURE;
5990             this.form.afterAction(this, false);
5991             return;
5992         }
5993         this.form.clearInvalid();
5994         this.form.setValues(result.data);
5995         this.form.afterAction(this, true);
5996     },
5997
5998     handleResponse : function(response){
5999         if(this.form.reader){
6000             var rs = this.form.reader.read(response);
6001             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
6002             return {
6003                 success : rs.success,
6004                 data : data
6005             };
6006         }
6007         return Roo.decode(response.responseText);
6008     }
6009 });
6010
6011 Roo.form.Action.ACTION_TYPES = {
6012     'load' : Roo.form.Action.Load,
6013     'submit' : Roo.form.Action.Submit
6014 };/*
6015  * - LGPL
6016  *
6017  * form
6018  * 
6019  */
6020
6021 /**
6022  * @class Roo.bootstrap.Form
6023  * @extends Roo.bootstrap.Component
6024  * Bootstrap Form class
6025  * @cfg {String} method  GET | POST (default POST)
6026  * @cfg {String} labelAlign top | left (default top)
6027   * @cfg {String} align left  | right - for navbars
6028
6029  * 
6030  * @constructor
6031  * Create a new Form
6032  * @param {Object} config The config object
6033  */
6034
6035
6036 Roo.bootstrap.Form = function(config){
6037     Roo.bootstrap.Form.superclass.constructor.call(this, config);
6038     this.addEvents({
6039         /**
6040          * @event clientvalidation
6041          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
6042          * @param {Form} this
6043          * @param {Boolean} valid true if the form has passed client-side validation
6044          */
6045         clientvalidation: true,
6046         /**
6047          * @event beforeaction
6048          * Fires before any action is performed. Return false to cancel the action.
6049          * @param {Form} this
6050          * @param {Action} action The action to be performed
6051          */
6052         beforeaction: true,
6053         /**
6054          * @event actionfailed
6055          * Fires when an action fails.
6056          * @param {Form} this
6057          * @param {Action} action The action that failed
6058          */
6059         actionfailed : true,
6060         /**
6061          * @event actioncomplete
6062          * Fires when an action is completed.
6063          * @param {Form} this
6064          * @param {Action} action The action that completed
6065          */
6066         actioncomplete : true
6067     });
6068     
6069 };
6070
6071 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
6072       
6073      /**
6074      * @cfg {String} method
6075      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
6076      */
6077     method : 'POST',
6078     /**
6079      * @cfg {String} url
6080      * The URL to use for form actions if one isn't supplied in the action options.
6081      */
6082     /**
6083      * @cfg {Boolean} fileUpload
6084      * Set to true if this form is a file upload.
6085      */
6086      
6087     /**
6088      * @cfg {Object} baseParams
6089      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
6090      */
6091       
6092     /**
6093      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
6094      */
6095     timeout: 30,
6096     /**
6097      * @cfg {Sting} align (left|right) for navbar forms
6098      */
6099     align : 'left',
6100
6101     // private
6102     activeAction : null,
6103  
6104     /**
6105      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
6106      * element by passing it or its id or mask the form itself by passing in true.
6107      * @type Mixed
6108      */
6109     waitMsgTarget : false,
6110     
6111      
6112     
6113     /**
6114      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
6115      * element by passing it or its id or mask the form itself by passing in true.
6116      * @type Mixed
6117      */
6118     
6119     getAutoCreate : function(){
6120         
6121         var cfg = {
6122             tag: 'form',
6123             method : this.method || 'POST',
6124             id : this.id || Roo.id(),
6125             cls : ''
6126         }
6127         if (this.parent().xtype.match(/^Nav/)) {
6128             cfg.cls = 'navbar-form navbar-' + this.align;
6129             
6130         }
6131         
6132         if (this.labelAlign == 'left' ) {
6133             cfg.cls += ' form-horizontal';
6134         }
6135         
6136         
6137         return cfg;
6138     },
6139     initEvents : function()
6140     {
6141         this.el.on('submit', this.onSubmit, this);
6142         // this was added as random key presses on the form where triggering form submit.
6143         this.el.on('keypress', function(e) {
6144             if (e.getCharCode() != 13) {
6145                 return true;
6146             }
6147             // we might need to allow it for textareas.. and some other items.
6148             // check e.getTarget().
6149             
6150             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
6151                 return true;
6152             }
6153         
6154             Roo.log("keypress blocked");
6155             
6156             e.preventDefault();
6157             return false;
6158         });
6159         
6160     },
6161     // private
6162     onSubmit : function(e){
6163         e.stopEvent();
6164     },
6165     
6166      /**
6167      * Returns true if client-side validation on the form is successful.
6168      * @return Boolean
6169      */
6170     isValid : function(){
6171         var items = this.getItems();
6172         var valid = true;
6173         items.each(function(f){
6174            if(!f.validate()){
6175                valid = false;
6176                
6177            }
6178         });
6179         return valid;
6180     },
6181     /**
6182      * Returns true if any fields in this form have changed since their original load.
6183      * @return Boolean
6184      */
6185     isDirty : function(){
6186         var dirty = false;
6187         var items = this.getItems();
6188         items.each(function(f){
6189            if(f.isDirty()){
6190                dirty = true;
6191                return false;
6192            }
6193            return true;
6194         });
6195         return dirty;
6196     },
6197      /**
6198      * Performs a predefined action (submit or load) or custom actions you define on this form.
6199      * @param {String} actionName The name of the action type
6200      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
6201      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
6202      * accept other config options):
6203      * <pre>
6204 Property          Type             Description
6205 ----------------  ---------------  ----------------------------------------------------------------------------------
6206 url               String           The url for the action (defaults to the form's url)
6207 method            String           The form method to use (defaults to the form's method, or POST if not defined)
6208 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
6209 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
6210                                    validate the form on the client (defaults to false)
6211      * </pre>
6212      * @return {BasicForm} this
6213      */
6214     doAction : function(action, options){
6215         if(typeof action == 'string'){
6216             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
6217         }
6218         if(this.fireEvent('beforeaction', this, action) !== false){
6219             this.beforeAction(action);
6220             action.run.defer(100, action);
6221         }
6222         return this;
6223     },
6224     
6225     // private
6226     beforeAction : function(action){
6227         var o = action.options;
6228         
6229         // not really supported yet.. ??
6230         
6231         //if(this.waitMsgTarget === true){
6232             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6233         //}else if(this.waitMsgTarget){
6234         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
6235         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
6236         //}else {
6237         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
6238        // }
6239          
6240     },
6241
6242     // private
6243     afterAction : function(action, success){
6244         this.activeAction = null;
6245         var o = action.options;
6246         
6247         //if(this.waitMsgTarget === true){
6248             this.el.unmask();
6249         //}else if(this.waitMsgTarget){
6250         //    this.waitMsgTarget.unmask();
6251         //}else{
6252         //    Roo.MessageBox.updateProgress(1);
6253         //    Roo.MessageBox.hide();
6254        // }
6255         // 
6256         if(success){
6257             if(o.reset){
6258                 this.reset();
6259             }
6260             Roo.callback(o.success, o.scope, [this, action]);
6261             this.fireEvent('actioncomplete', this, action);
6262             
6263         }else{
6264             
6265             // failure condition..
6266             // we have a scenario where updates need confirming.
6267             // eg. if a locking scenario exists..
6268             // we look for { errors : { needs_confirm : true }} in the response.
6269             if (
6270                 (typeof(action.result) != 'undefined')  &&
6271                 (typeof(action.result.errors) != 'undefined')  &&
6272                 (typeof(action.result.errors.needs_confirm) != 'undefined')
6273            ){
6274                 var _t = this;
6275                 Roo.log("not supported yet");
6276                  /*
6277                 
6278                 Roo.MessageBox.confirm(
6279                     "Change requires confirmation",
6280                     action.result.errorMsg,
6281                     function(r) {
6282                         if (r != 'yes') {
6283                             return;
6284                         }
6285                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
6286                     }
6287                     
6288                 );
6289                 */
6290                 
6291                 
6292                 return;
6293             }
6294             
6295             Roo.callback(o.failure, o.scope, [this, action]);
6296             // show an error message if no failed handler is set..
6297             if (!this.hasListener('actionfailed')) {
6298                 Roo.log("need to add dialog support");
6299                 /*
6300                 Roo.MessageBox.alert("Error",
6301                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
6302                         action.result.errorMsg :
6303                         "Saving Failed, please check your entries or try again"
6304                 );
6305                 */
6306             }
6307             
6308             this.fireEvent('actionfailed', this, action);
6309         }
6310         
6311     },
6312     /**
6313      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
6314      * @param {String} id The value to search for
6315      * @return Field
6316      */
6317     findField : function(id){
6318         var items = this.getItems();
6319         var field = items.get(id);
6320         if(!field){
6321              items.each(function(f){
6322                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
6323                     field = f;
6324                     return false;
6325                 }
6326                 return true;
6327             });
6328         }
6329         return field || null;
6330     },
6331      /**
6332      * Mark fields in this form invalid in bulk.
6333      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
6334      * @return {BasicForm} this
6335      */
6336     markInvalid : function(errors){
6337         if(errors instanceof Array){
6338             for(var i = 0, len = errors.length; i < len; i++){
6339                 var fieldError = errors[i];
6340                 var f = this.findField(fieldError.id);
6341                 if(f){
6342                     f.markInvalid(fieldError.msg);
6343                 }
6344             }
6345         }else{
6346             var field, id;
6347             for(id in errors){
6348                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
6349                     field.markInvalid(errors[id]);
6350                 }
6351             }
6352         }
6353         //Roo.each(this.childForms || [], function (f) {
6354         //    f.markInvalid(errors);
6355         //});
6356         
6357         return this;
6358     },
6359
6360     /**
6361      * Set values for fields in this form in bulk.
6362      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
6363      * @return {BasicForm} this
6364      */
6365     setValues : function(values){
6366         if(values instanceof Array){ // array of objects
6367             for(var i = 0, len = values.length; i < len; i++){
6368                 var v = values[i];
6369                 var f = this.findField(v.id);
6370                 if(f){
6371                     f.setValue(v.value);
6372                     if(this.trackResetOnLoad){
6373                         f.originalValue = f.getValue();
6374                     }
6375                 }
6376             }
6377         }else{ // object hash
6378             var field, id;
6379             for(id in values){
6380                 if(typeof values[id] != 'function' && (field = this.findField(id))){
6381                     
6382                     if (field.setFromData && 
6383                         field.valueField && 
6384                         field.displayField &&
6385                         // combos' with local stores can 
6386                         // be queried via setValue()
6387                         // to set their value..
6388                         (field.store && !field.store.isLocal)
6389                         ) {
6390                         // it's a combo
6391                         var sd = { };
6392                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
6393                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
6394                         field.setFromData(sd);
6395                         
6396                     } else {
6397                         field.setValue(values[id]);
6398                     }
6399                     
6400                     
6401                     if(this.trackResetOnLoad){
6402                         field.originalValue = field.getValue();
6403                     }
6404                 }
6405             }
6406         }
6407          
6408         //Roo.each(this.childForms || [], function (f) {
6409         //    f.setValues(values);
6410         //});
6411                 
6412         return this;
6413     },
6414
6415     /**
6416      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
6417      * they are returned as an array.
6418      * @param {Boolean} asString
6419      * @return {Object}
6420      */
6421     getValues : function(asString){
6422         //if (this.childForms) {
6423             // copy values from the child forms
6424         //    Roo.each(this.childForms, function (f) {
6425         //        this.setValues(f.getValues());
6426         //    }, this);
6427         //}
6428         
6429         
6430         
6431         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
6432         if(asString === true){
6433             return fs;
6434         }
6435         return Roo.urlDecode(fs);
6436     },
6437     
6438     /**
6439      * Returns the fields in this form as an object with key/value pairs. 
6440      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
6441      * @return {Object}
6442      */
6443     getFieldValues : function(with_hidden)
6444     {
6445         var items = this.getItems();
6446         var ret = {};
6447         items.each(function(f){
6448             if (!f.getName()) {
6449                 return;
6450             }
6451             var v = f.getValue();
6452             if (f.inputType =='radio') {
6453                 if (typeof(ret[f.getName()]) == 'undefined') {
6454                     ret[f.getName()] = ''; // empty..
6455                 }
6456                 
6457                 if (!f.el.dom.checked) {
6458                     return;
6459                     
6460                 }
6461                 v = f.el.dom.value;
6462                 
6463             }
6464             
6465             // not sure if this supported any more..
6466             if ((typeof(v) == 'object') && f.getRawValue) {
6467                 v = f.getRawValue() ; // dates..
6468             }
6469             // combo boxes where name != hiddenName...
6470             if (f.name != f.getName()) {
6471                 ret[f.name] = f.getRawValue();
6472             }
6473             ret[f.getName()] = v;
6474         });
6475         
6476         return ret;
6477     },
6478
6479     /**
6480      * Clears all invalid messages in this form.
6481      * @return {BasicForm} this
6482      */
6483     clearInvalid : function(){
6484         var items = this.getItems();
6485         
6486         items.each(function(f){
6487            f.clearInvalid();
6488         });
6489         
6490         
6491         
6492         return this;
6493     },
6494
6495     /**
6496      * Resets this form.
6497      * @return {BasicForm} this
6498      */
6499     reset : function(){
6500         var items = this.getItems();
6501         items.each(function(f){
6502             f.reset();
6503         });
6504         
6505         Roo.each(this.childForms || [], function (f) {
6506             f.reset();
6507         });
6508        
6509         
6510         return this;
6511     },
6512     getItems : function()
6513     {
6514         var r=new Roo.util.MixedCollection(false, function(o){
6515             return o.id || (o.id = Roo.id());
6516         });
6517         var iter = function(el) {
6518             if (el.inputEl) {
6519                 r.add(el);
6520             }
6521             if (!el.items) {
6522                 return;
6523             }
6524             Roo.each(el.items,function(e) {
6525                 iter(e);
6526             });
6527             
6528             
6529         };
6530         iter(this);
6531         return r;
6532         
6533         
6534         
6535         
6536     }
6537     
6538 });
6539
6540  
6541 /*
6542  * Based on:
6543  * Ext JS Library 1.1.1
6544  * Copyright(c) 2006-2007, Ext JS, LLC.
6545  *
6546  * Originally Released Under LGPL - original licence link has changed is not relivant.
6547  *
6548  * Fork - LGPL
6549  * <script type="text/javascript">
6550  */
6551 /**
6552  * @class Roo.form.VTypes
6553  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
6554  * @singleton
6555  */
6556 Roo.form.VTypes = function(){
6557     // closure these in so they are only created once.
6558     var alpha = /^[a-zA-Z_]+$/;
6559     var alphanum = /^[a-zA-Z0-9_]+$/;
6560     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
6561     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
6562
6563     // All these messages and functions are configurable
6564     return {
6565         /**
6566          * The function used to validate email addresses
6567          * @param {String} value The email address
6568          */
6569         'email' : function(v){
6570             return email.test(v);
6571         },
6572         /**
6573          * The error text to display when the email validation function returns false
6574          * @type String
6575          */
6576         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
6577         /**
6578          * The keystroke filter mask to be applied on email input
6579          * @type RegExp
6580          */
6581         'emailMask' : /[a-z0-9_\.\-@]/i,
6582
6583         /**
6584          * The function used to validate URLs
6585          * @param {String} value The URL
6586          */
6587         'url' : function(v){
6588             return url.test(v);
6589         },
6590         /**
6591          * The error text to display when the url validation function returns false
6592          * @type String
6593          */
6594         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
6595         
6596         /**
6597          * The function used to validate alpha values
6598          * @param {String} value The value
6599          */
6600         'alpha' : function(v){
6601             return alpha.test(v);
6602         },
6603         /**
6604          * The error text to display when the alpha validation function returns false
6605          * @type String
6606          */
6607         'alphaText' : 'This field should only contain letters and _',
6608         /**
6609          * The keystroke filter mask to be applied on alpha input
6610          * @type RegExp
6611          */
6612         'alphaMask' : /[a-z_]/i,
6613
6614         /**
6615          * The function used to validate alphanumeric values
6616          * @param {String} value The value
6617          */
6618         'alphanum' : function(v){
6619             return alphanum.test(v);
6620         },
6621         /**
6622          * The error text to display when the alphanumeric validation function returns false
6623          * @type String
6624          */
6625         'alphanumText' : 'This field should only contain letters, numbers and _',
6626         /**
6627          * The keystroke filter mask to be applied on alphanumeric input
6628          * @type RegExp
6629          */
6630         'alphanumMask' : /[a-z0-9_]/i
6631     };
6632 }();/*
6633  * - LGPL
6634  *
6635  * Input
6636  * 
6637  */
6638
6639 /**
6640  * @class Roo.bootstrap.Input
6641  * @extends Roo.bootstrap.Component
6642  * Bootstrap Input class
6643  * @cfg {Boolean} disabled is it disabled
6644  * @cfg {String} fieldLabel - the label associated
6645  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
6646  * @cfg {String} name name of the input
6647  * @cfg {string} fieldLabel - the label associated
6648  * @cfg {string}  inputType - input / file submit ...
6649  * @cfg {string} placeholder - placeholder to put in text.
6650  * @cfg {string}  before - input group add on before
6651  * @cfg {string} after - input group add on after
6652  * @cfg {string} size - (lg|sm) or leave empty..
6653  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
6654  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
6655  * @cfg {Number} md colspan out of 12 for computer-sized screens
6656  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
6657  * @cfg {string} value default value of the input
6658  * @cfg {Number} labelWidth set the width of label (0-12)
6659  * @cfg {String} labelAlign (top|left)
6660  * @cfg {Boolean} readOnly Specifies that the field should be read-only
6661  * @cfg {String} align (left|center|right) Default left
6662  * 
6663  * 
6664  * @constructor
6665  * Create a new Input
6666  * @param {Object} config The config object
6667  */
6668
6669 Roo.bootstrap.Input = function(config){
6670     Roo.bootstrap.Input.superclass.constructor.call(this, config);
6671    
6672         this.addEvents({
6673             /**
6674              * @event focus
6675              * Fires when this field receives input focus.
6676              * @param {Roo.form.Field} this
6677              */
6678             focus : true,
6679             /**
6680              * @event blur
6681              * Fires when this field loses input focus.
6682              * @param {Roo.form.Field} this
6683              */
6684             blur : true,
6685             /**
6686              * @event specialkey
6687              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
6688              * {@link Roo.EventObject#getKey} to determine which key was pressed.
6689              * @param {Roo.form.Field} this
6690              * @param {Roo.EventObject} e The event object
6691              */
6692             specialkey : true,
6693             /**
6694              * @event change
6695              * Fires just before the field blurs if the field value has changed.
6696              * @param {Roo.form.Field} this
6697              * @param {Mixed} newValue The new value
6698              * @param {Mixed} oldValue The original value
6699              */
6700             change : true,
6701             /**
6702              * @event invalid
6703              * Fires after the field has been marked as invalid.
6704              * @param {Roo.form.Field} this
6705              * @param {String} msg The validation message
6706              */
6707             invalid : true,
6708             /**
6709              * @event valid
6710              * Fires after the field has been validated with no errors.
6711              * @param {Roo.form.Field} this
6712              */
6713             valid : true,
6714              /**
6715              * @event keyup
6716              * Fires after the key up
6717              * @param {Roo.form.Field} this
6718              * @param {Roo.EventObject}  e The event Object
6719              */
6720             keyup : true
6721         });
6722 };
6723
6724 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
6725      /**
6726      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
6727       automatic validation (defaults to "keyup").
6728      */
6729     validationEvent : "keyup",
6730      /**
6731      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
6732      */
6733     validateOnBlur : true,
6734     /**
6735      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
6736      */
6737     validationDelay : 250,
6738      /**
6739      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
6740      */
6741     focusClass : "x-form-focus",  // not needed???
6742     
6743        
6744     /**
6745      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
6746      */
6747     invalidClass : "has-error",
6748     
6749     /**
6750      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
6751      */
6752     selectOnFocus : false,
6753     
6754      /**
6755      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
6756      */
6757     maskRe : null,
6758        /**
6759      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
6760      */
6761     vtype : null,
6762     
6763       /**
6764      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
6765      */
6766     disableKeyFilter : false,
6767     
6768        /**
6769      * @cfg {Boolean} disabled True to disable the field (defaults to false).
6770      */
6771     disabled : false,
6772      /**
6773      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
6774      */
6775     allowBlank : true,
6776     /**
6777      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
6778      */
6779     blankText : "This field is required",
6780     
6781      /**
6782      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
6783      */
6784     minLength : 0,
6785     /**
6786      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
6787      */
6788     maxLength : Number.MAX_VALUE,
6789     /**
6790      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
6791      */
6792     minLengthText : "The minimum length for this field is {0}",
6793     /**
6794      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
6795      */
6796     maxLengthText : "The maximum length for this field is {0}",
6797   
6798     
6799     /**
6800      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
6801      * If available, this function will be called only after the basic validators all return true, and will be passed the
6802      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
6803      */
6804     validator : null,
6805     /**
6806      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
6807      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
6808      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
6809      */
6810     regex : null,
6811     /**
6812      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
6813      */
6814     regexText : "",
6815     
6816     
6817     
6818     fieldLabel : '',
6819     inputType : 'text',
6820     
6821     name : false,
6822     placeholder: false,
6823     before : false,
6824     after : false,
6825     size : false,
6826     // private
6827     hasFocus : false,
6828     preventMark: false,
6829     isFormField : true,
6830     value : '',
6831     labelWidth : 2,
6832     labelAlign : false,
6833     readOnly : false,
6834     align : false,
6835     formatedValue : false,
6836     
6837     parentLabelAlign : function()
6838     {
6839         var parent = this;
6840         while (parent.parent()) {
6841             parent = parent.parent();
6842             if (typeof(parent.labelAlign) !='undefined') {
6843                 return parent.labelAlign;
6844             }
6845         }
6846         return 'left';
6847         
6848     },
6849     
6850     getAutoCreate : function(){
6851         
6852         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
6853         
6854         var id = Roo.id();
6855         
6856         var cfg = {};
6857         
6858         if(this.inputType != 'hidden'){
6859             cfg.cls = 'form-group' //input-group
6860         }
6861         
6862         var input =  {
6863             tag: 'input',
6864             id : id,
6865             type : this.inputType,
6866             value : this.value,
6867             cls : 'form-control',
6868             placeholder : this.placeholder || ''
6869             
6870         };
6871         
6872         if(this.align){
6873             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
6874         }
6875         
6876         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
6877             input.maxLength = this.maxLength;
6878         }
6879         
6880         if (this.disabled) {
6881             input.disabled=true;
6882         }
6883         
6884         if (this.readOnly) {
6885             input.readonly=true;
6886         }
6887         
6888         if (this.name) {
6889             input.name = this.name;
6890         }
6891         if (this.size) {
6892             input.cls += ' input-' + this.size;
6893         }
6894         var settings=this;
6895         ['xs','sm','md','lg'].map(function(size){
6896             if (settings[size]) {
6897                 cfg.cls += ' col-' + size + '-' + settings[size];
6898             }
6899         });
6900         
6901         var inputblock = input;
6902         
6903         if (this.before || this.after) {
6904             
6905             inputblock = {
6906                 cls : 'input-group',
6907                 cn :  [] 
6908             };
6909             if (this.before && typeof(this.before) == 'string') {
6910                 
6911                 inputblock.cn.push({
6912                     tag :'span',
6913                     cls : 'roo-input-before input-group-addon',
6914                     html : this.before
6915                 });
6916             }
6917             if (this.before && typeof(this.before) == 'object') {
6918                 this.before = Roo.factory(this.before);
6919                 Roo.log(this.before);
6920                 inputblock.cn.push({
6921                     tag :'span',
6922                     cls : 'roo-input-before input-group-' +
6923                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
6924                 });
6925             }
6926             
6927             inputblock.cn.push(input);
6928             
6929             if (this.after && typeof(this.after) == 'string') {
6930                 inputblock.cn.push({
6931                     tag :'span',
6932                     cls : 'roo-input-after input-group-addon',
6933                     html : this.after
6934                 });
6935             }
6936             if (this.after && typeof(this.after) == 'object') {
6937                 this.after = Roo.factory(this.after);
6938                 Roo.log(this.after);
6939                 inputblock.cn.push({
6940                     tag :'span',
6941                     cls : 'roo-input-after input-group-' +
6942                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
6943                 });
6944             }
6945         };
6946         
6947         if (align ==='left' && this.fieldLabel.length) {
6948                 Roo.log("left and has label");
6949                 cfg.cn = [
6950                     
6951                     {
6952                         tag: 'label',
6953                         'for' :  id,
6954                         cls : 'control-label col-sm-' + this.labelWidth,
6955                         html : this.fieldLabel
6956                         
6957                     },
6958                     {
6959                         cls : "col-sm-" + (12 - this.labelWidth), 
6960                         cn: [
6961                             inputblock
6962                         ]
6963                     }
6964                     
6965                 ];
6966         } else if ( this.fieldLabel.length) {
6967                 Roo.log(" label");
6968                  cfg.cn = [
6969                    
6970                     {
6971                         tag: 'label',
6972                         //cls : 'input-group-addon',
6973                         html : this.fieldLabel
6974                         
6975                     },
6976                     
6977                     inputblock
6978                     
6979                 ];
6980
6981         } else {
6982             
6983                 Roo.log(" no label && no align");
6984                 cfg.cn = [
6985                     
6986                         inputblock
6987                     
6988                 ];
6989                 
6990                 
6991         };
6992         Roo.log('input-parentType: ' + this.parentType);
6993         
6994         if (this.parentType === 'Navbar' &&  this.parent().bar) {
6995            cfg.cls += ' navbar-form';
6996            Roo.log(cfg);
6997         }
6998         
6999         return cfg;
7000         
7001     },
7002     /**
7003      * return the real input element.
7004      */
7005     inputEl: function ()
7006     {
7007         return this.el.select('input.form-control',true).first();
7008     },
7009     setDisabled : function(v)
7010     {
7011         var i  = this.inputEl().dom;
7012         if (!v) {
7013             i.removeAttribute('disabled');
7014             return;
7015             
7016         }
7017         i.setAttribute('disabled','true');
7018     },
7019     initEvents : function()
7020     {
7021         
7022         this.inputEl().on("keydown" , this.fireKey,  this);
7023         this.inputEl().on("focus", this.onFocus,  this);
7024         this.inputEl().on("blur", this.onBlur,  this);
7025         
7026         this.inputEl().relayEvent('keyup', this);
7027
7028         // reference to original value for reset
7029         this.originalValue = this.getValue();
7030         //Roo.form.TextField.superclass.initEvents.call(this);
7031         if(this.validationEvent == 'keyup'){
7032             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
7033             this.inputEl().on('keyup', this.filterValidation, this);
7034         }
7035         else if(this.validationEvent !== false){
7036             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
7037         }
7038         
7039         if(this.selectOnFocus){
7040             this.on("focus", this.preFocus, this);
7041             
7042         }
7043         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
7044             this.inputEl().on("keypress", this.filterKeys, this);
7045         }
7046        /* if(this.grow){
7047             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
7048             this.el.on("click", this.autoSize,  this);
7049         }
7050         */
7051         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
7052             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
7053         }
7054         
7055         if (typeof(this.before) == 'object') {
7056             this.before.render(this.el.select('.roo-input-before',true).first());
7057         }
7058         if (typeof(this.after) == 'object') {
7059             this.after.render(this.el.select('.roo-input-after',true).first());
7060         }
7061         
7062         
7063     },
7064     filterValidation : function(e){
7065         if(!e.isNavKeyPress()){
7066             this.validationTask.delay(this.validationDelay);
7067         }
7068     },
7069      /**
7070      * Validates the field value
7071      * @return {Boolean} True if the value is valid, else false
7072      */
7073     validate : function(){
7074         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
7075         if(this.disabled || this.validateValue(this.getRawValue())){
7076             this.clearInvalid();
7077             return true;
7078         }
7079         return false;
7080     },
7081     
7082     
7083     /**
7084      * Validates a value according to the field's validation rules and marks the field as invalid
7085      * if the validation fails
7086      * @param {Mixed} value The value to validate
7087      * @return {Boolean} True if the value is valid, else false
7088      */
7089     validateValue : function(value){
7090         if(value.length < 1)  { // if it's blank
7091              if(this.allowBlank){
7092                 this.clearInvalid();
7093                 return true;
7094              }else{
7095                 this.markInvalid(this.blankText);
7096                 return false;
7097              }
7098         }
7099         if(value.length < this.minLength){
7100             this.markInvalid(String.format(this.minLengthText, this.minLength));
7101             return false;
7102         }
7103         if(value.length > this.maxLength){
7104             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
7105             return false;
7106         }
7107         if(this.vtype){
7108             var vt = Roo.form.VTypes;
7109             if(!vt[this.vtype](value, this)){
7110                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
7111                 return false;
7112             }
7113         }
7114         if(typeof this.validator == "function"){
7115             var msg = this.validator(value);
7116             if(msg !== true){
7117                 this.markInvalid(msg);
7118                 return false;
7119             }
7120         }
7121         if(this.regex && !this.regex.test(value)){
7122             this.markInvalid(this.regexText);
7123             return false;
7124         }
7125         return true;
7126     },
7127
7128     
7129     
7130      // private
7131     fireKey : function(e){
7132         //Roo.log('field ' + e.getKey());
7133         if(e.isNavKeyPress()){
7134             this.fireEvent("specialkey", this, e);
7135         }
7136     },
7137     focus : function (selectText){
7138         if(this.rendered){
7139             this.inputEl().focus();
7140             if(selectText === true){
7141                 this.inputEl().dom.select();
7142             }
7143         }
7144         return this;
7145     } ,
7146     
7147     onFocus : function(){
7148         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7149            // this.el.addClass(this.focusClass);
7150         }
7151         if(!this.hasFocus){
7152             this.hasFocus = true;
7153             this.startValue = this.getValue();
7154             this.fireEvent("focus", this);
7155         }
7156     },
7157     
7158     beforeBlur : Roo.emptyFn,
7159
7160     
7161     // private
7162     onBlur : function(){
7163         this.beforeBlur();
7164         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7165             //this.el.removeClass(this.focusClass);
7166         }
7167         this.hasFocus = false;
7168         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
7169             this.validate();
7170         }
7171         var v = this.getValue();
7172         if(String(v) !== String(this.startValue)){
7173             this.fireEvent('change', this, v, this.startValue);
7174         }
7175         this.fireEvent("blur", this);
7176     },
7177     
7178     /**
7179      * Resets the current field value to the originally loaded value and clears any validation messages
7180      */
7181     reset : function(){
7182         this.setValue(this.originalValue);
7183         this.clearInvalid();
7184     },
7185      /**
7186      * Returns the name of the field
7187      * @return {Mixed} name The name field
7188      */
7189     getName: function(){
7190         return this.name;
7191     },
7192      /**
7193      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
7194      * @return {Mixed} value The field value
7195      */
7196     getValue : function(){
7197         
7198         var v = this.inputEl().getValue();
7199         
7200         return v;
7201     },
7202     /**
7203      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
7204      * @return {Mixed} value The field value
7205      */
7206     getRawValue : function(){
7207         var v = this.inputEl().getValue();
7208         
7209         return v;
7210     },
7211     
7212     /**
7213      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
7214      * @param {Mixed} value The value to set
7215      */
7216     setRawValue : function(v){
7217         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7218     },
7219     
7220     selectText : function(start, end){
7221         var v = this.getRawValue();
7222         if(v.length > 0){
7223             start = start === undefined ? 0 : start;
7224             end = end === undefined ? v.length : end;
7225             var d = this.inputEl().dom;
7226             if(d.setSelectionRange){
7227                 d.setSelectionRange(start, end);
7228             }else if(d.createTextRange){
7229                 var range = d.createTextRange();
7230                 range.moveStart("character", start);
7231                 range.moveEnd("character", v.length-end);
7232                 range.select();
7233             }
7234         }
7235     },
7236     
7237     /**
7238      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
7239      * @param {Mixed} value The value to set
7240      */
7241     setValue : function(v){
7242         this.value = v;
7243         if(this.rendered){
7244             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7245             this.validate();
7246         }
7247     },
7248     
7249     /*
7250     processValue : function(value){
7251         if(this.stripCharsRe){
7252             var newValue = value.replace(this.stripCharsRe, '');
7253             if(newValue !== value){
7254                 this.setRawValue(newValue);
7255                 return newValue;
7256             }
7257         }
7258         return value;
7259     },
7260   */
7261     preFocus : function(){
7262         
7263         if(this.selectOnFocus){
7264             this.inputEl().dom.select();
7265         }
7266     },
7267     filterKeys : function(e){
7268         var k = e.getKey();
7269         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
7270             return;
7271         }
7272         var c = e.getCharCode(), cc = String.fromCharCode(c);
7273         if(Roo.isIE && (e.isSpecialKey() || !cc)){
7274             return;
7275         }
7276         if(!this.maskRe.test(cc)){
7277             e.stopEvent();
7278         }
7279     },
7280      /**
7281      * Clear any invalid styles/messages for this field
7282      */
7283     clearInvalid : function(){
7284         
7285         if(!this.el || this.preventMark){ // not rendered
7286             return;
7287         }
7288         this.el.removeClass(this.invalidClass);
7289         /*
7290         switch(this.msgTarget){
7291             case 'qtip':
7292                 this.el.dom.qtip = '';
7293                 break;
7294             case 'title':
7295                 this.el.dom.title = '';
7296                 break;
7297             case 'under':
7298                 if(this.errorEl){
7299                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
7300                 }
7301                 break;
7302             case 'side':
7303                 if(this.errorIcon){
7304                     this.errorIcon.dom.qtip = '';
7305                     this.errorIcon.hide();
7306                     this.un('resize', this.alignErrorIcon, this);
7307                 }
7308                 break;
7309             default:
7310                 var t = Roo.getDom(this.msgTarget);
7311                 t.innerHTML = '';
7312                 t.style.display = 'none';
7313                 break;
7314         }
7315         */
7316         this.fireEvent('valid', this);
7317     },
7318      /**
7319      * Mark this field as invalid
7320      * @param {String} msg The validation message
7321      */
7322     markInvalid : function(msg){
7323         if(!this.el  || this.preventMark){ // not rendered
7324             return;
7325         }
7326         this.el.addClass(this.invalidClass);
7327         /*
7328         msg = msg || this.invalidText;
7329         switch(this.msgTarget){
7330             case 'qtip':
7331                 this.el.dom.qtip = msg;
7332                 this.el.dom.qclass = 'x-form-invalid-tip';
7333                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
7334                     Roo.QuickTips.enable();
7335                 }
7336                 break;
7337             case 'title':
7338                 this.el.dom.title = msg;
7339                 break;
7340             case 'under':
7341                 if(!this.errorEl){
7342                     var elp = this.el.findParent('.x-form-element', 5, true);
7343                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
7344                     this.errorEl.setWidth(elp.getWidth(true)-20);
7345                 }
7346                 this.errorEl.update(msg);
7347                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
7348                 break;
7349             case 'side':
7350                 if(!this.errorIcon){
7351                     var elp = this.el.findParent('.x-form-element', 5, true);
7352                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
7353                 }
7354                 this.alignErrorIcon();
7355                 this.errorIcon.dom.qtip = msg;
7356                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
7357                 this.errorIcon.show();
7358                 this.on('resize', this.alignErrorIcon, this);
7359                 break;
7360             default:
7361                 var t = Roo.getDom(this.msgTarget);
7362                 t.innerHTML = msg;
7363                 t.style.display = this.msgDisplay;
7364                 break;
7365         }
7366         */
7367         this.fireEvent('invalid', this, msg);
7368     },
7369     // private
7370     SafariOnKeyDown : function(event)
7371     {
7372         // this is a workaround for a password hang bug on chrome/ webkit.
7373         
7374         var isSelectAll = false;
7375         
7376         if(this.inputEl().dom.selectionEnd > 0){
7377             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
7378         }
7379         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
7380             event.preventDefault();
7381             this.setValue('');
7382             return;
7383         }
7384         
7385         if(isSelectAll){ // backspace and delete key
7386             
7387             event.preventDefault();
7388             // this is very hacky as keydown always get's upper case.
7389             //
7390             var cc = String.fromCharCode(event.getCharCode());
7391             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
7392             
7393         }
7394     },
7395     adjustWidth : function(tag, w){
7396         tag = tag.toLowerCase();
7397         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
7398             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
7399                 if(tag == 'input'){
7400                     return w + 2;
7401                 }
7402                 if(tag == 'textarea'){
7403                     return w-2;
7404                 }
7405             }else if(Roo.isOpera){
7406                 if(tag == 'input'){
7407                     return w + 2;
7408                 }
7409                 if(tag == 'textarea'){
7410                     return w-2;
7411                 }
7412             }
7413         }
7414         return w;
7415     }
7416     
7417 });
7418
7419  
7420 /*
7421  * - LGPL
7422  *
7423  * Input
7424  * 
7425  */
7426
7427 /**
7428  * @class Roo.bootstrap.TextArea
7429  * @extends Roo.bootstrap.Input
7430  * Bootstrap TextArea class
7431  * @cfg {Number} cols Specifies the visible width of a text area
7432  * @cfg {Number} rows Specifies the visible number of lines in a text area
7433  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
7434  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
7435  * @cfg {string} html text
7436  * 
7437  * @constructor
7438  * Create a new TextArea
7439  * @param {Object} config The config object
7440  */
7441
7442 Roo.bootstrap.TextArea = function(config){
7443     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
7444    
7445 };
7446
7447 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
7448      
7449     cols : false,
7450     rows : 5,
7451     readOnly : false,
7452     warp : 'soft',
7453     resize : false,
7454     value: false,
7455     html: false,
7456     
7457     getAutoCreate : function(){
7458         
7459         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7460         
7461         var id = Roo.id();
7462         
7463         var cfg = {};
7464         
7465         var input =  {
7466             tag: 'textarea',
7467             id : id,
7468             warp : this.warp,
7469             rows : this.rows,
7470             value : this.value || '',
7471             html: this.html || '',
7472             cls : 'form-control',
7473             placeholder : this.placeholder || '' 
7474             
7475         };
7476         
7477         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7478             input.maxLength = this.maxLength;
7479         }
7480         
7481         if(this.resize){
7482             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
7483         }
7484         
7485         if(this.cols){
7486             input.cols = this.cols;
7487         }
7488         
7489         if (this.readOnly) {
7490             input.readonly = true;
7491         }
7492         
7493         if (this.name) {
7494             input.name = this.name;
7495         }
7496         
7497         if (this.size) {
7498             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
7499         }
7500         
7501         var settings=this;
7502         ['xs','sm','md','lg'].map(function(size){
7503             if (settings[size]) {
7504                 cfg.cls += ' col-' + size + '-' + settings[size];
7505             }
7506         });
7507         
7508         var inputblock = input;
7509         
7510         if (this.before || this.after) {
7511             
7512             inputblock = {
7513                 cls : 'input-group',
7514                 cn :  [] 
7515             };
7516             if (this.before) {
7517                 inputblock.cn.push({
7518                     tag :'span',
7519                     cls : 'input-group-addon',
7520                     html : this.before
7521                 });
7522             }
7523             inputblock.cn.push(input);
7524             if (this.after) {
7525                 inputblock.cn.push({
7526                     tag :'span',
7527                     cls : 'input-group-addon',
7528                     html : this.after
7529                 });
7530             }
7531             
7532         }
7533         
7534         if (align ==='left' && this.fieldLabel.length) {
7535                 Roo.log("left and has label");
7536                 cfg.cn = [
7537                     
7538                     {
7539                         tag: 'label',
7540                         'for' :  id,
7541                         cls : 'control-label col-sm-' + this.labelWidth,
7542                         html : this.fieldLabel
7543                         
7544                     },
7545                     {
7546                         cls : "col-sm-" + (12 - this.labelWidth), 
7547                         cn: [
7548                             inputblock
7549                         ]
7550                     }
7551                     
7552                 ];
7553         } else if ( this.fieldLabel.length) {
7554                 Roo.log(" label");
7555                  cfg.cn = [
7556                    
7557                     {
7558                         tag: 'label',
7559                         //cls : 'input-group-addon',
7560                         html : this.fieldLabel
7561                         
7562                     },
7563                     
7564                     inputblock
7565                     
7566                 ];
7567
7568         } else {
7569             
7570                    Roo.log(" no label && no align");
7571                 cfg.cn = [
7572                     
7573                         inputblock
7574                     
7575                 ];
7576                 
7577                 
7578         }
7579         
7580         if (this.disabled) {
7581             input.disabled=true;
7582         }
7583         
7584         return cfg;
7585         
7586     },
7587     /**
7588      * return the real textarea element.
7589      */
7590     inputEl: function ()
7591     {
7592         return this.el.select('textarea.form-control',true).first();
7593     }
7594 });
7595
7596  
7597 /*
7598  * - LGPL
7599  *
7600  * trigger field - base class for combo..
7601  * 
7602  */
7603  
7604 /**
7605  * @class Roo.bootstrap.TriggerField
7606  * @extends Roo.bootstrap.Input
7607  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
7608  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
7609  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
7610  * for which you can provide a custom implementation.  For example:
7611  * <pre><code>
7612 var trigger = new Roo.bootstrap.TriggerField();
7613 trigger.onTriggerClick = myTriggerFn;
7614 trigger.applyTo('my-field');
7615 </code></pre>
7616  *
7617  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
7618  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
7619  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
7620  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
7621  * @constructor
7622  * Create a new TriggerField.
7623  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
7624  * to the base TextField)
7625  */
7626 Roo.bootstrap.TriggerField = function(config){
7627     this.mimicing = false;
7628     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
7629 };
7630
7631 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
7632     /**
7633      * @cfg {String} triggerClass A CSS class to apply to the trigger
7634      */
7635      /**
7636      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
7637      */
7638     hideTrigger:false,
7639
7640     /** @cfg {Boolean} grow @hide */
7641     /** @cfg {Number} growMin @hide */
7642     /** @cfg {Number} growMax @hide */
7643
7644     /**
7645      * @hide 
7646      * @method
7647      */
7648     autoSize: Roo.emptyFn,
7649     // private
7650     monitorTab : true,
7651     // private
7652     deferHeight : true,
7653
7654     
7655     actionMode : 'wrap',
7656     
7657     
7658     
7659     getAutoCreate : function(){
7660        
7661         var parent = this.parent();
7662         
7663         var align = this.labelAlign || this.parentLabelAlign();
7664         
7665         var id = Roo.id();
7666         
7667         var cfg = {
7668             cls: 'form-group' //input-group
7669         };
7670         
7671         
7672         var input =  {
7673             tag: 'input',
7674             id : id,
7675             type : this.inputType,
7676             cls : 'form-control',
7677             autocomplete: 'off',
7678             placeholder : this.placeholder || '' 
7679             
7680         };
7681         if (this.name) {
7682             input.name = this.name;
7683         }
7684         if (this.size) {
7685             input.cls += ' input-' + this.size;
7686         }
7687         
7688         if (this.disabled) {
7689             input.disabled=true;
7690         }
7691         
7692         var inputblock = input;
7693         
7694         if (this.before || this.after) {
7695             
7696             inputblock = {
7697                 cls : 'input-group',
7698                 cn :  [] 
7699             };
7700             if (this.before) {
7701                 inputblock.cn.push({
7702                     tag :'span',
7703                     cls : 'input-group-addon',
7704                     html : this.before
7705                 });
7706             }
7707             inputblock.cn.push(input);
7708             if (this.after) {
7709                 inputblock.cn.push({
7710                     tag :'span',
7711                     cls : 'input-group-addon',
7712                     html : this.after
7713                 });
7714             }
7715             
7716         };
7717         
7718         var box = {
7719             tag: 'div',
7720             cn: [
7721                 {
7722                     tag: 'input',
7723                     type : 'hidden',
7724                     cls: 'form-hidden-field'
7725                 },
7726                 inputblock
7727             ]
7728             
7729         };
7730         
7731         if(this.multiple){
7732             Roo.log('multiple');
7733             
7734             box = {
7735                 tag: 'div',
7736                 cn: [
7737                     {
7738                         tag: 'input',
7739                         type : 'hidden',
7740                         cls: 'form-hidden-field'
7741                     },
7742                     {
7743                         tag: 'ul',
7744                         cls: 'select2-choices',
7745                         cn:[
7746                             {
7747                                 tag: 'li',
7748                                 cls: 'select2-search-field',
7749                                 cn: [
7750
7751                                     inputblock
7752                                 ]
7753                             }
7754                         ]
7755                     }
7756                 ]
7757             }
7758         };
7759         
7760         var combobox = {
7761             cls: 'select2-container input-group',
7762             cn: [
7763                 box,
7764                 {
7765                     tag: 'ul',
7766                     cls: 'typeahead typeahead-long dropdown-menu',
7767                     style: 'display:none'
7768                 }
7769             ]
7770         };
7771         
7772         if(!this.multiple){
7773             combobox.cn.push({
7774                 tag :'span',
7775                 cls : 'input-group-addon btn dropdown-toggle',
7776                 cn : [
7777                     {
7778                         tag: 'span',
7779                         cls: 'caret'
7780                     },
7781                     {
7782                         tag: 'span',
7783                         cls: 'combobox-clear',
7784                         cn  : [
7785                             {
7786                                 tag : 'i',
7787                                 cls: 'icon-remove'
7788                             }
7789                         ]
7790                     }
7791                 ]
7792
7793             })
7794         }
7795         
7796         if(this.multiple){
7797             combobox.cls += ' select2-container-multi';
7798         }
7799         
7800         if (align ==='left' && this.fieldLabel.length) {
7801             
7802                 Roo.log("left and has label");
7803                 cfg.cn = [
7804                     
7805                     {
7806                         tag: 'label',
7807                         'for' :  id,
7808                         cls : 'control-label col-sm-' + this.labelWidth,
7809                         html : this.fieldLabel
7810                         
7811                     },
7812                     {
7813                         cls : "col-sm-" + (12 - this.labelWidth), 
7814                         cn: [
7815                             combobox
7816                         ]
7817                     }
7818                     
7819                 ];
7820         } else if ( this.fieldLabel.length) {
7821                 Roo.log(" label");
7822                  cfg.cn = [
7823                    
7824                     {
7825                         tag: 'label',
7826                         //cls : 'input-group-addon',
7827                         html : this.fieldLabel
7828                         
7829                     },
7830                     
7831                     combobox
7832                     
7833                 ];
7834
7835         } else {
7836             
7837                 Roo.log(" no label && no align");
7838                 cfg = combobox
7839                      
7840                 
7841         }
7842          
7843         var settings=this;
7844         ['xs','sm','md','lg'].map(function(size){
7845             if (settings[size]) {
7846                 cfg.cls += ' col-' + size + '-' + settings[size];
7847             }
7848         });
7849         
7850         return cfg;
7851         
7852     },
7853     
7854     
7855     
7856     // private
7857     onResize : function(w, h){
7858 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
7859 //        if(typeof w == 'number'){
7860 //            var x = w - this.trigger.getWidth();
7861 //            this.inputEl().setWidth(this.adjustWidth('input', x));
7862 //            this.trigger.setStyle('left', x+'px');
7863 //        }
7864     },
7865
7866     // private
7867     adjustSize : Roo.BoxComponent.prototype.adjustSize,
7868
7869     // private
7870     getResizeEl : function(){
7871         return this.inputEl();
7872     },
7873
7874     // private
7875     getPositionEl : function(){
7876         return this.inputEl();
7877     },
7878
7879     // private
7880     alignErrorIcon : function(){
7881         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
7882     },
7883
7884     // private
7885     initEvents : function(){
7886         
7887         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
7888         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
7889         if(!this.multiple){
7890             this.trigger = this.el.select('span.dropdown-toggle',true).first();
7891             if(this.hideTrigger){
7892                 this.trigger.setDisplayed(false);
7893             }
7894             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
7895         }
7896         
7897         if(this.multiple){
7898             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
7899         }
7900         
7901         //this.trigger.addClassOnOver('x-form-trigger-over');
7902         //this.trigger.addClassOnClick('x-form-trigger-click');
7903         
7904         //if(!this.width){
7905         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
7906         //}
7907     },
7908
7909     // private
7910     initTrigger : function(){
7911        
7912     },
7913
7914     // private
7915     onDestroy : function(){
7916         if(this.trigger){
7917             this.trigger.removeAllListeners();
7918           //  this.trigger.remove();
7919         }
7920         //if(this.wrap){
7921         //    this.wrap.remove();
7922         //}
7923         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
7924     },
7925
7926     // private
7927     onFocus : function(){
7928         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
7929         /*
7930         if(!this.mimicing){
7931             this.wrap.addClass('x-trigger-wrap-focus');
7932             this.mimicing = true;
7933             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
7934             if(this.monitorTab){
7935                 this.el.on("keydown", this.checkTab, this);
7936             }
7937         }
7938         */
7939     },
7940
7941     // private
7942     checkTab : function(e){
7943         if(e.getKey() == e.TAB){
7944             this.triggerBlur();
7945         }
7946     },
7947
7948     // private
7949     onBlur : function(){
7950         // do nothing
7951     },
7952
7953     // private
7954     mimicBlur : function(e, t){
7955         /*
7956         if(!this.wrap.contains(t) && this.validateBlur()){
7957             this.triggerBlur();
7958         }
7959         */
7960     },
7961
7962     // private
7963     triggerBlur : function(){
7964         this.mimicing = false;
7965         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
7966         if(this.monitorTab){
7967             this.el.un("keydown", this.checkTab, this);
7968         }
7969         //this.wrap.removeClass('x-trigger-wrap-focus');
7970         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
7971     },
7972
7973     // private
7974     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
7975     validateBlur : function(e, t){
7976         return true;
7977     },
7978
7979     // private
7980     onDisable : function(){
7981         this.inputEl().dom.disabled = true;
7982         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
7983         //if(this.wrap){
7984         //    this.wrap.addClass('x-item-disabled');
7985         //}
7986     },
7987
7988     // private
7989     onEnable : function(){
7990         this.inputEl().dom.disabled = false;
7991         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
7992         //if(this.wrap){
7993         //    this.el.removeClass('x-item-disabled');
7994         //}
7995     },
7996
7997     // private
7998     onShow : function(){
7999         var ae = this.getActionEl();
8000         
8001         if(ae){
8002             ae.dom.style.display = '';
8003             ae.dom.style.visibility = 'visible';
8004         }
8005     },
8006
8007     // private
8008     
8009     onHide : function(){
8010         var ae = this.getActionEl();
8011         ae.dom.style.display = 'none';
8012     },
8013
8014     /**
8015      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
8016      * by an implementing function.
8017      * @method
8018      * @param {EventObject} e
8019      */
8020     onTriggerClick : Roo.emptyFn
8021 });
8022  /*
8023  * Based on:
8024  * Ext JS Library 1.1.1
8025  * Copyright(c) 2006-2007, Ext JS, LLC.
8026  *
8027  * Originally Released Under LGPL - original licence link has changed is not relivant.
8028  *
8029  * Fork - LGPL
8030  * <script type="text/javascript">
8031  */
8032
8033
8034 /**
8035  * @class Roo.data.SortTypes
8036  * @singleton
8037  * Defines the default sorting (casting?) comparison functions used when sorting data.
8038  */
8039 Roo.data.SortTypes = {
8040     /**
8041      * Default sort that does nothing
8042      * @param {Mixed} s The value being converted
8043      * @return {Mixed} The comparison value
8044      */
8045     none : function(s){
8046         return s;
8047     },
8048     
8049     /**
8050      * The regular expression used to strip tags
8051      * @type {RegExp}
8052      * @property
8053      */
8054     stripTagsRE : /<\/?[^>]+>/gi,
8055     
8056     /**
8057      * Strips all HTML tags to sort on text only
8058      * @param {Mixed} s The value being converted
8059      * @return {String} The comparison value
8060      */
8061     asText : function(s){
8062         return String(s).replace(this.stripTagsRE, "");
8063     },
8064     
8065     /**
8066      * Strips all HTML tags to sort on text only - Case insensitive
8067      * @param {Mixed} s The value being converted
8068      * @return {String} The comparison value
8069      */
8070     asUCText : function(s){
8071         return String(s).toUpperCase().replace(this.stripTagsRE, "");
8072     },
8073     
8074     /**
8075      * Case insensitive string
8076      * @param {Mixed} s The value being converted
8077      * @return {String} The comparison value
8078      */
8079     asUCString : function(s) {
8080         return String(s).toUpperCase();
8081     },
8082     
8083     /**
8084      * Date sorting
8085      * @param {Mixed} s The value being converted
8086      * @return {Number} The comparison value
8087      */
8088     asDate : function(s) {
8089         if(!s){
8090             return 0;
8091         }
8092         if(s instanceof Date){
8093             return s.getTime();
8094         }
8095         return Date.parse(String(s));
8096     },
8097     
8098     /**
8099      * Float sorting
8100      * @param {Mixed} s The value being converted
8101      * @return {Float} The comparison value
8102      */
8103     asFloat : function(s) {
8104         var val = parseFloat(String(s).replace(/,/g, ""));
8105         if(isNaN(val)) val = 0;
8106         return val;
8107     },
8108     
8109     /**
8110      * Integer sorting
8111      * @param {Mixed} s The value being converted
8112      * @return {Number} The comparison value
8113      */
8114     asInt : function(s) {
8115         var val = parseInt(String(s).replace(/,/g, ""));
8116         if(isNaN(val)) val = 0;
8117         return val;
8118     }
8119 };/*
8120  * Based on:
8121  * Ext JS Library 1.1.1
8122  * Copyright(c) 2006-2007, Ext JS, LLC.
8123  *
8124  * Originally Released Under LGPL - original licence link has changed is not relivant.
8125  *
8126  * Fork - LGPL
8127  * <script type="text/javascript">
8128  */
8129
8130 /**
8131 * @class Roo.data.Record
8132  * Instances of this class encapsulate both record <em>definition</em> information, and record
8133  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
8134  * to access Records cached in an {@link Roo.data.Store} object.<br>
8135  * <p>
8136  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
8137  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
8138  * objects.<br>
8139  * <p>
8140  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
8141  * @constructor
8142  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
8143  * {@link #create}. The parameters are the same.
8144  * @param {Array} data An associative Array of data values keyed by the field name.
8145  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
8146  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
8147  * not specified an integer id is generated.
8148  */
8149 Roo.data.Record = function(data, id){
8150     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
8151     this.data = data;
8152 };
8153
8154 /**
8155  * Generate a constructor for a specific record layout.
8156  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
8157  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
8158  * Each field definition object may contain the following properties: <ul>
8159  * <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,
8160  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
8161  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
8162  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
8163  * is being used, then this is a string containing the javascript expression to reference the data relative to 
8164  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
8165  * to the data item relative to the record element. If the mapping expression is the same as the field name,
8166  * this may be omitted.</p></li>
8167  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
8168  * <ul><li>auto (Default, implies no conversion)</li>
8169  * <li>string</li>
8170  * <li>int</li>
8171  * <li>float</li>
8172  * <li>boolean</li>
8173  * <li>date</li></ul></p></li>
8174  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
8175  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
8176  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
8177  * by the Reader into an object that will be stored in the Record. It is passed the
8178  * following parameters:<ul>
8179  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
8180  * </ul></p></li>
8181  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
8182  * </ul>
8183  * <br>usage:<br><pre><code>
8184 var TopicRecord = Roo.data.Record.create(
8185     {name: 'title', mapping: 'topic_title'},
8186     {name: 'author', mapping: 'username'},
8187     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
8188     {name: 'lastPost', mapping: 'post_time', type: 'date'},
8189     {name: 'lastPoster', mapping: 'user2'},
8190     {name: 'excerpt', mapping: 'post_text'}
8191 );
8192
8193 var myNewRecord = new TopicRecord({
8194     title: 'Do my job please',
8195     author: 'noobie',
8196     totalPosts: 1,
8197     lastPost: new Date(),
8198     lastPoster: 'Animal',
8199     excerpt: 'No way dude!'
8200 });
8201 myStore.add(myNewRecord);
8202 </code></pre>
8203  * @method create
8204  * @static
8205  */
8206 Roo.data.Record.create = function(o){
8207     var f = function(){
8208         f.superclass.constructor.apply(this, arguments);
8209     };
8210     Roo.extend(f, Roo.data.Record);
8211     var p = f.prototype;
8212     p.fields = new Roo.util.MixedCollection(false, function(field){
8213         return field.name;
8214     });
8215     for(var i = 0, len = o.length; i < len; i++){
8216         p.fields.add(new Roo.data.Field(o[i]));
8217     }
8218     f.getField = function(name){
8219         return p.fields.get(name);  
8220     };
8221     return f;
8222 };
8223
8224 Roo.data.Record.AUTO_ID = 1000;
8225 Roo.data.Record.EDIT = 'edit';
8226 Roo.data.Record.REJECT = 'reject';
8227 Roo.data.Record.COMMIT = 'commit';
8228
8229 Roo.data.Record.prototype = {
8230     /**
8231      * Readonly flag - true if this record has been modified.
8232      * @type Boolean
8233      */
8234     dirty : false,
8235     editing : false,
8236     error: null,
8237     modified: null,
8238
8239     // private
8240     join : function(store){
8241         this.store = store;
8242     },
8243
8244     /**
8245      * Set the named field to the specified value.
8246      * @param {String} name The name of the field to set.
8247      * @param {Object} value The value to set the field to.
8248      */
8249     set : function(name, value){
8250         if(this.data[name] == value){
8251             return;
8252         }
8253         this.dirty = true;
8254         if(!this.modified){
8255             this.modified = {};
8256         }
8257         if(typeof this.modified[name] == 'undefined'){
8258             this.modified[name] = this.data[name];
8259         }
8260         this.data[name] = value;
8261         if(!this.editing && this.store){
8262             this.store.afterEdit(this);
8263         }       
8264     },
8265
8266     /**
8267      * Get the value of the named field.
8268      * @param {String} name The name of the field to get the value of.
8269      * @return {Object} The value of the field.
8270      */
8271     get : function(name){
8272         return this.data[name]; 
8273     },
8274
8275     // private
8276     beginEdit : function(){
8277         this.editing = true;
8278         this.modified = {}; 
8279     },
8280
8281     // private
8282     cancelEdit : function(){
8283         this.editing = false;
8284         delete this.modified;
8285     },
8286
8287     // private
8288     endEdit : function(){
8289         this.editing = false;
8290         if(this.dirty && this.store){
8291             this.store.afterEdit(this);
8292         }
8293     },
8294
8295     /**
8296      * Usually called by the {@link Roo.data.Store} which owns the Record.
8297      * Rejects all changes made to the Record since either creation, or the last commit operation.
8298      * Modified fields are reverted to their original values.
8299      * <p>
8300      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8301      * of reject operations.
8302      */
8303     reject : function(){
8304         var m = this.modified;
8305         for(var n in m){
8306             if(typeof m[n] != "function"){
8307                 this.data[n] = m[n];
8308             }
8309         }
8310         this.dirty = false;
8311         delete this.modified;
8312         this.editing = false;
8313         if(this.store){
8314             this.store.afterReject(this);
8315         }
8316     },
8317
8318     /**
8319      * Usually called by the {@link Roo.data.Store} which owns the Record.
8320      * Commits all changes made to the Record since either creation, or the last commit operation.
8321      * <p>
8322      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8323      * of commit operations.
8324      */
8325     commit : function(){
8326         this.dirty = false;
8327         delete this.modified;
8328         this.editing = false;
8329         if(this.store){
8330             this.store.afterCommit(this);
8331         }
8332     },
8333
8334     // private
8335     hasError : function(){
8336         return this.error != null;
8337     },
8338
8339     // private
8340     clearError : function(){
8341         this.error = null;
8342     },
8343
8344     /**
8345      * Creates a copy of this record.
8346      * @param {String} id (optional) A new record id if you don't want to use this record's id
8347      * @return {Record}
8348      */
8349     copy : function(newId) {
8350         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
8351     }
8352 };/*
8353  * Based on:
8354  * Ext JS Library 1.1.1
8355  * Copyright(c) 2006-2007, Ext JS, LLC.
8356  *
8357  * Originally Released Under LGPL - original licence link has changed is not relivant.
8358  *
8359  * Fork - LGPL
8360  * <script type="text/javascript">
8361  */
8362
8363
8364
8365 /**
8366  * @class Roo.data.Store
8367  * @extends Roo.util.Observable
8368  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
8369  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
8370  * <p>
8371  * 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
8372  * has no knowledge of the format of the data returned by the Proxy.<br>
8373  * <p>
8374  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
8375  * instances from the data object. These records are cached and made available through accessor functions.
8376  * @constructor
8377  * Creates a new Store.
8378  * @param {Object} config A config object containing the objects needed for the Store to access data,
8379  * and read the data into Records.
8380  */
8381 Roo.data.Store = function(config){
8382     this.data = new Roo.util.MixedCollection(false);
8383     this.data.getKey = function(o){
8384         return o.id;
8385     };
8386     this.baseParams = {};
8387     // private
8388     this.paramNames = {
8389         "start" : "start",
8390         "limit" : "limit",
8391         "sort" : "sort",
8392         "dir" : "dir",
8393         "multisort" : "_multisort"
8394     };
8395
8396     if(config && config.data){
8397         this.inlineData = config.data;
8398         delete config.data;
8399     }
8400
8401     Roo.apply(this, config);
8402     
8403     if(this.reader){ // reader passed
8404         this.reader = Roo.factory(this.reader, Roo.data);
8405         this.reader.xmodule = this.xmodule || false;
8406         if(!this.recordType){
8407             this.recordType = this.reader.recordType;
8408         }
8409         if(this.reader.onMetaChange){
8410             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
8411         }
8412     }
8413
8414     if(this.recordType){
8415         this.fields = this.recordType.prototype.fields;
8416     }
8417     this.modified = [];
8418
8419     this.addEvents({
8420         /**
8421          * @event datachanged
8422          * Fires when the data cache has changed, and a widget which is using this Store
8423          * as a Record cache should refresh its view.
8424          * @param {Store} this
8425          */
8426         datachanged : true,
8427         /**
8428          * @event metachange
8429          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
8430          * @param {Store} this
8431          * @param {Object} meta The JSON metadata
8432          */
8433         metachange : true,
8434         /**
8435          * @event add
8436          * Fires when Records have been added to the Store
8437          * @param {Store} this
8438          * @param {Roo.data.Record[]} records The array of Records added
8439          * @param {Number} index The index at which the record(s) were added
8440          */
8441         add : true,
8442         /**
8443          * @event remove
8444          * Fires when a Record has been removed from the Store
8445          * @param {Store} this
8446          * @param {Roo.data.Record} record The Record that was removed
8447          * @param {Number} index The index at which the record was removed
8448          */
8449         remove : true,
8450         /**
8451          * @event update
8452          * Fires when a Record has been updated
8453          * @param {Store} this
8454          * @param {Roo.data.Record} record The Record that was updated
8455          * @param {String} operation The update operation being performed.  Value may be one of:
8456          * <pre><code>
8457  Roo.data.Record.EDIT
8458  Roo.data.Record.REJECT
8459  Roo.data.Record.COMMIT
8460          * </code></pre>
8461          */
8462         update : true,
8463         /**
8464          * @event clear
8465          * Fires when the data cache has been cleared.
8466          * @param {Store} this
8467          */
8468         clear : true,
8469         /**
8470          * @event beforeload
8471          * Fires before a request is made for a new data object.  If the beforeload handler returns false
8472          * the load action will be canceled.
8473          * @param {Store} this
8474          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8475          */
8476         beforeload : true,
8477         /**
8478          * @event beforeloadadd
8479          * Fires after a new set of Records has been loaded.
8480          * @param {Store} this
8481          * @param {Roo.data.Record[]} records The Records that were loaded
8482          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8483          */
8484         beforeloadadd : true,
8485         /**
8486          * @event load
8487          * Fires after a new set of Records has been loaded, before they are added to the store.
8488          * @param {Store} this
8489          * @param {Roo.data.Record[]} records The Records that were loaded
8490          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8491          * @params {Object} return from reader
8492          */
8493         load : true,
8494         /**
8495          * @event loadexception
8496          * Fires if an exception occurs in the Proxy during loading.
8497          * Called with the signature of the Proxy's "loadexception" event.
8498          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
8499          * 
8500          * @param {Proxy} 
8501          * @param {Object} return from JsonData.reader() - success, totalRecords, records
8502          * @param {Object} load options 
8503          * @param {Object} jsonData from your request (normally this contains the Exception)
8504          */
8505         loadexception : true
8506     });
8507     
8508     if(this.proxy){
8509         this.proxy = Roo.factory(this.proxy, Roo.data);
8510         this.proxy.xmodule = this.xmodule || false;
8511         this.relayEvents(this.proxy,  ["loadexception"]);
8512     }
8513     this.sortToggle = {};
8514     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
8515
8516     Roo.data.Store.superclass.constructor.call(this);
8517
8518     if(this.inlineData){
8519         this.loadData(this.inlineData);
8520         delete this.inlineData;
8521     }
8522 };
8523
8524 Roo.extend(Roo.data.Store, Roo.util.Observable, {
8525      /**
8526     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
8527     * without a remote query - used by combo/forms at present.
8528     */
8529     
8530     /**
8531     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
8532     */
8533     /**
8534     * @cfg {Array} data Inline data to be loaded when the store is initialized.
8535     */
8536     /**
8537     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
8538     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
8539     */
8540     /**
8541     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
8542     * on any HTTP request
8543     */
8544     /**
8545     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
8546     */
8547     /**
8548     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
8549     */
8550     multiSort: false,
8551     /**
8552     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
8553     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
8554     */
8555     remoteSort : false,
8556
8557     /**
8558     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
8559      * loaded or when a record is removed. (defaults to false).
8560     */
8561     pruneModifiedRecords : false,
8562
8563     // private
8564     lastOptions : null,
8565
8566     /**
8567      * Add Records to the Store and fires the add event.
8568      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8569      */
8570     add : function(records){
8571         records = [].concat(records);
8572         for(var i = 0, len = records.length; i < len; i++){
8573             records[i].join(this);
8574         }
8575         var index = this.data.length;
8576         this.data.addAll(records);
8577         this.fireEvent("add", this, records, index);
8578     },
8579
8580     /**
8581      * Remove a Record from the Store and fires the remove event.
8582      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
8583      */
8584     remove : function(record){
8585         var index = this.data.indexOf(record);
8586         this.data.removeAt(index);
8587         if(this.pruneModifiedRecords){
8588             this.modified.remove(record);
8589         }
8590         this.fireEvent("remove", this, record, index);
8591     },
8592
8593     /**
8594      * Remove all Records from the Store and fires the clear event.
8595      */
8596     removeAll : function(){
8597         this.data.clear();
8598         if(this.pruneModifiedRecords){
8599             this.modified = [];
8600         }
8601         this.fireEvent("clear", this);
8602     },
8603
8604     /**
8605      * Inserts Records to the Store at the given index and fires the add event.
8606      * @param {Number} index The start index at which to insert the passed Records.
8607      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8608      */
8609     insert : function(index, records){
8610         records = [].concat(records);
8611         for(var i = 0, len = records.length; i < len; i++){
8612             this.data.insert(index, records[i]);
8613             records[i].join(this);
8614         }
8615         this.fireEvent("add", this, records, index);
8616     },
8617
8618     /**
8619      * Get the index within the cache of the passed Record.
8620      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
8621      * @return {Number} The index of the passed Record. Returns -1 if not found.
8622      */
8623     indexOf : function(record){
8624         return this.data.indexOf(record);
8625     },
8626
8627     /**
8628      * Get the index within the cache of the Record with the passed id.
8629      * @param {String} id The id of the Record to find.
8630      * @return {Number} The index of the Record. Returns -1 if not found.
8631      */
8632     indexOfId : function(id){
8633         return this.data.indexOfKey(id);
8634     },
8635
8636     /**
8637      * Get the Record with the specified id.
8638      * @param {String} id The id of the Record to find.
8639      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
8640      */
8641     getById : function(id){
8642         return this.data.key(id);
8643     },
8644
8645     /**
8646      * Get the Record at the specified index.
8647      * @param {Number} index The index of the Record to find.
8648      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
8649      */
8650     getAt : function(index){
8651         return this.data.itemAt(index);
8652     },
8653
8654     /**
8655      * Returns a range of Records between specified indices.
8656      * @param {Number} startIndex (optional) The starting index (defaults to 0)
8657      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
8658      * @return {Roo.data.Record[]} An array of Records
8659      */
8660     getRange : function(start, end){
8661         return this.data.getRange(start, end);
8662     },
8663
8664     // private
8665     storeOptions : function(o){
8666         o = Roo.apply({}, o);
8667         delete o.callback;
8668         delete o.scope;
8669         this.lastOptions = o;
8670     },
8671
8672     /**
8673      * Loads the Record cache from the configured Proxy using the configured Reader.
8674      * <p>
8675      * If using remote paging, then the first load call must specify the <em>start</em>
8676      * and <em>limit</em> properties in the options.params property to establish the initial
8677      * position within the dataset, and the number of Records to cache on each read from the Proxy.
8678      * <p>
8679      * <strong>It is important to note that for remote data sources, loading is asynchronous,
8680      * and this call will return before the new data has been loaded. Perform any post-processing
8681      * in a callback function, or in a "load" event handler.</strong>
8682      * <p>
8683      * @param {Object} options An object containing properties which control loading options:<ul>
8684      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
8685      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
8686      * passed the following arguments:<ul>
8687      * <li>r : Roo.data.Record[]</li>
8688      * <li>options: Options object from the load call</li>
8689      * <li>success: Boolean success indicator</li></ul></li>
8690      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
8691      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
8692      * </ul>
8693      */
8694     load : function(options){
8695         options = options || {};
8696         if(this.fireEvent("beforeload", this, options) !== false){
8697             this.storeOptions(options);
8698             var p = Roo.apply(options.params || {}, this.baseParams);
8699             // if meta was not loaded from remote source.. try requesting it.
8700             if (!this.reader.metaFromRemote) {
8701                 p._requestMeta = 1;
8702             }
8703             if(this.sortInfo && this.remoteSort){
8704                 var pn = this.paramNames;
8705                 p[pn["sort"]] = this.sortInfo.field;
8706                 p[pn["dir"]] = this.sortInfo.direction;
8707             }
8708             if (this.multiSort) {
8709                 var pn = this.paramNames;
8710                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
8711             }
8712             
8713             this.proxy.load(p, this.reader, this.loadRecords, this, options);
8714         }
8715     },
8716
8717     /**
8718      * Reloads the Record cache from the configured Proxy using the configured Reader and
8719      * the options from the last load operation performed.
8720      * @param {Object} options (optional) An object containing properties which may override the options
8721      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
8722      * the most recently used options are reused).
8723      */
8724     reload : function(options){
8725         this.load(Roo.applyIf(options||{}, this.lastOptions));
8726     },
8727
8728     // private
8729     // Called as a callback by the Reader during a load operation.
8730     loadRecords : function(o, options, success){
8731         if(!o || success === false){
8732             if(success !== false){
8733                 this.fireEvent("load", this, [], options, o);
8734             }
8735             if(options.callback){
8736                 options.callback.call(options.scope || this, [], options, false);
8737             }
8738             return;
8739         }
8740         // if data returned failure - throw an exception.
8741         if (o.success === false) {
8742             // show a message if no listener is registered.
8743             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
8744                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
8745             }
8746             // loadmask wil be hooked into this..
8747             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
8748             return;
8749         }
8750         var r = o.records, t = o.totalRecords || r.length;
8751         
8752         this.fireEvent("beforeloadadd", this, r, options, o);
8753         
8754         if(!options || options.add !== true){
8755             if(this.pruneModifiedRecords){
8756                 this.modified = [];
8757             }
8758             for(var i = 0, len = r.length; i < len; i++){
8759                 r[i].join(this);
8760             }
8761             if(this.snapshot){
8762                 this.data = this.snapshot;
8763                 delete this.snapshot;
8764             }
8765             this.data.clear();
8766             this.data.addAll(r);
8767             this.totalLength = t;
8768             this.applySort();
8769             this.fireEvent("datachanged", this);
8770         }else{
8771             this.totalLength = Math.max(t, this.data.length+r.length);
8772             this.add(r);
8773         }
8774         this.fireEvent("load", this, r, options, o);
8775         if(options.callback){
8776             options.callback.call(options.scope || this, r, options, true);
8777         }
8778     },
8779
8780
8781     /**
8782      * Loads data from a passed data block. A Reader which understands the format of the data
8783      * must have been configured in the constructor.
8784      * @param {Object} data The data block from which to read the Records.  The format of the data expected
8785      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
8786      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
8787      */
8788     loadData : function(o, append){
8789         var r = this.reader.readRecords(o);
8790         this.loadRecords(r, {add: append}, true);
8791     },
8792
8793     /**
8794      * Gets the number of cached records.
8795      * <p>
8796      * <em>If using paging, this may not be the total size of the dataset. If the data object
8797      * used by the Reader contains the dataset size, then the getTotalCount() function returns
8798      * the data set size</em>
8799      */
8800     getCount : function(){
8801         return this.data.length || 0;
8802     },
8803
8804     /**
8805      * Gets the total number of records in the dataset as returned by the server.
8806      * <p>
8807      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
8808      * the dataset size</em>
8809      */
8810     getTotalCount : function(){
8811         return this.totalLength || 0;
8812     },
8813
8814     /**
8815      * Returns the sort state of the Store as an object with two properties:
8816      * <pre><code>
8817  field {String} The name of the field by which the Records are sorted
8818  direction {String} The sort order, "ASC" or "DESC"
8819      * </code></pre>
8820      */
8821     getSortState : function(){
8822         return this.sortInfo;
8823     },
8824
8825     // private
8826     applySort : function(){
8827         if(this.sortInfo && !this.remoteSort){
8828             var s = this.sortInfo, f = s.field;
8829             var st = this.fields.get(f).sortType;
8830             var fn = function(r1, r2){
8831                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
8832                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
8833             };
8834             this.data.sort(s.direction, fn);
8835             if(this.snapshot && this.snapshot != this.data){
8836                 this.snapshot.sort(s.direction, fn);
8837             }
8838         }
8839     },
8840
8841     /**
8842      * Sets the default sort column and order to be used by the next load operation.
8843      * @param {String} fieldName The name of the field to sort by.
8844      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
8845      */
8846     setDefaultSort : function(field, dir){
8847         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
8848     },
8849
8850     /**
8851      * Sort the Records.
8852      * If remote sorting is used, the sort is performed on the server, and the cache is
8853      * reloaded. If local sorting is used, the cache is sorted internally.
8854      * @param {String} fieldName The name of the field to sort by.
8855      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
8856      */
8857     sort : function(fieldName, dir){
8858         var f = this.fields.get(fieldName);
8859         if(!dir){
8860             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
8861             
8862             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
8863                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
8864             }else{
8865                 dir = f.sortDir;
8866             }
8867         }
8868         this.sortToggle[f.name] = dir;
8869         this.sortInfo = {field: f.name, direction: dir};
8870         if(!this.remoteSort){
8871             this.applySort();
8872             this.fireEvent("datachanged", this);
8873         }else{
8874             this.load(this.lastOptions);
8875         }
8876     },
8877
8878     /**
8879      * Calls the specified function for each of the Records in the cache.
8880      * @param {Function} fn The function to call. The Record is passed as the first parameter.
8881      * Returning <em>false</em> aborts and exits the iteration.
8882      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
8883      */
8884     each : function(fn, scope){
8885         this.data.each(fn, scope);
8886     },
8887
8888     /**
8889      * Gets all records modified since the last commit.  Modified records are persisted across load operations
8890      * (e.g., during paging).
8891      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
8892      */
8893     getModifiedRecords : function(){
8894         return this.modified;
8895     },
8896
8897     // private
8898     createFilterFn : function(property, value, anyMatch){
8899         if(!value.exec){ // not a regex
8900             value = String(value);
8901             if(value.length == 0){
8902                 return false;
8903             }
8904             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
8905         }
8906         return function(r){
8907             return value.test(r.data[property]);
8908         };
8909     },
8910
8911     /**
8912      * Sums the value of <i>property</i> for each record between start and end and returns the result.
8913      * @param {String} property A field on your records
8914      * @param {Number} start The record index to start at (defaults to 0)
8915      * @param {Number} end The last record index to include (defaults to length - 1)
8916      * @return {Number} The sum
8917      */
8918     sum : function(property, start, end){
8919         var rs = this.data.items, v = 0;
8920         start = start || 0;
8921         end = (end || end === 0) ? end : rs.length-1;
8922
8923         for(var i = start; i <= end; i++){
8924             v += (rs[i].data[property] || 0);
8925         }
8926         return v;
8927     },
8928
8929     /**
8930      * Filter the records by a specified property.
8931      * @param {String} field A field on your records
8932      * @param {String/RegExp} value Either a string that the field
8933      * should start with or a RegExp to test against the field
8934      * @param {Boolean} anyMatch True to match any part not just the beginning
8935      */
8936     filter : function(property, value, anyMatch){
8937         var fn = this.createFilterFn(property, value, anyMatch);
8938         return fn ? this.filterBy(fn) : this.clearFilter();
8939     },
8940
8941     /**
8942      * Filter by a function. The specified function will be called with each
8943      * record in this data source. If the function returns true the record is included,
8944      * otherwise it is filtered.
8945      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
8946      * @param {Object} scope (optional) The scope of the function (defaults to this)
8947      */
8948     filterBy : function(fn, scope){
8949         this.snapshot = this.snapshot || this.data;
8950         this.data = this.queryBy(fn, scope||this);
8951         this.fireEvent("datachanged", this);
8952     },
8953
8954     /**
8955      * Query the records by a specified property.
8956      * @param {String} field A field on your records
8957      * @param {String/RegExp} value Either a string that the field
8958      * should start with or a RegExp to test against the field
8959      * @param {Boolean} anyMatch True to match any part not just the beginning
8960      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
8961      */
8962     query : function(property, value, anyMatch){
8963         var fn = this.createFilterFn(property, value, anyMatch);
8964         return fn ? this.queryBy(fn) : this.data.clone();
8965     },
8966
8967     /**
8968      * Query by a function. The specified function will be called with each
8969      * record in this data source. If the function returns true the record is included
8970      * in the results.
8971      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
8972      * @param {Object} scope (optional) The scope of the function (defaults to this)
8973       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
8974      **/
8975     queryBy : function(fn, scope){
8976         var data = this.snapshot || this.data;
8977         return data.filterBy(fn, scope||this);
8978     },
8979
8980     /**
8981      * Collects unique values for a particular dataIndex from this store.
8982      * @param {String} dataIndex The property to collect
8983      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
8984      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
8985      * @return {Array} An array of the unique values
8986      **/
8987     collect : function(dataIndex, allowNull, bypassFilter){
8988         var d = (bypassFilter === true && this.snapshot) ?
8989                 this.snapshot.items : this.data.items;
8990         var v, sv, r = [], l = {};
8991         for(var i = 0, len = d.length; i < len; i++){
8992             v = d[i].data[dataIndex];
8993             sv = String(v);
8994             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
8995                 l[sv] = true;
8996                 r[r.length] = v;
8997             }
8998         }
8999         return r;
9000     },
9001
9002     /**
9003      * Revert to a view of the Record cache with no filtering applied.
9004      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
9005      */
9006     clearFilter : function(suppressEvent){
9007         if(this.snapshot && this.snapshot != this.data){
9008             this.data = this.snapshot;
9009             delete this.snapshot;
9010             if(suppressEvent !== true){
9011                 this.fireEvent("datachanged", this);
9012             }
9013         }
9014     },
9015
9016     // private
9017     afterEdit : function(record){
9018         if(this.modified.indexOf(record) == -1){
9019             this.modified.push(record);
9020         }
9021         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
9022     },
9023     
9024     // private
9025     afterReject : function(record){
9026         this.modified.remove(record);
9027         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
9028     },
9029
9030     // private
9031     afterCommit : function(record){
9032         this.modified.remove(record);
9033         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
9034     },
9035
9036     /**
9037      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
9038      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
9039      */
9040     commitChanges : function(){
9041         var m = this.modified.slice(0);
9042         this.modified = [];
9043         for(var i = 0, len = m.length; i < len; i++){
9044             m[i].commit();
9045         }
9046     },
9047
9048     /**
9049      * Cancel outstanding changes on all changed records.
9050      */
9051     rejectChanges : function(){
9052         var m = this.modified.slice(0);
9053         this.modified = [];
9054         for(var i = 0, len = m.length; i < len; i++){
9055             m[i].reject();
9056         }
9057     },
9058
9059     onMetaChange : function(meta, rtype, o){
9060         this.recordType = rtype;
9061         this.fields = rtype.prototype.fields;
9062         delete this.snapshot;
9063         this.sortInfo = meta.sortInfo || this.sortInfo;
9064         this.modified = [];
9065         this.fireEvent('metachange', this, this.reader.meta);
9066     },
9067     
9068     moveIndex : function(data, type)
9069     {
9070         var index = this.indexOf(data);
9071         
9072         var newIndex = index + type;
9073         
9074         this.remove(data);
9075         
9076         this.insert(newIndex, data);
9077         
9078     }
9079 });/*
9080  * Based on:
9081  * Ext JS Library 1.1.1
9082  * Copyright(c) 2006-2007, Ext JS, LLC.
9083  *
9084  * Originally Released Under LGPL - original licence link has changed is not relivant.
9085  *
9086  * Fork - LGPL
9087  * <script type="text/javascript">
9088  */
9089
9090 /**
9091  * @class Roo.data.SimpleStore
9092  * @extends Roo.data.Store
9093  * Small helper class to make creating Stores from Array data easier.
9094  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
9095  * @cfg {Array} fields An array of field definition objects, or field name strings.
9096  * @cfg {Array} data The multi-dimensional array of data
9097  * @constructor
9098  * @param {Object} config
9099  */
9100 Roo.data.SimpleStore = function(config){
9101     Roo.data.SimpleStore.superclass.constructor.call(this, {
9102         isLocal : true,
9103         reader: new Roo.data.ArrayReader({
9104                 id: config.id
9105             },
9106             Roo.data.Record.create(config.fields)
9107         ),
9108         proxy : new Roo.data.MemoryProxy(config.data)
9109     });
9110     this.load();
9111 };
9112 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
9113  * Based on:
9114  * Ext JS Library 1.1.1
9115  * Copyright(c) 2006-2007, Ext JS, LLC.
9116  *
9117  * Originally Released Under LGPL - original licence link has changed is not relivant.
9118  *
9119  * Fork - LGPL
9120  * <script type="text/javascript">
9121  */
9122
9123 /**
9124 /**
9125  * @extends Roo.data.Store
9126  * @class Roo.data.JsonStore
9127  * Small helper class to make creating Stores for JSON data easier. <br/>
9128 <pre><code>
9129 var store = new Roo.data.JsonStore({
9130     url: 'get-images.php',
9131     root: 'images',
9132     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
9133 });
9134 </code></pre>
9135  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
9136  * JsonReader and HttpProxy (unless inline data is provided).</b>
9137  * @cfg {Array} fields An array of field definition objects, or field name strings.
9138  * @constructor
9139  * @param {Object} config
9140  */
9141 Roo.data.JsonStore = function(c){
9142     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
9143         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
9144         reader: new Roo.data.JsonReader(c, c.fields)
9145     }));
9146 };
9147 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
9148  * Based on:
9149  * Ext JS Library 1.1.1
9150  * Copyright(c) 2006-2007, Ext JS, LLC.
9151  *
9152  * Originally Released Under LGPL - original licence link has changed is not relivant.
9153  *
9154  * Fork - LGPL
9155  * <script type="text/javascript">
9156  */
9157
9158  
9159 Roo.data.Field = function(config){
9160     if(typeof config == "string"){
9161         config = {name: config};
9162     }
9163     Roo.apply(this, config);
9164     
9165     if(!this.type){
9166         this.type = "auto";
9167     }
9168     
9169     var st = Roo.data.SortTypes;
9170     // named sortTypes are supported, here we look them up
9171     if(typeof this.sortType == "string"){
9172         this.sortType = st[this.sortType];
9173     }
9174     
9175     // set default sortType for strings and dates
9176     if(!this.sortType){
9177         switch(this.type){
9178             case "string":
9179                 this.sortType = st.asUCString;
9180                 break;
9181             case "date":
9182                 this.sortType = st.asDate;
9183                 break;
9184             default:
9185                 this.sortType = st.none;
9186         }
9187     }
9188
9189     // define once
9190     var stripRe = /[\$,%]/g;
9191
9192     // prebuilt conversion function for this field, instead of
9193     // switching every time we're reading a value
9194     if(!this.convert){
9195         var cv, dateFormat = this.dateFormat;
9196         switch(this.type){
9197             case "":
9198             case "auto":
9199             case undefined:
9200                 cv = function(v){ return v; };
9201                 break;
9202             case "string":
9203                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
9204                 break;
9205             case "int":
9206                 cv = function(v){
9207                     return v !== undefined && v !== null && v !== '' ?
9208                            parseInt(String(v).replace(stripRe, ""), 10) : '';
9209                     };
9210                 break;
9211             case "float":
9212                 cv = function(v){
9213                     return v !== undefined && v !== null && v !== '' ?
9214                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
9215                     };
9216                 break;
9217             case "bool":
9218             case "boolean":
9219                 cv = function(v){ return v === true || v === "true" || v == 1; };
9220                 break;
9221             case "date":
9222                 cv = function(v){
9223                     if(!v){
9224                         return '';
9225                     }
9226                     if(v instanceof Date){
9227                         return v;
9228                     }
9229                     if(dateFormat){
9230                         if(dateFormat == "timestamp"){
9231                             return new Date(v*1000);
9232                         }
9233                         return Date.parseDate(v, dateFormat);
9234                     }
9235                     var parsed = Date.parse(v);
9236                     return parsed ? new Date(parsed) : null;
9237                 };
9238              break;
9239             
9240         }
9241         this.convert = cv;
9242     }
9243 };
9244
9245 Roo.data.Field.prototype = {
9246     dateFormat: null,
9247     defaultValue: "",
9248     mapping: null,
9249     sortType : null,
9250     sortDir : "ASC"
9251 };/*
9252  * Based on:
9253  * Ext JS Library 1.1.1
9254  * Copyright(c) 2006-2007, Ext JS, LLC.
9255  *
9256  * Originally Released Under LGPL - original licence link has changed is not relivant.
9257  *
9258  * Fork - LGPL
9259  * <script type="text/javascript">
9260  */
9261  
9262 // Base class for reading structured data from a data source.  This class is intended to be
9263 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
9264
9265 /**
9266  * @class Roo.data.DataReader
9267  * Base class for reading structured data from a data source.  This class is intended to be
9268  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
9269  */
9270
9271 Roo.data.DataReader = function(meta, recordType){
9272     
9273     this.meta = meta;
9274     
9275     this.recordType = recordType instanceof Array ? 
9276         Roo.data.Record.create(recordType) : recordType;
9277 };
9278
9279 Roo.data.DataReader.prototype = {
9280      /**
9281      * Create an empty record
9282      * @param {Object} data (optional) - overlay some values
9283      * @return {Roo.data.Record} record created.
9284      */
9285     newRow :  function(d) {
9286         var da =  {};
9287         this.recordType.prototype.fields.each(function(c) {
9288             switch( c.type) {
9289                 case 'int' : da[c.name] = 0; break;
9290                 case 'date' : da[c.name] = new Date(); break;
9291                 case 'float' : da[c.name] = 0.0; break;
9292                 case 'boolean' : da[c.name] = false; break;
9293                 default : da[c.name] = ""; break;
9294             }
9295             
9296         });
9297         return new this.recordType(Roo.apply(da, d));
9298     }
9299     
9300 };/*
9301  * Based on:
9302  * Ext JS Library 1.1.1
9303  * Copyright(c) 2006-2007, Ext JS, LLC.
9304  *
9305  * Originally Released Under LGPL - original licence link has changed is not relivant.
9306  *
9307  * Fork - LGPL
9308  * <script type="text/javascript">
9309  */
9310
9311 /**
9312  * @class Roo.data.DataProxy
9313  * @extends Roo.data.Observable
9314  * This class is an abstract base class for implementations which provide retrieval of
9315  * unformatted data objects.<br>
9316  * <p>
9317  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
9318  * (of the appropriate type which knows how to parse the data object) to provide a block of
9319  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
9320  * <p>
9321  * Custom implementations must implement the load method as described in
9322  * {@link Roo.data.HttpProxy#load}.
9323  */
9324 Roo.data.DataProxy = function(){
9325     this.addEvents({
9326         /**
9327          * @event beforeload
9328          * Fires before a network request is made to retrieve a data object.
9329          * @param {Object} This DataProxy object.
9330          * @param {Object} params The params parameter to the load function.
9331          */
9332         beforeload : true,
9333         /**
9334          * @event load
9335          * Fires before the load method's callback is called.
9336          * @param {Object} This DataProxy object.
9337          * @param {Object} o The data object.
9338          * @param {Object} arg The callback argument object passed to the load function.
9339          */
9340         load : true,
9341         /**
9342          * @event loadexception
9343          * Fires if an Exception occurs during data retrieval.
9344          * @param {Object} This DataProxy object.
9345          * @param {Object} o The data object.
9346          * @param {Object} arg The callback argument object passed to the load function.
9347          * @param {Object} e The Exception.
9348          */
9349         loadexception : true
9350     });
9351     Roo.data.DataProxy.superclass.constructor.call(this);
9352 };
9353
9354 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
9355
9356     /**
9357      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
9358      */
9359 /*
9360  * Based on:
9361  * Ext JS Library 1.1.1
9362  * Copyright(c) 2006-2007, Ext JS, LLC.
9363  *
9364  * Originally Released Under LGPL - original licence link has changed is not relivant.
9365  *
9366  * Fork - LGPL
9367  * <script type="text/javascript">
9368  */
9369 /**
9370  * @class Roo.data.MemoryProxy
9371  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
9372  * to the Reader when its load method is called.
9373  * @constructor
9374  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
9375  */
9376 Roo.data.MemoryProxy = function(data){
9377     if (data.data) {
9378         data = data.data;
9379     }
9380     Roo.data.MemoryProxy.superclass.constructor.call(this);
9381     this.data = data;
9382 };
9383
9384 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
9385     /**
9386      * Load data from the requested source (in this case an in-memory
9387      * data object passed to the constructor), read the data object into
9388      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9389      * process that block using the passed callback.
9390      * @param {Object} params This parameter is not used by the MemoryProxy class.
9391      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9392      * object into a block of Roo.data.Records.
9393      * @param {Function} callback The function into which to pass the block of Roo.data.records.
9394      * The function must be passed <ul>
9395      * <li>The Record block object</li>
9396      * <li>The "arg" argument from the load function</li>
9397      * <li>A boolean success indicator</li>
9398      * </ul>
9399      * @param {Object} scope The scope in which to call the callback
9400      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9401      */
9402     load : function(params, reader, callback, scope, arg){
9403         params = params || {};
9404         var result;
9405         try {
9406             result = reader.readRecords(this.data);
9407         }catch(e){
9408             this.fireEvent("loadexception", this, arg, null, e);
9409             callback.call(scope, null, arg, false);
9410             return;
9411         }
9412         callback.call(scope, result, arg, true);
9413     },
9414     
9415     // private
9416     update : function(params, records){
9417         
9418     }
9419 });/*
9420  * Based on:
9421  * Ext JS Library 1.1.1
9422  * Copyright(c) 2006-2007, Ext JS, LLC.
9423  *
9424  * Originally Released Under LGPL - original licence link has changed is not relivant.
9425  *
9426  * Fork - LGPL
9427  * <script type="text/javascript">
9428  */
9429 /**
9430  * @class Roo.data.HttpProxy
9431  * @extends Roo.data.DataProxy
9432  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
9433  * configured to reference a certain URL.<br><br>
9434  * <p>
9435  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
9436  * from which the running page was served.<br><br>
9437  * <p>
9438  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
9439  * <p>
9440  * Be aware that to enable the browser to parse an XML document, the server must set
9441  * the Content-Type header in the HTTP response to "text/xml".
9442  * @constructor
9443  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
9444  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
9445  * will be used to make the request.
9446  */
9447 Roo.data.HttpProxy = function(conn){
9448     Roo.data.HttpProxy.superclass.constructor.call(this);
9449     // is conn a conn config or a real conn?
9450     this.conn = conn;
9451     this.useAjax = !conn || !conn.events;
9452   
9453 };
9454
9455 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
9456     // thse are take from connection...
9457     
9458     /**
9459      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
9460      */
9461     /**
9462      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
9463      * extra parameters to each request made by this object. (defaults to undefined)
9464      */
9465     /**
9466      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
9467      *  to each request made by this object. (defaults to undefined)
9468      */
9469     /**
9470      * @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)
9471      */
9472     /**
9473      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
9474      */
9475      /**
9476      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
9477      * @type Boolean
9478      */
9479   
9480
9481     /**
9482      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
9483      * @type Boolean
9484      */
9485     /**
9486      * Return the {@link Roo.data.Connection} object being used by this Proxy.
9487      * @return {Connection} The Connection object. This object may be used to subscribe to events on
9488      * a finer-grained basis than the DataProxy events.
9489      */
9490     getConnection : function(){
9491         return this.useAjax ? Roo.Ajax : this.conn;
9492     },
9493
9494     /**
9495      * Load data from the configured {@link Roo.data.Connection}, read the data object into
9496      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
9497      * process that block using the passed callback.
9498      * @param {Object} params An object containing properties which are to be used as HTTP parameters
9499      * for the request to the remote server.
9500      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9501      * object into a block of Roo.data.Records.
9502      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9503      * The function must be passed <ul>
9504      * <li>The Record block object</li>
9505      * <li>The "arg" argument from the load function</li>
9506      * <li>A boolean success indicator</li>
9507      * </ul>
9508      * @param {Object} scope The scope in which to call the callback
9509      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9510      */
9511     load : function(params, reader, callback, scope, arg){
9512         if(this.fireEvent("beforeload", this, params) !== false){
9513             var  o = {
9514                 params : params || {},
9515                 request: {
9516                     callback : callback,
9517                     scope : scope,
9518                     arg : arg
9519                 },
9520                 reader: reader,
9521                 callback : this.loadResponse,
9522                 scope: this
9523             };
9524             if(this.useAjax){
9525                 Roo.applyIf(o, this.conn);
9526                 if(this.activeRequest){
9527                     Roo.Ajax.abort(this.activeRequest);
9528                 }
9529                 this.activeRequest = Roo.Ajax.request(o);
9530             }else{
9531                 this.conn.request(o);
9532             }
9533         }else{
9534             callback.call(scope||this, null, arg, false);
9535         }
9536     },
9537
9538     // private
9539     loadResponse : function(o, success, response){
9540         delete this.activeRequest;
9541         if(!success){
9542             this.fireEvent("loadexception", this, o, response);
9543             o.request.callback.call(o.request.scope, null, o.request.arg, false);
9544             return;
9545         }
9546         var result;
9547         try {
9548             result = o.reader.read(response);
9549         }catch(e){
9550             this.fireEvent("loadexception", this, o, response, e);
9551             o.request.callback.call(o.request.scope, null, o.request.arg, false);
9552             return;
9553         }
9554         
9555         this.fireEvent("load", this, o, o.request.arg);
9556         o.request.callback.call(o.request.scope, result, o.request.arg, true);
9557     },
9558
9559     // private
9560     update : function(dataSet){
9561
9562     },
9563
9564     // private
9565     updateResponse : function(dataSet){
9566
9567     }
9568 });/*
9569  * Based on:
9570  * Ext JS Library 1.1.1
9571  * Copyright(c) 2006-2007, Ext JS, LLC.
9572  *
9573  * Originally Released Under LGPL - original licence link has changed is not relivant.
9574  *
9575  * Fork - LGPL
9576  * <script type="text/javascript">
9577  */
9578
9579 /**
9580  * @class Roo.data.ScriptTagProxy
9581  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
9582  * other than the originating domain of the running page.<br><br>
9583  * <p>
9584  * <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
9585  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
9586  * <p>
9587  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
9588  * source code that is used as the source inside a &lt;script> tag.<br><br>
9589  * <p>
9590  * In order for the browser to process the returned data, the server must wrap the data object
9591  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
9592  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
9593  * depending on whether the callback name was passed:
9594  * <p>
9595  * <pre><code>
9596 boolean scriptTag = false;
9597 String cb = request.getParameter("callback");
9598 if (cb != null) {
9599     scriptTag = true;
9600     response.setContentType("text/javascript");
9601 } else {
9602     response.setContentType("application/x-json");
9603 }
9604 Writer out = response.getWriter();
9605 if (scriptTag) {
9606     out.write(cb + "(");
9607 }
9608 out.print(dataBlock.toJsonString());
9609 if (scriptTag) {
9610     out.write(");");
9611 }
9612 </pre></code>
9613  *
9614  * @constructor
9615  * @param {Object} config A configuration object.
9616  */
9617 Roo.data.ScriptTagProxy = function(config){
9618     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
9619     Roo.apply(this, config);
9620     this.head = document.getElementsByTagName("head")[0];
9621 };
9622
9623 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
9624
9625 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
9626     /**
9627      * @cfg {String} url The URL from which to request the data object.
9628      */
9629     /**
9630      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
9631      */
9632     timeout : 30000,
9633     /**
9634      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
9635      * the server the name of the callback function set up by the load call to process the returned data object.
9636      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
9637      * javascript output which calls this named function passing the data object as its only parameter.
9638      */
9639     callbackParam : "callback",
9640     /**
9641      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
9642      * name to the request.
9643      */
9644     nocache : true,
9645
9646     /**
9647      * Load data from the configured URL, read the data object into
9648      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9649      * process that block using the passed callback.
9650      * @param {Object} params An object containing properties which are to be used as HTTP parameters
9651      * for the request to the remote server.
9652      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9653      * object into a block of Roo.data.Records.
9654      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9655      * The function must be passed <ul>
9656      * <li>The Record block object</li>
9657      * <li>The "arg" argument from the load function</li>
9658      * <li>A boolean success indicator</li>
9659      * </ul>
9660      * @param {Object} scope The scope in which to call the callback
9661      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9662      */
9663     load : function(params, reader, callback, scope, arg){
9664         if(this.fireEvent("beforeload", this, params) !== false){
9665
9666             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
9667
9668             var url = this.url;
9669             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
9670             if(this.nocache){
9671                 url += "&_dc=" + (new Date().getTime());
9672             }
9673             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
9674             var trans = {
9675                 id : transId,
9676                 cb : "stcCallback"+transId,
9677                 scriptId : "stcScript"+transId,
9678                 params : params,
9679                 arg : arg,
9680                 url : url,
9681                 callback : callback,
9682                 scope : scope,
9683                 reader : reader
9684             };
9685             var conn = this;
9686
9687             window[trans.cb] = function(o){
9688                 conn.handleResponse(o, trans);
9689             };
9690
9691             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
9692
9693             if(this.autoAbort !== false){
9694                 this.abort();
9695             }
9696
9697             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
9698
9699             var script = document.createElement("script");
9700             script.setAttribute("src", url);
9701             script.setAttribute("type", "text/javascript");
9702             script.setAttribute("id", trans.scriptId);
9703             this.head.appendChild(script);
9704
9705             this.trans = trans;
9706         }else{
9707             callback.call(scope||this, null, arg, false);
9708         }
9709     },
9710
9711     // private
9712     isLoading : function(){
9713         return this.trans ? true : false;
9714     },
9715
9716     /**
9717      * Abort the current server request.
9718      */
9719     abort : function(){
9720         if(this.isLoading()){
9721             this.destroyTrans(this.trans);
9722         }
9723     },
9724
9725     // private
9726     destroyTrans : function(trans, isLoaded){
9727         this.head.removeChild(document.getElementById(trans.scriptId));
9728         clearTimeout(trans.timeoutId);
9729         if(isLoaded){
9730             window[trans.cb] = undefined;
9731             try{
9732                 delete window[trans.cb];
9733             }catch(e){}
9734         }else{
9735             // if hasn't been loaded, wait for load to remove it to prevent script error
9736             window[trans.cb] = function(){
9737                 window[trans.cb] = undefined;
9738                 try{
9739                     delete window[trans.cb];
9740                 }catch(e){}
9741             };
9742         }
9743     },
9744
9745     // private
9746     handleResponse : function(o, trans){
9747         this.trans = false;
9748         this.destroyTrans(trans, true);
9749         var result;
9750         try {
9751             result = trans.reader.readRecords(o);
9752         }catch(e){
9753             this.fireEvent("loadexception", this, o, trans.arg, e);
9754             trans.callback.call(trans.scope||window, null, trans.arg, false);
9755             return;
9756         }
9757         this.fireEvent("load", this, o, trans.arg);
9758         trans.callback.call(trans.scope||window, result, trans.arg, true);
9759     },
9760
9761     // private
9762     handleFailure : function(trans){
9763         this.trans = false;
9764         this.destroyTrans(trans, false);
9765         this.fireEvent("loadexception", this, null, trans.arg);
9766         trans.callback.call(trans.scope||window, null, trans.arg, false);
9767     }
9768 });/*
9769  * Based on:
9770  * Ext JS Library 1.1.1
9771  * Copyright(c) 2006-2007, Ext JS, LLC.
9772  *
9773  * Originally Released Under LGPL - original licence link has changed is not relivant.
9774  *
9775  * Fork - LGPL
9776  * <script type="text/javascript">
9777  */
9778
9779 /**
9780  * @class Roo.data.JsonReader
9781  * @extends Roo.data.DataReader
9782  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
9783  * based on mappings in a provided Roo.data.Record constructor.
9784  * 
9785  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
9786  * in the reply previously. 
9787  * 
9788  * <p>
9789  * Example code:
9790  * <pre><code>
9791 var RecordDef = Roo.data.Record.create([
9792     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
9793     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
9794 ]);
9795 var myReader = new Roo.data.JsonReader({
9796     totalProperty: "results",    // The property which contains the total dataset size (optional)
9797     root: "rows",                // The property which contains an Array of row objects
9798     id: "id"                     // The property within each row object that provides an ID for the record (optional)
9799 }, RecordDef);
9800 </code></pre>
9801  * <p>
9802  * This would consume a JSON file like this:
9803  * <pre><code>
9804 { 'results': 2, 'rows': [
9805     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
9806     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
9807 }
9808 </code></pre>
9809  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
9810  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
9811  * paged from the remote server.
9812  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
9813  * @cfg {String} root name of the property which contains the Array of row objects.
9814  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
9815  * @constructor
9816  * Create a new JsonReader
9817  * @param {Object} meta Metadata configuration options
9818  * @param {Object} recordType Either an Array of field definition objects,
9819  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
9820  */
9821 Roo.data.JsonReader = function(meta, recordType){
9822     
9823     meta = meta || {};
9824     // set some defaults:
9825     Roo.applyIf(meta, {
9826         totalProperty: 'total',
9827         successProperty : 'success',
9828         root : 'data',
9829         id : 'id'
9830     });
9831     
9832     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
9833 };
9834 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
9835     
9836     /**
9837      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
9838      * Used by Store query builder to append _requestMeta to params.
9839      * 
9840      */
9841     metaFromRemote : false,
9842     /**
9843      * This method is only used by a DataProxy which has retrieved data from a remote server.
9844      * @param {Object} response The XHR object which contains the JSON data in its responseText.
9845      * @return {Object} data A data block which is used by an Roo.data.Store object as
9846      * a cache of Roo.data.Records.
9847      */
9848     read : function(response){
9849         var json = response.responseText;
9850        
9851         var o = /* eval:var:o */ eval("("+json+")");
9852         if(!o) {
9853             throw {message: "JsonReader.read: Json object not found"};
9854         }
9855         
9856         if(o.metaData){
9857             
9858             delete this.ef;
9859             this.metaFromRemote = true;
9860             this.meta = o.metaData;
9861             this.recordType = Roo.data.Record.create(o.metaData.fields);
9862             this.onMetaChange(this.meta, this.recordType, o);
9863         }
9864         return this.readRecords(o);
9865     },
9866
9867     // private function a store will implement
9868     onMetaChange : function(meta, recordType, o){
9869
9870     },
9871
9872     /**
9873          * @ignore
9874          */
9875     simpleAccess: function(obj, subsc) {
9876         return obj[subsc];
9877     },
9878
9879         /**
9880          * @ignore
9881          */
9882     getJsonAccessor: function(){
9883         var re = /[\[\.]/;
9884         return function(expr) {
9885             try {
9886                 return(re.test(expr))
9887                     ? new Function("obj", "return obj." + expr)
9888                     : function(obj){
9889                         return obj[expr];
9890                     };
9891             } catch(e){}
9892             return Roo.emptyFn;
9893         };
9894     }(),
9895
9896     /**
9897      * Create a data block containing Roo.data.Records from an XML document.
9898      * @param {Object} o An object which contains an Array of row objects in the property specified
9899      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
9900      * which contains the total size of the dataset.
9901      * @return {Object} data A data block which is used by an Roo.data.Store object as
9902      * a cache of Roo.data.Records.
9903      */
9904     readRecords : function(o){
9905         /**
9906          * After any data loads, the raw JSON data is available for further custom processing.
9907          * @type Object
9908          */
9909         this.o = o;
9910         var s = this.meta, Record = this.recordType,
9911             f = Record.prototype.fields, fi = f.items, fl = f.length;
9912
9913 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
9914         if (!this.ef) {
9915             if(s.totalProperty) {
9916                     this.getTotal = this.getJsonAccessor(s.totalProperty);
9917                 }
9918                 if(s.successProperty) {
9919                     this.getSuccess = this.getJsonAccessor(s.successProperty);
9920                 }
9921                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
9922                 if (s.id) {
9923                         var g = this.getJsonAccessor(s.id);
9924                         this.getId = function(rec) {
9925                                 var r = g(rec);
9926                                 return (r === undefined || r === "") ? null : r;
9927                         };
9928                 } else {
9929                         this.getId = function(){return null;};
9930                 }
9931             this.ef = [];
9932             for(var jj = 0; jj < fl; jj++){
9933                 f = fi[jj];
9934                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
9935                 this.ef[jj] = this.getJsonAccessor(map);
9936             }
9937         }
9938
9939         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
9940         if(s.totalProperty){
9941             var vt = parseInt(this.getTotal(o), 10);
9942             if(!isNaN(vt)){
9943                 totalRecords = vt;
9944             }
9945         }
9946         if(s.successProperty){
9947             var vs = this.getSuccess(o);
9948             if(vs === false || vs === 'false'){
9949                 success = false;
9950             }
9951         }
9952         var records = [];
9953             for(var i = 0; i < c; i++){
9954                     var n = root[i];
9955                 var values = {};
9956                 var id = this.getId(n);
9957                 for(var j = 0; j < fl; j++){
9958                     f = fi[j];
9959                 var v = this.ef[j](n);
9960                 if (!f.convert) {
9961                     Roo.log('missing convert for ' + f.name);
9962                     Roo.log(f);
9963                     continue;
9964                 }
9965                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
9966                 }
9967                 var record = new Record(values, id);
9968                 record.json = n;
9969                 records[i] = record;
9970             }
9971             return {
9972             raw : o,
9973                 success : success,
9974                 records : records,
9975                 totalRecords : totalRecords
9976             };
9977     }
9978 });/*
9979  * Based on:
9980  * Ext JS Library 1.1.1
9981  * Copyright(c) 2006-2007, Ext JS, LLC.
9982  *
9983  * Originally Released Under LGPL - original licence link has changed is not relivant.
9984  *
9985  * Fork - LGPL
9986  * <script type="text/javascript">
9987  */
9988
9989 /**
9990  * @class Roo.data.ArrayReader
9991  * @extends Roo.data.DataReader
9992  * Data reader class to create an Array of Roo.data.Record objects from an Array.
9993  * Each element of that Array represents a row of data fields. The
9994  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
9995  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
9996  * <p>
9997  * Example code:.
9998  * <pre><code>
9999 var RecordDef = Roo.data.Record.create([
10000     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
10001     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
10002 ]);
10003 var myReader = new Roo.data.ArrayReader({
10004     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
10005 }, RecordDef);
10006 </code></pre>
10007  * <p>
10008  * This would consume an Array like this:
10009  * <pre><code>
10010 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
10011   </code></pre>
10012  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
10013  * @constructor
10014  * Create a new JsonReader
10015  * @param {Object} meta Metadata configuration options.
10016  * @param {Object} recordType Either an Array of field definition objects
10017  * as specified to {@link Roo.data.Record#create},
10018  * or an {@link Roo.data.Record} object
10019  * created using {@link Roo.data.Record#create}.
10020  */
10021 Roo.data.ArrayReader = function(meta, recordType){
10022     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
10023 };
10024
10025 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
10026     /**
10027      * Create a data block containing Roo.data.Records from an XML document.
10028      * @param {Object} o An Array of row objects which represents the dataset.
10029      * @return {Object} data A data block which is used by an Roo.data.Store object as
10030      * a cache of Roo.data.Records.
10031      */
10032     readRecords : function(o){
10033         var sid = this.meta ? this.meta.id : null;
10034         var recordType = this.recordType, fields = recordType.prototype.fields;
10035         var records = [];
10036         var root = o;
10037             for(var i = 0; i < root.length; i++){
10038                     var n = root[i];
10039                 var values = {};
10040                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
10041                 for(var j = 0, jlen = fields.length; j < jlen; j++){
10042                 var f = fields.items[j];
10043                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
10044                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
10045                 v = f.convert(v);
10046                 values[f.name] = v;
10047             }
10048                 var record = new recordType(values, id);
10049                 record.json = n;
10050                 records[records.length] = record;
10051             }
10052             return {
10053                 records : records,
10054                 totalRecords : records.length
10055             };
10056     }
10057 });/*
10058  * - LGPL
10059  * * 
10060  */
10061
10062 /**
10063  * @class Roo.bootstrap.ComboBox
10064  * @extends Roo.bootstrap.TriggerField
10065  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
10066  * @cfg {Boolean} append (true|false) default false
10067  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
10068  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
10069  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
10070  * @constructor
10071  * Create a new ComboBox.
10072  * @param {Object} config Configuration options
10073  */
10074 Roo.bootstrap.ComboBox = function(config){
10075     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
10076     this.addEvents({
10077         /**
10078          * @event expand
10079          * Fires when the dropdown list is expanded
10080              * @param {Roo.bootstrap.ComboBox} combo This combo box
10081              */
10082         'expand' : true,
10083         /**
10084          * @event collapse
10085          * Fires when the dropdown list is collapsed
10086              * @param {Roo.bootstrap.ComboBox} combo This combo box
10087              */
10088         'collapse' : true,
10089         /**
10090          * @event beforeselect
10091          * Fires before a list item is selected. Return false to cancel the selection.
10092              * @param {Roo.bootstrap.ComboBox} combo This combo box
10093              * @param {Roo.data.Record} record The data record returned from the underlying store
10094              * @param {Number} index The index of the selected item in the dropdown list
10095              */
10096         'beforeselect' : true,
10097         /**
10098          * @event select
10099          * Fires when a list item is selected
10100              * @param {Roo.bootstrap.ComboBox} combo This combo box
10101              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
10102              * @param {Number} index The index of the selected item in the dropdown list
10103              */
10104         'select' : true,
10105         /**
10106          * @event beforequery
10107          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
10108          * The event object passed has these properties:
10109              * @param {Roo.bootstrap.ComboBox} combo This combo box
10110              * @param {String} query The query
10111              * @param {Boolean} forceAll true to force "all" query
10112              * @param {Boolean} cancel true to cancel the query
10113              * @param {Object} e The query event object
10114              */
10115         'beforequery': true,
10116          /**
10117          * @event add
10118          * Fires when the 'add' icon is pressed (add a listener to enable add button)
10119              * @param {Roo.bootstrap.ComboBox} combo This combo box
10120              */
10121         'add' : true,
10122         /**
10123          * @event edit
10124          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
10125              * @param {Roo.bootstrap.ComboBox} combo This combo box
10126              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
10127              */
10128         'edit' : true,
10129         /**
10130          * @event remove
10131          * Fires when the remove value from the combobox array
10132              * @param {Roo.bootstrap.ComboBox} combo This combo box
10133              */
10134         'remove' : true
10135         
10136     });
10137     
10138     this.item = [];
10139     this.tickItems = [];
10140     
10141     this.selectedIndex = -1;
10142     if(this.mode == 'local'){
10143         if(config.queryDelay === undefined){
10144             this.queryDelay = 10;
10145         }
10146         if(config.minChars === undefined){
10147             this.minChars = 0;
10148         }
10149     }
10150 };
10151
10152 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
10153      
10154     /**
10155      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
10156      * rendering into an Roo.Editor, defaults to false)
10157      */
10158     /**
10159      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
10160      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
10161      */
10162     /**
10163      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
10164      */
10165     /**
10166      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
10167      * the dropdown list (defaults to undefined, with no header element)
10168      */
10169
10170      /**
10171      * @cfg {String/Roo.Template} tpl The template to use to render the output
10172      */
10173      
10174      /**
10175      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
10176      */
10177     listWidth: undefined,
10178     /**
10179      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
10180      * mode = 'remote' or 'text' if mode = 'local')
10181      */
10182     displayField: undefined,
10183     /**
10184      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
10185      * mode = 'remote' or 'value' if mode = 'local'). 
10186      * Note: use of a valueField requires the user make a selection
10187      * in order for a value to be mapped.
10188      */
10189     valueField: undefined,
10190     
10191     
10192     /**
10193      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
10194      * field's data value (defaults to the underlying DOM element's name)
10195      */
10196     hiddenName: undefined,
10197     /**
10198      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
10199      */
10200     listClass: '',
10201     /**
10202      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
10203      */
10204     selectedClass: 'active',
10205     
10206     /**
10207      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
10208      */
10209     shadow:'sides',
10210     /**
10211      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
10212      * anchor positions (defaults to 'tl-bl')
10213      */
10214     listAlign: 'tl-bl?',
10215     /**
10216      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
10217      */
10218     maxHeight: 300,
10219     /**
10220      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
10221      * query specified by the allQuery config option (defaults to 'query')
10222      */
10223     triggerAction: 'query',
10224     /**
10225      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
10226      * (defaults to 4, does not apply if editable = false)
10227      */
10228     minChars : 4,
10229     /**
10230      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
10231      * delay (typeAheadDelay) if it matches a known value (defaults to false)
10232      */
10233     typeAhead: false,
10234     /**
10235      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
10236      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
10237      */
10238     queryDelay: 500,
10239     /**
10240      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
10241      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
10242      */
10243     pageSize: 0,
10244     /**
10245      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
10246      * when editable = true (defaults to false)
10247      */
10248     selectOnFocus:false,
10249     /**
10250      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
10251      */
10252     queryParam: 'query',
10253     /**
10254      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
10255      * when mode = 'remote' (defaults to 'Loading...')
10256      */
10257     loadingText: 'Loading...',
10258     /**
10259      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
10260      */
10261     resizable: false,
10262     /**
10263      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
10264      */
10265     handleHeight : 8,
10266     /**
10267      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
10268      * traditional select (defaults to true)
10269      */
10270     editable: true,
10271     /**
10272      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
10273      */
10274     allQuery: '',
10275     /**
10276      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
10277      */
10278     mode: 'remote',
10279     /**
10280      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
10281      * listWidth has a higher value)
10282      */
10283     minListWidth : 70,
10284     /**
10285      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
10286      * allow the user to set arbitrary text into the field (defaults to false)
10287      */
10288     forceSelection:false,
10289     /**
10290      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
10291      * if typeAhead = true (defaults to 250)
10292      */
10293     typeAheadDelay : 250,
10294     /**
10295      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
10296      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
10297      */
10298     valueNotFoundText : undefined,
10299     /**
10300      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
10301      */
10302     blockFocus : false,
10303     
10304     /**
10305      * @cfg {Boolean} disableClear Disable showing of clear button.
10306      */
10307     disableClear : false,
10308     /**
10309      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
10310      */
10311     alwaysQuery : false,
10312     
10313     /**
10314      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
10315      */
10316     multiple : false,
10317     
10318     //private
10319     addicon : false,
10320     editicon: false,
10321     
10322     page: 0,
10323     hasQuery: false,
10324     append: false,
10325     loadNext: false,
10326     autoFocus : true,
10327     tickable : false,
10328     btnPosition : 'right',
10329     editNotList : false,
10330     // element that contains real text value.. (when hidden is used..)
10331     
10332     getAutoCreate : function()
10333     {
10334         var cfg = false;
10335         
10336         /*
10337          *  Normal ComboBox
10338          */
10339         if(!this.tickable){
10340             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
10341             return cfg;
10342         }
10343         
10344         /*
10345          *  ComboBox with tickable selections
10346          */
10347              
10348         var align = this.labelAlign || this.parentLabelAlign();
10349         
10350         cfg = {
10351             cls : 'form-group roo-combobox-tickable' //input-group
10352         };
10353         
10354         
10355         var buttons = {
10356             tag : 'div',
10357             cls : 'tickable-buttons',
10358             cn : [
10359                 {
10360                     tag : 'button',
10361                     type : 'button',
10362                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
10363                     html : 'Edit'
10364                 },
10365                 {
10366                     tag : 'button',
10367                     type : 'button',
10368                     name : 'ok',
10369                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
10370                     html : 'Done'
10371                 },
10372                 {
10373                     tag : 'button',
10374                     type : 'button',
10375                     name : 'cancel',
10376                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
10377                     html : 'Cancel'
10378                 }
10379             ]
10380         };
10381         
10382         var _this = this;
10383         Roo.each(buttons.cn, function(c){
10384             if (_this.size) {
10385                 c.cls += ' btn-' + _this.size;
10386             }
10387
10388             if (_this.disabled) {
10389                 c.disabled = true;
10390             }
10391         });
10392         
10393         var box = {
10394             tag: 'div',
10395             cn: [
10396                 {
10397                     tag: 'input',
10398                     type : 'hidden',
10399                     cls: 'form-hidden-field'
10400                 },
10401                 {
10402                     tag: 'ul',
10403                     cls: 'select2-choices',
10404                     cn:[
10405                         {
10406                             tag: 'li',
10407                             cls: 'select2-search-field',
10408                             cn: [
10409
10410                                 buttons
10411                             ]
10412                         }
10413                     ]
10414                 }
10415             ]
10416         }
10417         
10418         var combobox = {
10419             cls: 'select2-container input-group select2-container-multi',
10420             cn: [
10421                 box,
10422                 {
10423                     tag: 'ul',
10424                     cls: 'typeahead typeahead-long dropdown-menu',
10425                     style: 'display:none; max-height:' + this.maxHeight + 'px;'
10426                 }
10427             ]
10428         };
10429         
10430         if (align ==='left' && this.fieldLabel.length) {
10431             
10432                 Roo.log("left and has label");
10433                 cfg.cn = [
10434                     
10435                     {
10436                         tag: 'label',
10437                         'for' :  id,
10438                         cls : 'control-label col-sm-' + this.labelWidth,
10439                         html : this.fieldLabel
10440                         
10441                     },
10442                     {
10443                         cls : "col-sm-" + (12 - this.labelWidth), 
10444                         cn: [
10445                             combobox
10446                         ]
10447                     }
10448                     
10449                 ];
10450         } else if ( this.fieldLabel.length) {
10451                 Roo.log(" label");
10452                  cfg.cn = [
10453                    
10454                     {
10455                         tag: 'label',
10456                         //cls : 'input-group-addon',
10457                         html : this.fieldLabel
10458                         
10459                     },
10460                     
10461                     combobox
10462                     
10463                 ];
10464
10465         } else {
10466             
10467                 Roo.log(" no label && no align");
10468                 cfg = combobox
10469                      
10470                 
10471         }
10472          
10473         var settings=this;
10474         ['xs','sm','md','lg'].map(function(size){
10475             if (settings[size]) {
10476                 cfg.cls += ' col-' + size + '-' + settings[size];
10477             }
10478         });
10479         
10480         return cfg;
10481         
10482     },
10483     
10484     // private
10485     initEvents: function()
10486     {
10487         
10488         if (!this.store) {
10489             throw "can not find store for combo";
10490         }
10491         this.store = Roo.factory(this.store, Roo.data);
10492         
10493         if(this.tickable){
10494             this.initTickableEvnets();
10495             return;
10496         }
10497         
10498         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
10499         
10500         
10501         if(this.hiddenName){
10502             
10503             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10504             
10505             this.hiddenField.dom.value =
10506                 this.hiddenValue !== undefined ? this.hiddenValue :
10507                 this.value !== undefined ? this.value : '';
10508
10509             // prevent input submission
10510             this.el.dom.removeAttribute('name');
10511             this.hiddenField.dom.setAttribute('name', this.hiddenName);
10512              
10513              
10514         }
10515         //if(Roo.isGecko){
10516         //    this.el.dom.setAttribute('autocomplete', 'off');
10517         //}
10518
10519         var cls = 'x-combo-list';
10520         this.list = this.el.select('ul.dropdown-menu',true).first();
10521
10522         //this.list = new Roo.Layer({
10523         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
10524         //});
10525         
10526         var _this = this;
10527         
10528         (function(){
10529             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
10530             _this.list.setWidth(lw);
10531         }).defer(100);
10532         
10533         this.list.on('mouseover', this.onViewOver, this);
10534         this.list.on('mousemove', this.onViewMove, this);
10535         
10536         this.list.on('scroll', this.onViewScroll, this);
10537         
10538         /*
10539         this.list.swallowEvent('mousewheel');
10540         this.assetHeight = 0;
10541
10542         if(this.title){
10543             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
10544             this.assetHeight += this.header.getHeight();
10545         }
10546
10547         this.innerList = this.list.createChild({cls:cls+'-inner'});
10548         this.innerList.on('mouseover', this.onViewOver, this);
10549         this.innerList.on('mousemove', this.onViewMove, this);
10550         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
10551         
10552         if(this.allowBlank && !this.pageSize && !this.disableClear){
10553             this.footer = this.list.createChild({cls:cls+'-ft'});
10554             this.pageTb = new Roo.Toolbar(this.footer);
10555            
10556         }
10557         if(this.pageSize){
10558             this.footer = this.list.createChild({cls:cls+'-ft'});
10559             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
10560                     {pageSize: this.pageSize});
10561             
10562         }
10563         
10564         if (this.pageTb && this.allowBlank && !this.disableClear) {
10565             var _this = this;
10566             this.pageTb.add(new Roo.Toolbar.Fill(), {
10567                 cls: 'x-btn-icon x-btn-clear',
10568                 text: '&#160;',
10569                 handler: function()
10570                 {
10571                     _this.collapse();
10572                     _this.clearValue();
10573                     _this.onSelect(false, -1);
10574                 }
10575             });
10576         }
10577         if (this.footer) {
10578             this.assetHeight += this.footer.getHeight();
10579         }
10580         */
10581             
10582         if(!this.tpl){
10583             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
10584         }
10585
10586         this.view = new Roo.View(this.el.select('ul.dropdown-menu',true).first(), this.tpl, {
10587             singleSelect:true, store: this.store, selectedClass: this.selectedClass
10588         });
10589         //this.view.wrapEl.setDisplayed(false);
10590         this.view.on('click', this.onViewClick, this);
10591         
10592         
10593         
10594         this.store.on('beforeload', this.onBeforeLoad, this);
10595         this.store.on('load', this.onLoad, this);
10596         this.store.on('loadexception', this.onLoadException, this);
10597         /*
10598         if(this.resizable){
10599             this.resizer = new Roo.Resizable(this.list,  {
10600                pinned:true, handles:'se'
10601             });
10602             this.resizer.on('resize', function(r, w, h){
10603                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
10604                 this.listWidth = w;
10605                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
10606                 this.restrictHeight();
10607             }, this);
10608             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
10609         }
10610         */
10611         if(!this.editable){
10612             this.editable = true;
10613             this.setEditable(false);
10614         }
10615         
10616         /*
10617         
10618         if (typeof(this.events.add.listeners) != 'undefined') {
10619             
10620             this.addicon = this.wrap.createChild(
10621                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
10622        
10623             this.addicon.on('click', function(e) {
10624                 this.fireEvent('add', this);
10625             }, this);
10626         }
10627         if (typeof(this.events.edit.listeners) != 'undefined') {
10628             
10629             this.editicon = this.wrap.createChild(
10630                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
10631             if (this.addicon) {
10632                 this.editicon.setStyle('margin-left', '40px');
10633             }
10634             this.editicon.on('click', function(e) {
10635                 
10636                 // we fire even  if inothing is selected..
10637                 this.fireEvent('edit', this, this.lastData );
10638                 
10639             }, this);
10640         }
10641         */
10642         
10643         this.keyNav = new Roo.KeyNav(this.inputEl(), {
10644             "up" : function(e){
10645                 this.inKeyMode = true;
10646                 this.selectPrev();
10647             },
10648
10649             "down" : function(e){
10650                 if(!this.isExpanded()){
10651                     this.onTriggerClick();
10652                 }else{
10653                     this.inKeyMode = true;
10654                     this.selectNext();
10655                 }
10656             },
10657
10658             "enter" : function(e){
10659 //                this.onViewClick();
10660                 //return true;
10661                 this.collapse();
10662                 
10663                 if(this.fireEvent("specialkey", this, e)){
10664                     this.onViewClick(false);
10665                 }
10666                 
10667                 return true;
10668             },
10669
10670             "esc" : function(e){
10671                 this.collapse();
10672             },
10673
10674             "tab" : function(e){
10675                 this.collapse();
10676                 
10677                 if(this.fireEvent("specialkey", this, e)){
10678                     this.onViewClick(false);
10679                 }
10680                 
10681                 return true;
10682             },
10683
10684             scope : this,
10685
10686             doRelay : function(foo, bar, hname){
10687                 if(hname == 'down' || this.scope.isExpanded()){
10688                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
10689                 }
10690                 return true;
10691             },
10692
10693             forceKeyDown: true
10694         });
10695         
10696         
10697         this.queryDelay = Math.max(this.queryDelay || 10,
10698                 this.mode == 'local' ? 10 : 250);
10699         
10700         
10701         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
10702         
10703         if(this.typeAhead){
10704             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
10705         }
10706         if(this.editable !== false){
10707             this.inputEl().on("keyup", this.onKeyUp, this);
10708         }
10709         if(this.forceSelection){
10710             this.inputEl().on('blur', this.doForce, this);
10711         }
10712         
10713         if(this.multiple){
10714             this.choices = this.el.select('ul.select2-choices', true).first();
10715             this.searchField = this.el.select('ul li.select2-search-field', true).first();
10716         }
10717     },
10718     
10719     initTickableEvnets: function()
10720     {   
10721         if(this.hiddenName){
10722             
10723             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10724             
10725             this.hiddenField.dom.value =
10726                 this.hiddenValue !== undefined ? this.hiddenValue :
10727                 this.value !== undefined ? this.value : '';
10728
10729             // prevent input submission
10730             this.el.dom.removeAttribute('name');
10731             this.hiddenField.dom.setAttribute('name', this.hiddenName);
10732              
10733              
10734         }
10735         
10736         this.list = this.el.select('ul.dropdown-menu',true).first();
10737         
10738         this.choices = this.el.select('ul.select2-choices', true).first();
10739         this.searchField = this.el.select('ul li.select2-search-field', true).first();
10740         if(this.editNotList){
10741             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
10742         }
10743          
10744         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
10745         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
10746         
10747         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
10748         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
10749         
10750         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
10751         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
10752         
10753         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
10754         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
10755         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
10756         
10757         this.okBtn.hide();
10758         this.cancelBtn.hide();
10759         
10760         var _this = this;
10761         
10762         (function(){
10763             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
10764             _this.list.setWidth(lw);
10765         }).defer(100);
10766         
10767         this.list.on('mouseover', this.onViewOver, this);
10768         this.list.on('mousemove', this.onViewMove, this);
10769         
10770         this.list.on('scroll', this.onViewScroll, this);
10771         
10772         if(!this.tpl){
10773             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>';
10774         }
10775
10776         this.view = new Roo.View(this.el.select('ul.dropdown-menu',true).first(), this.tpl, {
10777             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
10778         });
10779         
10780         //this.view.wrapEl.setDisplayed(false);
10781         this.view.on('click', this.onViewClick, this);
10782         
10783         
10784         
10785         this.store.on('beforeload', this.onBeforeLoad, this);
10786         this.store.on('load', this.onLoad, this);
10787         this.store.on('loadexception', this.onLoadException, this);
10788         
10789 //        this.keyNav = new Roo.KeyNav(this.inputEl(), {
10790 //            "up" : function(e){
10791 //                this.inKeyMode = true;
10792 //                this.selectPrev();
10793 //            },
10794 //
10795 //            "down" : function(e){
10796 //                if(!this.isExpanded()){
10797 //                    this.onTriggerClick();
10798 //                }else{
10799 //                    this.inKeyMode = true;
10800 //                    this.selectNext();
10801 //                }
10802 //            },
10803 //
10804 //            "enter" : function(e){
10805 ////                this.onViewClick();
10806 //                //return true;
10807 //                this.collapse();
10808 //                
10809 //                if(this.fireEvent("specialkey", this, e)){
10810 //                    this.onViewClick(false);
10811 //                }
10812 //                
10813 //                return true;
10814 //            },
10815 //
10816 //            "esc" : function(e){
10817 //                this.collapse();
10818 //            },
10819 //
10820 //            "tab" : function(e){
10821 //                this.collapse();
10822 //                
10823 //                if(this.fireEvent("specialkey", this, e)){
10824 //                    this.onViewClick(false);
10825 //                }
10826 //                
10827 //                return true;
10828 //            },
10829 //
10830 //            scope : this,
10831 //
10832 //            doRelay : function(foo, bar, hname){
10833 //                if(hname == 'down' || this.scope.isExpanded()){
10834 //                   return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
10835 //                }
10836 //                return true;
10837 //            },
10838 //
10839 //            forceKeyDown: true
10840 //        });
10841         
10842         
10843         this.queryDelay = Math.max(this.queryDelay || 10,
10844                 this.mode == 'local' ? 10 : 250);
10845         
10846         
10847         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
10848         
10849         if(this.typeAhead){
10850             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
10851         }
10852     },
10853
10854     onDestroy : function(){
10855         if(this.view){
10856             this.view.setStore(null);
10857             this.view.el.removeAllListeners();
10858             this.view.el.remove();
10859             this.view.purgeListeners();
10860         }
10861         if(this.list){
10862             this.list.dom.innerHTML  = '';
10863         }
10864         
10865         if(this.store){
10866             this.store.un('beforeload', this.onBeforeLoad, this);
10867             this.store.un('load', this.onLoad, this);
10868             this.store.un('loadexception', this.onLoadException, this);
10869         }
10870         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
10871     },
10872
10873     // private
10874     fireKey : function(e){
10875         if(e.isNavKeyPress() && !this.list.isVisible()){
10876             this.fireEvent("specialkey", this, e);
10877         }
10878     },
10879
10880     // private
10881     onResize: function(w, h){
10882 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
10883 //        
10884 //        if(typeof w != 'number'){
10885 //            // we do not handle it!?!?
10886 //            return;
10887 //        }
10888 //        var tw = this.trigger.getWidth();
10889 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
10890 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
10891 //        var x = w - tw;
10892 //        this.inputEl().setWidth( this.adjustWidth('input', x));
10893 //            
10894 //        //this.trigger.setStyle('left', x+'px');
10895 //        
10896 //        if(this.list && this.listWidth === undefined){
10897 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
10898 //            this.list.setWidth(lw);
10899 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
10900 //        }
10901         
10902     
10903         
10904     },
10905
10906     /**
10907      * Allow or prevent the user from directly editing the field text.  If false is passed,
10908      * the user will only be able to select from the items defined in the dropdown list.  This method
10909      * is the runtime equivalent of setting the 'editable' config option at config time.
10910      * @param {Boolean} value True to allow the user to directly edit the field text
10911      */
10912     setEditable : function(value){
10913         if(value == this.editable){
10914             return;
10915         }
10916         this.editable = value;
10917         if(!value){
10918             this.inputEl().dom.setAttribute('readOnly', true);
10919             this.inputEl().on('mousedown', this.onTriggerClick,  this);
10920             this.inputEl().addClass('x-combo-noedit');
10921         }else{
10922             this.inputEl().dom.setAttribute('readOnly', false);
10923             this.inputEl().un('mousedown', this.onTriggerClick,  this);
10924             this.inputEl().removeClass('x-combo-noedit');
10925         }
10926     },
10927
10928     // private
10929     
10930     onBeforeLoad : function(combo,opts){
10931         if(!this.hasFocus){
10932             return;
10933         }
10934          if (!opts.add) {
10935             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
10936          }
10937         this.restrictHeight();
10938         this.selectedIndex = -1;
10939     },
10940
10941     // private
10942     onLoad : function(){
10943         
10944         this.hasQuery = false;
10945         
10946         if(!this.hasFocus){
10947             return;
10948         }
10949         
10950         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
10951             this.loading.hide();
10952         }
10953         
10954         if(this.store.getCount() > 0){
10955             this.expand();
10956             this.restrictHeight();
10957             if(this.lastQuery == this.allQuery){
10958                 if(this.editable && !this.tickable){
10959                     this.inputEl().dom.select();
10960                 }
10961                 if(!this.selectByValue(this.value, true) && this.autoFocus){
10962                     this.select(0, true);
10963                 }
10964             }else{
10965                 if(this.autoFocus){
10966                     this.selectNext();
10967                 }
10968                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
10969                     this.taTask.delay(this.typeAheadDelay);
10970                 }
10971             }
10972         }else{
10973             this.onEmptyResults();
10974         }
10975         
10976         //this.el.focus();
10977     },
10978     // private
10979     onLoadException : function()
10980     {
10981         this.hasQuery = false;
10982         
10983         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
10984             this.loading.hide();
10985         }
10986         
10987         this.collapse();
10988         Roo.log(this.store.reader.jsonData);
10989         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
10990             // fixme
10991             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
10992         }
10993         
10994         
10995     },
10996     // private
10997     onTypeAhead : function(){
10998         if(this.store.getCount() > 0){
10999             var r = this.store.getAt(0);
11000             var newValue = r.data[this.displayField];
11001             var len = newValue.length;
11002             var selStart = this.getRawValue().length;
11003             
11004             if(selStart != len){
11005                 this.setRawValue(newValue);
11006                 this.selectText(selStart, newValue.length);
11007             }
11008         }
11009     },
11010
11011     // private
11012     onSelect : function(record, index){
11013         
11014         if(this.fireEvent('beforeselect', this, record, index) !== false){
11015         
11016             this.setFromData(index > -1 ? record.data : false);
11017             
11018             this.collapse();
11019             this.fireEvent('select', this, record, index);
11020         }
11021     },
11022
11023     /**
11024      * Returns the currently selected field value or empty string if no value is set.
11025      * @return {String} value The selected value
11026      */
11027     getValue : function(){
11028         
11029         if(this.multiple){
11030             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
11031         }
11032         
11033         if(this.valueField){
11034             return typeof this.value != 'undefined' ? this.value : '';
11035         }else{
11036             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
11037         }
11038     },
11039
11040     /**
11041      * Clears any text/value currently set in the field
11042      */
11043     clearValue : function(){
11044         if(this.hiddenField){
11045             this.hiddenField.dom.value = '';
11046         }
11047         this.value = '';
11048         this.setRawValue('');
11049         this.lastSelectionText = '';
11050         
11051     },
11052
11053     /**
11054      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
11055      * will be displayed in the field.  If the value does not match the data value of an existing item,
11056      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
11057      * Otherwise the field will be blank (although the value will still be set).
11058      * @param {String} value The value to match
11059      */
11060     setValue : function(v){
11061         if(this.multiple){
11062             this.syncValue();
11063             return;
11064         }
11065         
11066         var text = v;
11067         if(this.valueField){
11068             var r = this.findRecord(this.valueField, v);
11069             if(r){
11070                 text = r.data[this.displayField];
11071             }else if(this.valueNotFoundText !== undefined){
11072                 text = this.valueNotFoundText;
11073             }
11074         }
11075         this.lastSelectionText = text;
11076         if(this.hiddenField){
11077             this.hiddenField.dom.value = v;
11078         }
11079         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
11080         this.value = v;
11081     },
11082     /**
11083      * @property {Object} the last set data for the element
11084      */
11085     
11086     lastData : false,
11087     /**
11088      * Sets the value of the field based on a object which is related to the record format for the store.
11089      * @param {Object} value the value to set as. or false on reset?
11090      */
11091     setFromData : function(o){
11092         
11093         if(this.multiple){
11094             this.addItem(o);
11095             return;
11096         }
11097             
11098         var dv = ''; // display value
11099         var vv = ''; // value value..
11100         this.lastData = o;
11101         if (this.displayField) {
11102             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11103         } else {
11104             // this is an error condition!!!
11105             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
11106         }
11107         
11108         if(this.valueField){
11109             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
11110         }
11111         
11112         if(this.hiddenField){
11113             this.hiddenField.dom.value = vv;
11114             
11115             this.lastSelectionText = dv;
11116             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11117             this.value = vv;
11118             return;
11119         }
11120         // no hidden field.. - we store the value in 'value', but still display
11121         // display field!!!!
11122         this.lastSelectionText = dv;
11123         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11124         this.value = vv;
11125         
11126         
11127     },
11128     // private
11129     reset : function(){
11130         // overridden so that last data is reset..
11131         this.setValue(this.originalValue);
11132         this.clearInvalid();
11133         this.lastData = false;
11134         if (this.view) {
11135             this.view.clearSelections();
11136         }
11137     },
11138     // private
11139     findRecord : function(prop, value){
11140         var record;
11141         if(this.store.getCount() > 0){
11142             this.store.each(function(r){
11143                 if(r.data[prop] == value){
11144                     record = r;
11145                     return false;
11146                 }
11147                 return true;
11148             });
11149         }
11150         return record;
11151     },
11152     
11153     getName: function()
11154     {
11155         // returns hidden if it's set..
11156         if (!this.rendered) {return ''};
11157         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
11158         
11159     },
11160     // private
11161     onViewMove : function(e, t){
11162         this.inKeyMode = false;
11163     },
11164
11165     // private
11166     onViewOver : function(e, t){
11167         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
11168             return;
11169         }
11170         var item = this.view.findItemFromChild(t);
11171         
11172         if(item){
11173             var index = this.view.indexOf(item);
11174             this.select(index, false);
11175         }
11176     },
11177
11178     // private
11179     onViewClick : function(view, doFocus, el, e)
11180     {
11181         var index = this.view.getSelectedIndexes()[0];
11182         
11183         var r = this.store.getAt(index);
11184         
11185         if(this.tickable){
11186             
11187             if(e.getTarget().nodeName.toLowerCase() != 'input'){
11188                 return;
11189             }
11190             
11191             var rm = false;
11192             var _this = this;
11193             
11194             Roo.each(this.tickItems, function(v,k){
11195                 
11196                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
11197                     _this.tickItems.splice(k, 1);
11198                     rm = true;
11199                     return;
11200                 }
11201             })
11202             
11203             if(rm){
11204                 return;
11205             }
11206             
11207             this.tickItems.push(r.data);
11208             return;
11209         }
11210         
11211         if(r){
11212             this.onSelect(r, index);
11213         }
11214         if(doFocus !== false && !this.blockFocus){
11215             this.inputEl().focus();
11216         }
11217     },
11218
11219     // private
11220     restrictHeight : function(){
11221         //this.innerList.dom.style.height = '';
11222         //var inner = this.innerList.dom;
11223         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
11224         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
11225         //this.list.beginUpdate();
11226         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
11227         this.list.alignTo(this.inputEl(), this.listAlign);
11228         //this.list.endUpdate();
11229     },
11230
11231     // private
11232     onEmptyResults : function(){
11233         this.collapse();
11234     },
11235
11236     /**
11237      * Returns true if the dropdown list is expanded, else false.
11238      */
11239     isExpanded : function(){
11240         return this.list.isVisible();
11241     },
11242
11243     /**
11244      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
11245      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11246      * @param {String} value The data value of the item to select
11247      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11248      * selected item if it is not currently in view (defaults to true)
11249      * @return {Boolean} True if the value matched an item in the list, else false
11250      */
11251     selectByValue : function(v, scrollIntoView){
11252         if(v !== undefined && v !== null){
11253             var r = this.findRecord(this.valueField || this.displayField, v);
11254             if(r){
11255                 this.select(this.store.indexOf(r), scrollIntoView);
11256                 return true;
11257             }
11258         }
11259         return false;
11260     },
11261
11262     /**
11263      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
11264      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11265      * @param {Number} index The zero-based index of the list item to select
11266      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11267      * selected item if it is not currently in view (defaults to true)
11268      */
11269     select : function(index, scrollIntoView){
11270         this.selectedIndex = index;
11271         this.view.select(index);
11272         if(scrollIntoView !== false){
11273             var el = this.view.getNode(index);
11274             if(el){
11275                 //this.innerList.scrollChildIntoView(el, false);
11276                 
11277             }
11278         }
11279     },
11280
11281     // private
11282     selectNext : function(){
11283         var ct = this.store.getCount();
11284         if(ct > 0){
11285             if(this.selectedIndex == -1){
11286                 this.select(0);
11287             }else if(this.selectedIndex < ct-1){
11288                 this.select(this.selectedIndex+1);
11289             }
11290         }
11291     },
11292
11293     // private
11294     selectPrev : function(){
11295         var ct = this.store.getCount();
11296         if(ct > 0){
11297             if(this.selectedIndex == -1){
11298                 this.select(0);
11299             }else if(this.selectedIndex != 0){
11300                 this.select(this.selectedIndex-1);
11301             }
11302         }
11303     },
11304
11305     // private
11306     onKeyUp : function(e){
11307         if(this.editable !== false && !e.isSpecialKey()){
11308             this.lastKey = e.getKey();
11309             this.dqTask.delay(this.queryDelay);
11310         }
11311     },
11312
11313     // private
11314     validateBlur : function(){
11315         return !this.list || !this.list.isVisible();   
11316     },
11317
11318     // private
11319     initQuery : function(){
11320         this.doQuery(this.getRawValue());
11321     },
11322
11323     // private
11324     doForce : function(){
11325         if(this.inputEl().dom.value.length > 0){
11326             this.inputEl().dom.value =
11327                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
11328              
11329         }
11330     },
11331
11332     /**
11333      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
11334      * query allowing the query action to be canceled if needed.
11335      * @param {String} query The SQL query to execute
11336      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
11337      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
11338      * saved in the current store (defaults to false)
11339      */
11340     doQuery : function(q, forceAll){
11341         
11342         if(q === undefined || q === null){
11343             q = '';
11344         }
11345         var qe = {
11346             query: q,
11347             forceAll: forceAll,
11348             combo: this,
11349             cancel:false
11350         };
11351         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
11352             return false;
11353         }
11354         q = qe.query;
11355         
11356         forceAll = qe.forceAll;
11357         if(forceAll === true || (q.length >= this.minChars)){
11358             
11359             this.hasQuery = true;
11360             
11361             if(this.lastQuery != q || this.alwaysQuery){
11362                 this.lastQuery = q;
11363                 if(this.mode == 'local'){
11364                     this.selectedIndex = -1;
11365                     if(forceAll){
11366                         this.store.clearFilter();
11367                     }else{
11368                         this.store.filter(this.displayField, q);
11369                     }
11370                     this.onLoad();
11371                 }else{
11372                     this.store.baseParams[this.queryParam] = q;
11373                     
11374                     var options = {params : this.getParams(q)};
11375                     
11376                     if(this.loadNext){
11377                         options.add = true;
11378                         options.params.start = this.page * this.pageSize;
11379                     }
11380                     
11381                     this.store.load(options);
11382                     /*
11383                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
11384                      *  we should expand the list on onLoad
11385                      *  so command out it
11386                      */
11387 //                    this.expand();
11388                 }
11389             }else{
11390                 this.selectedIndex = -1;
11391                 this.onLoad();   
11392             }
11393         }
11394         
11395         this.loadNext = false;
11396     },
11397
11398     // private
11399     getParams : function(q){
11400         var p = {};
11401         //p[this.queryParam] = q;
11402         
11403         if(this.pageSize){
11404             p.start = 0;
11405             p.limit = this.pageSize;
11406         }
11407         return p;
11408     },
11409
11410     /**
11411      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
11412      */
11413     collapse : function(){
11414         if(!this.isExpanded()){
11415             return;
11416         }
11417         
11418         this.hasFocus = false;
11419         
11420         this.list.hide();
11421         
11422         if(this.tickable){
11423             this.okBtn.hide();
11424             this.cancelBtn.hide();
11425             this.trigger.show();
11426         }
11427         
11428         Roo.get(document).un('mousedown', this.collapseIf, this);
11429         Roo.get(document).un('mousewheel', this.collapseIf, this);
11430         if (!this.editable) {
11431             Roo.get(document).un('keydown', this.listKeyPress, this);
11432         }
11433         this.fireEvent('collapse', this);
11434     },
11435
11436     // private
11437     collapseIf : function(e){
11438         var in_combo  = e.within(this.el);
11439         var in_list =  e.within(this.list);
11440         
11441         if (in_combo || in_list) {
11442             //e.stopPropagation();
11443             return;
11444         }
11445         
11446         if(this.tickable){
11447             this.onTickableFooterButtonClick(e, false, false);
11448         }
11449
11450         this.collapse();
11451         
11452     },
11453
11454     /**
11455      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
11456      */
11457     expand : function(){
11458        
11459         if(this.isExpanded() || !this.hasFocus){
11460             return;
11461         }
11462          Roo.log('expand');
11463         this.list.alignTo(this.inputEl(), this.listAlign);
11464         this.list.show();
11465         
11466         if(this.tickable){
11467             
11468             this.tickItems = Roo.apply([], this.item);
11469             
11470             this.okBtn.show();
11471             this.cancelBtn.show();
11472             this.trigger.hide();
11473             
11474         }
11475         
11476         Roo.get(document).on('mousedown', this.collapseIf, this);
11477         Roo.get(document).on('mousewheel', this.collapseIf, this);
11478         if (!this.editable) {
11479             Roo.get(document).on('keydown', this.listKeyPress, this);
11480         }
11481         
11482         this.fireEvent('expand', this);
11483     },
11484
11485     // private
11486     // Implements the default empty TriggerField.onTriggerClick function
11487     onTriggerClick : function(e)
11488     {
11489         Roo.log('trigger click');
11490         
11491         if(this.disabled){
11492             return;
11493         }
11494         
11495         this.page = 0;
11496         this.loadNext = false;
11497         
11498         if(this.isExpanded()){
11499             this.collapse();
11500             if (!this.blockFocus) {
11501                 this.inputEl().focus();
11502             }
11503             
11504         }else {
11505             this.hasFocus = true;
11506             if(this.triggerAction == 'all') {
11507                 this.doQuery(this.allQuery, true);
11508             } else {
11509                 this.doQuery(this.getRawValue());
11510             }
11511             if (!this.blockFocus) {
11512                 this.inputEl().focus();
11513             }
11514         }
11515     },
11516     
11517     onTickableTriggerClick : function(e)
11518     {
11519         if(this.disabled){
11520             return;
11521         }
11522         
11523         this.page = 0;
11524         this.loadNext = false;
11525         this.hasFocus = true;
11526         
11527         if(this.triggerAction == 'all') {
11528             this.doQuery(this.allQuery, true);
11529         } else {
11530             this.doQuery(this.getRawValue());
11531         }
11532     },
11533     
11534     onSearchFieldClick : function(e)
11535     {
11536         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
11537             return;
11538         }
11539         
11540         this.page = 0;
11541         this.loadNext = false;
11542         this.hasFocus = true;
11543         
11544         if(this.triggerAction == 'all') {
11545             this.doQuery(this.allQuery, true);
11546         } else {
11547             this.doQuery(this.getRawValue());
11548         }
11549     },
11550     
11551     listKeyPress : function(e)
11552     {
11553         //Roo.log('listkeypress');
11554         // scroll to first matching element based on key pres..
11555         if (e.isSpecialKey()) {
11556             return false;
11557         }
11558         var k = String.fromCharCode(e.getKey()).toUpperCase();
11559         //Roo.log(k);
11560         var match  = false;
11561         var csel = this.view.getSelectedNodes();
11562         var cselitem = false;
11563         if (csel.length) {
11564             var ix = this.view.indexOf(csel[0]);
11565             cselitem  = this.store.getAt(ix);
11566             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
11567                 cselitem = false;
11568             }
11569             
11570         }
11571         
11572         this.store.each(function(v) { 
11573             if (cselitem) {
11574                 // start at existing selection.
11575                 if (cselitem.id == v.id) {
11576                     cselitem = false;
11577                 }
11578                 return true;
11579             }
11580                 
11581             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
11582                 match = this.store.indexOf(v);
11583                 return false;
11584             }
11585             return true;
11586         }, this);
11587         
11588         if (match === false) {
11589             return true; // no more action?
11590         }
11591         // scroll to?
11592         this.view.select(match);
11593         var sn = Roo.get(this.view.getSelectedNodes()[0])
11594         //sn.scrollIntoView(sn.dom.parentNode, false);
11595     },
11596     
11597     onViewScroll : function(e, t){
11598         
11599         if(this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
11600             return;
11601         }
11602         
11603         this.hasQuery = true;
11604         
11605         this.loading = this.list.select('.loading', true).first();
11606         
11607         if(this.loading === null){
11608             this.list.createChild({
11609                 tag: 'div',
11610                 cls: 'loading select2-more-results select2-active',
11611                 html: 'Loading more results...'
11612             })
11613             
11614             this.loading = this.list.select('.loading', true).first();
11615             
11616             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
11617             
11618             this.loading.hide();
11619         }
11620         
11621         this.loading.show();
11622         
11623         var _combo = this;
11624         
11625         this.page++;
11626         this.loadNext = true;
11627         
11628         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
11629         
11630         return;
11631     },
11632     
11633     addItem : function(o)
11634     {   
11635         var dv = ''; // display value
11636         
11637         if (this.displayField) {
11638             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11639         } else {
11640             // this is an error condition!!!
11641             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
11642         }
11643         
11644         if(!dv.length){
11645             return;
11646         }
11647         
11648         var choice = this.choices.createChild({
11649             tag: 'li',
11650             cls: 'select2-search-choice',
11651             cn: [
11652                 {
11653                     tag: 'div',
11654                     html: dv
11655                 },
11656                 {
11657                     tag: 'a',
11658                     href: '#',
11659                     cls: 'select2-search-choice-close',
11660                     tabindex: '-1'
11661                 }
11662             ]
11663             
11664         }, this.searchField);
11665         
11666         var close = choice.select('a.select2-search-choice-close', true).first()
11667         
11668         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
11669         
11670         this.item.push(o);
11671         
11672         this.lastData = o;
11673         
11674         this.syncValue();
11675         
11676         this.inputEl().dom.value = '';
11677         
11678     },
11679     
11680     onRemoveItem : function(e, _self, o)
11681     {
11682         e.preventDefault();
11683         var index = this.item.indexOf(o.data) * 1;
11684         
11685         if( index < 0){
11686             Roo.log('not this item?!');
11687             return;
11688         }
11689         
11690         this.item.splice(index, 1);
11691         o.item.remove();
11692         
11693         this.syncValue();
11694         
11695         this.fireEvent('remove', this, e);
11696         
11697     },
11698     
11699     syncValue : function()
11700     {
11701         if(!this.item.length){
11702             this.clearValue();
11703             return;
11704         }
11705             
11706         var value = [];
11707         var _this = this;
11708         Roo.each(this.item, function(i){
11709             if(_this.valueField){
11710                 value.push(i[_this.valueField]);
11711                 return;
11712             }
11713
11714             value.push(i);
11715         });
11716
11717         this.value = value.join(',');
11718
11719         if(this.hiddenField){
11720             this.hiddenField.dom.value = this.value;
11721         }
11722     },
11723     
11724     clearItem : function()
11725     {
11726         if(!this.multiple){
11727             return;
11728         }
11729         
11730         this.item = [];
11731         
11732         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
11733            c.remove();
11734         });
11735         
11736         this.syncValue();
11737     },
11738     
11739     inputEl: function ()
11740     {
11741         if(this.tickable){
11742             return this.searchField;
11743         }
11744         return this.el.select('input.form-control',true).first();
11745     },
11746     
11747     
11748     onTickableFooterButtonClick : function(e, btn, el)
11749     {
11750         e.preventDefault();
11751         
11752         if(btn && btn.name == 'cancel'){
11753             this.tickItems = Roo.apply([], this.item);
11754             this.collapse();
11755             return;
11756         }
11757         
11758         this.clearItem();
11759         
11760         var _this = this;
11761         
11762         Roo.each(this.tickItems, function(o){
11763             _this.addItem(o);
11764         });
11765         
11766         this.collapse();
11767         
11768     }
11769     
11770     
11771
11772     /** 
11773     * @cfg {Boolean} grow 
11774     * @hide 
11775     */
11776     /** 
11777     * @cfg {Number} growMin 
11778     * @hide 
11779     */
11780     /** 
11781     * @cfg {Number} growMax 
11782     * @hide 
11783     */
11784     /**
11785      * @hide
11786      * @method autoSize
11787      */
11788 });
11789 /*
11790  * Based on:
11791  * Ext JS Library 1.1.1
11792  * Copyright(c) 2006-2007, Ext JS, LLC.
11793  *
11794  * Originally Released Under LGPL - original licence link has changed is not relivant.
11795  *
11796  * Fork - LGPL
11797  * <script type="text/javascript">
11798  */
11799
11800 /**
11801  * @class Roo.View
11802  * @extends Roo.util.Observable
11803  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
11804  * This class also supports single and multi selection modes. <br>
11805  * Create a data model bound view:
11806  <pre><code>
11807  var store = new Roo.data.Store(...);
11808
11809  var view = new Roo.View({
11810     el : "my-element",
11811     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
11812  
11813     singleSelect: true,
11814     selectedClass: "ydataview-selected",
11815     store: store
11816  });
11817
11818  // listen for node click?
11819  view.on("click", function(vw, index, node, e){
11820  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
11821  });
11822
11823  // load XML data
11824  dataModel.load("foobar.xml");
11825  </code></pre>
11826  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
11827  * <br><br>
11828  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
11829  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
11830  * 
11831  * Note: old style constructor is still suported (container, template, config)
11832  * 
11833  * @constructor
11834  * Create a new View
11835  * @param {Object} config The config object
11836  * 
11837  */
11838 Roo.View = function(config, depreciated_tpl, depreciated_config){
11839     
11840     this.parent = false;
11841     
11842     if (typeof(depreciated_tpl) == 'undefined') {
11843         // new way.. - universal constructor.
11844         Roo.apply(this, config);
11845         this.el  = Roo.get(this.el);
11846     } else {
11847         // old format..
11848         this.el  = Roo.get(config);
11849         this.tpl = depreciated_tpl;
11850         Roo.apply(this, depreciated_config);
11851     }
11852     this.wrapEl  = this.el.wrap().wrap();
11853     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
11854     
11855     
11856     if(typeof(this.tpl) == "string"){
11857         this.tpl = new Roo.Template(this.tpl);
11858     } else {
11859         // support xtype ctors..
11860         this.tpl = new Roo.factory(this.tpl, Roo);
11861     }
11862     
11863     
11864     this.tpl.compile();
11865     
11866     /** @private */
11867     this.addEvents({
11868         /**
11869          * @event beforeclick
11870          * Fires before a click is processed. Returns false to cancel the default action.
11871          * @param {Roo.View} this
11872          * @param {Number} index The index of the target node
11873          * @param {HTMLElement} node The target node
11874          * @param {Roo.EventObject} e The raw event object
11875          */
11876             "beforeclick" : true,
11877         /**
11878          * @event click
11879          * Fires when a template node is clicked.
11880          * @param {Roo.View} this
11881          * @param {Number} index The index of the target node
11882          * @param {HTMLElement} node The target node
11883          * @param {Roo.EventObject} e The raw event object
11884          */
11885             "click" : true,
11886         /**
11887          * @event dblclick
11888          * Fires when a template node is double clicked.
11889          * @param {Roo.View} this
11890          * @param {Number} index The index of the target node
11891          * @param {HTMLElement} node The target node
11892          * @param {Roo.EventObject} e The raw event object
11893          */
11894             "dblclick" : true,
11895         /**
11896          * @event contextmenu
11897          * Fires when a template node is right clicked.
11898          * @param {Roo.View} this
11899          * @param {Number} index The index of the target node
11900          * @param {HTMLElement} node The target node
11901          * @param {Roo.EventObject} e The raw event object
11902          */
11903             "contextmenu" : true,
11904         /**
11905          * @event selectionchange
11906          * Fires when the selected nodes change.
11907          * @param {Roo.View} this
11908          * @param {Array} selections Array of the selected nodes
11909          */
11910             "selectionchange" : true,
11911     
11912         /**
11913          * @event beforeselect
11914          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
11915          * @param {Roo.View} this
11916          * @param {HTMLElement} node The node to be selected
11917          * @param {Array} selections Array of currently selected nodes
11918          */
11919             "beforeselect" : true,
11920         /**
11921          * @event preparedata
11922          * Fires on every row to render, to allow you to change the data.
11923          * @param {Roo.View} this
11924          * @param {Object} data to be rendered (change this)
11925          */
11926           "preparedata" : true
11927           
11928           
11929         });
11930
11931
11932
11933     this.el.on({
11934         "click": this.onClick,
11935         "dblclick": this.onDblClick,
11936         "contextmenu": this.onContextMenu,
11937         scope:this
11938     });
11939
11940     this.selections = [];
11941     this.nodes = [];
11942     this.cmp = new Roo.CompositeElementLite([]);
11943     if(this.store){
11944         this.store = Roo.factory(this.store, Roo.data);
11945         this.setStore(this.store, true);
11946     }
11947     
11948     if ( this.footer && this.footer.xtype) {
11949            
11950          var fctr = this.wrapEl.appendChild(document.createElement("div"));
11951         
11952         this.footer.dataSource = this.store
11953         this.footer.container = fctr;
11954         this.footer = Roo.factory(this.footer, Roo);
11955         fctr.insertFirst(this.el);
11956         
11957         // this is a bit insane - as the paging toolbar seems to detach the el..
11958 //        dom.parentNode.parentNode.parentNode
11959          // they get detached?
11960     }
11961     
11962     
11963     Roo.View.superclass.constructor.call(this);
11964     
11965     
11966 };
11967
11968 Roo.extend(Roo.View, Roo.util.Observable, {
11969     
11970      /**
11971      * @cfg {Roo.data.Store} store Data store to load data from.
11972      */
11973     store : false,
11974     
11975     /**
11976      * @cfg {String|Roo.Element} el The container element.
11977      */
11978     el : '',
11979     
11980     /**
11981      * @cfg {String|Roo.Template} tpl The template used by this View 
11982      */
11983     tpl : false,
11984     /**
11985      * @cfg {String} dataName the named area of the template to use as the data area
11986      *                          Works with domtemplates roo-name="name"
11987      */
11988     dataName: false,
11989     /**
11990      * @cfg {String} selectedClass The css class to add to selected nodes
11991      */
11992     selectedClass : "x-view-selected",
11993      /**
11994      * @cfg {String} emptyText The empty text to show when nothing is loaded.
11995      */
11996     emptyText : "",
11997     
11998     /**
11999      * @cfg {String} text to display on mask (default Loading)
12000      */
12001     mask : false,
12002     /**
12003      * @cfg {Boolean} multiSelect Allow multiple selection
12004      */
12005     multiSelect : false,
12006     /**
12007      * @cfg {Boolean} singleSelect Allow single selection
12008      */
12009     singleSelect:  false,
12010     
12011     /**
12012      * @cfg {Boolean} toggleSelect - selecting 
12013      */
12014     toggleSelect : false,
12015     
12016     /**
12017      * @cfg {Boolean} tickable - selecting 
12018      */
12019     tickable : false,
12020     
12021     /**
12022      * Returns the element this view is bound to.
12023      * @return {Roo.Element}
12024      */
12025     getEl : function(){
12026         return this.wrapEl;
12027     },
12028     
12029     
12030
12031     /**
12032      * Refreshes the view. - called by datachanged on the store. - do not call directly.
12033      */
12034     refresh : function(){
12035         Roo.log('refresh');
12036         var t = this.tpl;
12037         
12038         // if we are using something like 'domtemplate', then
12039         // the what gets used is:
12040         // t.applySubtemplate(NAME, data, wrapping data..)
12041         // the outer template then get' applied with
12042         //     the store 'extra data'
12043         // and the body get's added to the
12044         //      roo-name="data" node?
12045         //      <span class='roo-tpl-{name}'></span> ?????
12046         
12047         
12048         
12049         this.clearSelections();
12050         this.el.update("");
12051         var html = [];
12052         var records = this.store.getRange();
12053         if(records.length < 1) {
12054             
12055             // is this valid??  = should it render a template??
12056             
12057             this.el.update(this.emptyText);
12058             return;
12059         }
12060         var el = this.el;
12061         if (this.dataName) {
12062             this.el.update(t.apply(this.store.meta)); //????
12063             el = this.el.child('.roo-tpl-' + this.dataName);
12064         }
12065         
12066         for(var i = 0, len = records.length; i < len; i++){
12067             var data = this.prepareData(records[i].data, i, records[i]);
12068             this.fireEvent("preparedata", this, data, i, records[i]);
12069             
12070             var d = Roo.apply({}, data);
12071             
12072             if(this.tickable){
12073                 Roo.apply(d, {'roo-id' : Roo.id()});
12074                 
12075                 var _this = this;
12076             
12077                 Roo.each(this.parent.item, function(item){
12078                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
12079                         return;
12080                     }
12081                     Roo.apply(d, {'roo-data-checked' : 'checked'});
12082                 });
12083             }
12084             
12085             html[html.length] = Roo.util.Format.trim(
12086                 this.dataName ?
12087                     t.applySubtemplate(this.dataName, d, this.store.meta) :
12088                     t.apply(d)
12089             );
12090         }
12091         
12092         
12093         
12094         el.update(html.join(""));
12095         this.nodes = el.dom.childNodes;
12096         this.updateIndexes(0);
12097     },
12098     
12099
12100     /**
12101      * Function to override to reformat the data that is sent to
12102      * the template for each node.
12103      * DEPRICATED - use the preparedata event handler.
12104      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
12105      * a JSON object for an UpdateManager bound view).
12106      */
12107     prepareData : function(data, index, record)
12108     {
12109         this.fireEvent("preparedata", this, data, index, record);
12110         return data;
12111     },
12112
12113     onUpdate : function(ds, record){
12114          Roo.log('on update');   
12115         this.clearSelections();
12116         var index = this.store.indexOf(record);
12117         var n = this.nodes[index];
12118         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
12119         n.parentNode.removeChild(n);
12120         this.updateIndexes(index, index);
12121     },
12122
12123     
12124     
12125 // --------- FIXME     
12126     onAdd : function(ds, records, index)
12127     {
12128         Roo.log(['on Add', ds, records, index] );        
12129         this.clearSelections();
12130         if(this.nodes.length == 0){
12131             this.refresh();
12132             return;
12133         }
12134         var n = this.nodes[index];
12135         for(var i = 0, len = records.length; i < len; i++){
12136             var d = this.prepareData(records[i].data, i, records[i]);
12137             if(n){
12138                 this.tpl.insertBefore(n, d);
12139             }else{
12140                 
12141                 this.tpl.append(this.el, d);
12142             }
12143         }
12144         this.updateIndexes(index);
12145     },
12146
12147     onRemove : function(ds, record, index){
12148         Roo.log('onRemove');
12149         this.clearSelections();
12150         var el = this.dataName  ?
12151             this.el.child('.roo-tpl-' + this.dataName) :
12152             this.el; 
12153         
12154         el.dom.removeChild(this.nodes[index]);
12155         this.updateIndexes(index);
12156     },
12157
12158     /**
12159      * Refresh an individual node.
12160      * @param {Number} index
12161      */
12162     refreshNode : function(index){
12163         this.onUpdate(this.store, this.store.getAt(index));
12164     },
12165
12166     updateIndexes : function(startIndex, endIndex){
12167         var ns = this.nodes;
12168         startIndex = startIndex || 0;
12169         endIndex = endIndex || ns.length - 1;
12170         for(var i = startIndex; i <= endIndex; i++){
12171             ns[i].nodeIndex = i;
12172         }
12173     },
12174
12175     /**
12176      * Changes the data store this view uses and refresh the view.
12177      * @param {Store} store
12178      */
12179     setStore : function(store, initial){
12180         if(!initial && this.store){
12181             this.store.un("datachanged", this.refresh);
12182             this.store.un("add", this.onAdd);
12183             this.store.un("remove", this.onRemove);
12184             this.store.un("update", this.onUpdate);
12185             this.store.un("clear", this.refresh);
12186             this.store.un("beforeload", this.onBeforeLoad);
12187             this.store.un("load", this.onLoad);
12188             this.store.un("loadexception", this.onLoad);
12189         }
12190         if(store){
12191           
12192             store.on("datachanged", this.refresh, this);
12193             store.on("add", this.onAdd, this);
12194             store.on("remove", this.onRemove, this);
12195             store.on("update", this.onUpdate, this);
12196             store.on("clear", this.refresh, this);
12197             store.on("beforeload", this.onBeforeLoad, this);
12198             store.on("load", this.onLoad, this);
12199             store.on("loadexception", this.onLoad, this);
12200         }
12201         
12202         if(store){
12203             this.refresh();
12204         }
12205     },
12206     /**
12207      * onbeforeLoad - masks the loading area.
12208      *
12209      */
12210     onBeforeLoad : function(store,opts)
12211     {
12212          Roo.log('onBeforeLoad');   
12213         if (!opts.add) {
12214             this.el.update("");
12215         }
12216         this.el.mask(this.mask ? this.mask : "Loading" ); 
12217     },
12218     onLoad : function ()
12219     {
12220         this.el.unmask();
12221     },
12222     
12223
12224     /**
12225      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
12226      * @param {HTMLElement} node
12227      * @return {HTMLElement} The template node
12228      */
12229     findItemFromChild : function(node){
12230         var el = this.dataName  ?
12231             this.el.child('.roo-tpl-' + this.dataName,true) :
12232             this.el.dom; 
12233         
12234         if(!node || node.parentNode == el){
12235                     return node;
12236             }
12237             var p = node.parentNode;
12238             while(p && p != el){
12239             if(p.parentNode == el){
12240                 return p;
12241             }
12242             p = p.parentNode;
12243         }
12244             return null;
12245     },
12246
12247     /** @ignore */
12248     onClick : function(e){
12249         var item = this.findItemFromChild(e.getTarget());
12250         if(item){
12251             var index = this.indexOf(item);
12252             if(this.onItemClick(item, index, e) !== false){
12253                 this.fireEvent("click", this, index, item, e);
12254             }
12255         }else{
12256             this.clearSelections();
12257         }
12258     },
12259
12260     /** @ignore */
12261     onContextMenu : function(e){
12262         var item = this.findItemFromChild(e.getTarget());
12263         if(item){
12264             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
12265         }
12266     },
12267
12268     /** @ignore */
12269     onDblClick : function(e){
12270         var item = this.findItemFromChild(e.getTarget());
12271         if(item){
12272             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
12273         }
12274     },
12275
12276     onItemClick : function(item, index, e)
12277     {
12278         if(this.fireEvent("beforeclick", this, index, item, e) === false){
12279             return false;
12280         }
12281         if (this.toggleSelect) {
12282             var m = this.isSelected(item) ? 'unselect' : 'select';
12283             Roo.log(m);
12284             var _t = this;
12285             _t[m](item, true, false);
12286             return true;
12287         }
12288         if(this.multiSelect || this.singleSelect){
12289             if(this.multiSelect && e.shiftKey && this.lastSelection){
12290                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
12291             }else{
12292                 this.select(item, this.multiSelect && e.ctrlKey);
12293                 this.lastSelection = item;
12294             }
12295             
12296             if(!this.tickable){
12297                 e.preventDefault();
12298             }
12299             
12300         }
12301         return true;
12302     },
12303
12304     /**
12305      * Get the number of selected nodes.
12306      * @return {Number}
12307      */
12308     getSelectionCount : function(){
12309         return this.selections.length;
12310     },
12311
12312     /**
12313      * Get the currently selected nodes.
12314      * @return {Array} An array of HTMLElements
12315      */
12316     getSelectedNodes : function(){
12317         return this.selections;
12318     },
12319
12320     /**
12321      * Get the indexes of the selected nodes.
12322      * @return {Array}
12323      */
12324     getSelectedIndexes : function(){
12325         var indexes = [], s = this.selections;
12326         for(var i = 0, len = s.length; i < len; i++){
12327             indexes.push(s[i].nodeIndex);
12328         }
12329         return indexes;
12330     },
12331
12332     /**
12333      * Clear all selections
12334      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
12335      */
12336     clearSelections : function(suppressEvent){
12337         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
12338             this.cmp.elements = this.selections;
12339             this.cmp.removeClass(this.selectedClass);
12340             this.selections = [];
12341             if(!suppressEvent){
12342                 this.fireEvent("selectionchange", this, this.selections);
12343             }
12344         }
12345     },
12346
12347     /**
12348      * Returns true if the passed node is selected
12349      * @param {HTMLElement/Number} node The node or node index
12350      * @return {Boolean}
12351      */
12352     isSelected : function(node){
12353         var s = this.selections;
12354         if(s.length < 1){
12355             return false;
12356         }
12357         node = this.getNode(node);
12358         return s.indexOf(node) !== -1;
12359     },
12360
12361     /**
12362      * Selects nodes.
12363      * @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
12364      * @param {Boolean} keepExisting (optional) true to keep existing selections
12365      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12366      */
12367     select : function(nodeInfo, keepExisting, suppressEvent){
12368         if(nodeInfo instanceof Array){
12369             if(!keepExisting){
12370                 this.clearSelections(true);
12371             }
12372             for(var i = 0, len = nodeInfo.length; i < len; i++){
12373                 this.select(nodeInfo[i], true, true);
12374             }
12375             return;
12376         } 
12377         var node = this.getNode(nodeInfo);
12378         if(!node || this.isSelected(node)){
12379             return; // already selected.
12380         }
12381         if(!keepExisting){
12382             this.clearSelections(true);
12383         }
12384         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
12385             Roo.fly(node).addClass(this.selectedClass);
12386             this.selections.push(node);
12387             if(!suppressEvent){
12388                 this.fireEvent("selectionchange", this, this.selections);
12389             }
12390         }
12391         
12392         
12393     },
12394       /**
12395      * Unselects nodes.
12396      * @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
12397      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
12398      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12399      */
12400     unselect : function(nodeInfo, keepExisting, suppressEvent)
12401     {
12402         if(nodeInfo instanceof Array){
12403             Roo.each(this.selections, function(s) {
12404                 this.unselect(s, nodeInfo);
12405             }, this);
12406             return;
12407         }
12408         var node = this.getNode(nodeInfo);
12409         if(!node || !this.isSelected(node)){
12410             Roo.log("not selected");
12411             return; // not selected.
12412         }
12413         // fireevent???
12414         var ns = [];
12415         Roo.each(this.selections, function(s) {
12416             if (s == node ) {
12417                 Roo.fly(node).removeClass(this.selectedClass);
12418
12419                 return;
12420             }
12421             ns.push(s);
12422         },this);
12423         
12424         this.selections= ns;
12425         this.fireEvent("selectionchange", this, this.selections);
12426     },
12427
12428     /**
12429      * Gets a template node.
12430      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12431      * @return {HTMLElement} The node or null if it wasn't found
12432      */
12433     getNode : function(nodeInfo){
12434         if(typeof nodeInfo == "string"){
12435             return document.getElementById(nodeInfo);
12436         }else if(typeof nodeInfo == "number"){
12437             return this.nodes[nodeInfo];
12438         }
12439         return nodeInfo;
12440     },
12441
12442     /**
12443      * Gets a range template nodes.
12444      * @param {Number} startIndex
12445      * @param {Number} endIndex
12446      * @return {Array} An array of nodes
12447      */
12448     getNodes : function(start, end){
12449         var ns = this.nodes;
12450         start = start || 0;
12451         end = typeof end == "undefined" ? ns.length - 1 : end;
12452         var nodes = [];
12453         if(start <= end){
12454             for(var i = start; i <= end; i++){
12455                 nodes.push(ns[i]);
12456             }
12457         } else{
12458             for(var i = start; i >= end; i--){
12459                 nodes.push(ns[i]);
12460             }
12461         }
12462         return nodes;
12463     },
12464
12465     /**
12466      * Finds the index of the passed node
12467      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12468      * @return {Number} The index of the node or -1
12469      */
12470     indexOf : function(node){
12471         node = this.getNode(node);
12472         if(typeof node.nodeIndex == "number"){
12473             return node.nodeIndex;
12474         }
12475         var ns = this.nodes;
12476         for(var i = 0, len = ns.length; i < len; i++){
12477             if(ns[i] == node){
12478                 return i;
12479             }
12480         }
12481         return -1;
12482     }
12483 });
12484 /*
12485  * - LGPL
12486  *
12487  * based on jquery fullcalendar
12488  * 
12489  */
12490
12491 Roo.bootstrap = Roo.bootstrap || {};
12492 /**
12493  * @class Roo.bootstrap.Calendar
12494  * @extends Roo.bootstrap.Component
12495  * Bootstrap Calendar class
12496  * @cfg {Boolean} loadMask (true|false) default false
12497  * @cfg {Object} header generate the user specific header of the calendar, default false
12498
12499  * @constructor
12500  * Create a new Container
12501  * @param {Object} config The config object
12502  */
12503
12504
12505
12506 Roo.bootstrap.Calendar = function(config){
12507     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
12508      this.addEvents({
12509         /**
12510              * @event select
12511              * Fires when a date is selected
12512              * @param {DatePicker} this
12513              * @param {Date} date The selected date
12514              */
12515         'select': true,
12516         /**
12517              * @event monthchange
12518              * Fires when the displayed month changes 
12519              * @param {DatePicker} this
12520              * @param {Date} date The selected month
12521              */
12522         'monthchange': true,
12523         /**
12524              * @event evententer
12525              * Fires when mouse over an event
12526              * @param {Calendar} this
12527              * @param {event} Event
12528              */
12529         'evententer': true,
12530         /**
12531              * @event eventleave
12532              * Fires when the mouse leaves an
12533              * @param {Calendar} this
12534              * @param {event}
12535              */
12536         'eventleave': true,
12537         /**
12538              * @event eventclick
12539              * Fires when the mouse click an
12540              * @param {Calendar} this
12541              * @param {event}
12542              */
12543         'eventclick': true
12544         
12545     });
12546
12547 };
12548
12549 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
12550     
12551      /**
12552      * @cfg {Number} startDay
12553      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
12554      */
12555     startDay : 0,
12556     
12557     loadMask : false,
12558     
12559     header : false,
12560       
12561     getAutoCreate : function(){
12562         
12563         
12564         var fc_button = function(name, corner, style, content ) {
12565             return Roo.apply({},{
12566                 tag : 'span',
12567                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
12568                          (corner.length ?
12569                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
12570                             ''
12571                         ),
12572                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
12573                 unselectable: 'on'
12574             });
12575         };
12576         
12577         var header = {};
12578         
12579         if(!this.header){
12580             header = {
12581                 tag : 'table',
12582                 cls : 'fc-header',
12583                 style : 'width:100%',
12584                 cn : [
12585                     {
12586                         tag: 'tr',
12587                         cn : [
12588                             {
12589                                 tag : 'td',
12590                                 cls : 'fc-header-left',
12591                                 cn : [
12592                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
12593                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
12594                                     { tag: 'span', cls: 'fc-header-space' },
12595                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
12596
12597
12598                                 ]
12599                             },
12600
12601                             {
12602                                 tag : 'td',
12603                                 cls : 'fc-header-center',
12604                                 cn : [
12605                                     {
12606                                         tag: 'span',
12607                                         cls: 'fc-header-title',
12608                                         cn : {
12609                                             tag: 'H2',
12610                                             html : 'month / year'
12611                                         }
12612                                     }
12613
12614                                 ]
12615                             },
12616                             {
12617                                 tag : 'td',
12618                                 cls : 'fc-header-right',
12619                                 cn : [
12620                               /*      fc_button('month', 'left', '', 'month' ),
12621                                     fc_button('week', '', '', 'week' ),
12622                                     fc_button('day', 'right', '', 'day' )
12623                                 */    
12624
12625                                 ]
12626                             }
12627
12628                         ]
12629                     }
12630                 ]
12631             };
12632         }
12633         
12634         header = this.header;
12635         
12636        
12637         var cal_heads = function() {
12638             var ret = [];
12639             // fixme - handle this.
12640             
12641             for (var i =0; i < Date.dayNames.length; i++) {
12642                 var d = Date.dayNames[i];
12643                 ret.push({
12644                     tag: 'th',
12645                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
12646                     html : d.substring(0,3)
12647                 });
12648                 
12649             }
12650             ret[0].cls += ' fc-first';
12651             ret[6].cls += ' fc-last';
12652             return ret;
12653         };
12654         var cal_cell = function(n) {
12655             return  {
12656                 tag: 'td',
12657                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
12658                 cn : [
12659                     {
12660                         cn : [
12661                             {
12662                                 cls: 'fc-day-number',
12663                                 html: 'D'
12664                             },
12665                             {
12666                                 cls: 'fc-day-content',
12667                              
12668                                 cn : [
12669                                      {
12670                                         style: 'position: relative;' // height: 17px;
12671                                     }
12672                                 ]
12673                             }
12674                             
12675                             
12676                         ]
12677                     }
12678                 ]
12679                 
12680             }
12681         };
12682         var cal_rows = function() {
12683             
12684             var ret = []
12685             for (var r = 0; r < 6; r++) {
12686                 var row= {
12687                     tag : 'tr',
12688                     cls : 'fc-week',
12689                     cn : []
12690                 };
12691                 
12692                 for (var i =0; i < Date.dayNames.length; i++) {
12693                     var d = Date.dayNames[i];
12694                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
12695
12696                 }
12697                 row.cn[0].cls+=' fc-first';
12698                 row.cn[0].cn[0].style = 'min-height:90px';
12699                 row.cn[6].cls+=' fc-last';
12700                 ret.push(row);
12701                 
12702             }
12703             ret[0].cls += ' fc-first';
12704             ret[4].cls += ' fc-prev-last';
12705             ret[5].cls += ' fc-last';
12706             return ret;
12707             
12708         };
12709         
12710         var cal_table = {
12711             tag: 'table',
12712             cls: 'fc-border-separate',
12713             style : 'width:100%',
12714             cellspacing  : 0,
12715             cn : [
12716                 { 
12717                     tag: 'thead',
12718                     cn : [
12719                         { 
12720                             tag: 'tr',
12721                             cls : 'fc-first fc-last',
12722                             cn : cal_heads()
12723                         }
12724                     ]
12725                 },
12726                 { 
12727                     tag: 'tbody',
12728                     cn : cal_rows()
12729                 }
12730                   
12731             ]
12732         };
12733          
12734          var cfg = {
12735             cls : 'fc fc-ltr',
12736             cn : [
12737                 header,
12738                 {
12739                     cls : 'fc-content',
12740                     style : "position: relative;",
12741                     cn : [
12742                         {
12743                             cls : 'fc-view fc-view-month fc-grid',
12744                             style : 'position: relative',
12745                             unselectable : 'on',
12746                             cn : [
12747                                 {
12748                                     cls : 'fc-event-container',
12749                                     style : 'position:absolute;z-index:8;top:0;left:0;'
12750                                 },
12751                                 cal_table
12752                             ]
12753                         }
12754                     ]
12755     
12756                 }
12757            ] 
12758             
12759         };
12760         
12761          
12762         
12763         return cfg;
12764     },
12765     
12766     
12767     initEvents : function()
12768     {
12769         if(!this.store){
12770             throw "can not find store for calendar";
12771         }
12772         
12773         var mark = {
12774             tag: "div",
12775             cls:"x-dlg-mask",
12776             style: "text-align:center",
12777             cn: [
12778                 {
12779                     tag: "div",
12780                     style: "background-color:white;width:50%;margin:250 auto",
12781                     cn: [
12782                         {
12783                             tag: "img",
12784                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
12785                         },
12786                         {
12787                             tag: "span",
12788                             html: "Loading"
12789                         }
12790                         
12791                     ]
12792                 }
12793             ]
12794         }
12795         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
12796         
12797         var size = this.el.select('.fc-content', true).first().getSize();
12798         this.maskEl.setSize(size.width, size.height);
12799         this.maskEl.enableDisplayMode("block");
12800         if(!this.loadMask){
12801             this.maskEl.hide();
12802         }
12803         
12804         this.store = Roo.factory(this.store, Roo.data);
12805         this.store.on('load', this.onLoad, this);
12806         this.store.on('beforeload', this.onBeforeLoad, this);
12807         
12808         this.resize();
12809         
12810         this.cells = this.el.select('.fc-day',true);
12811         //Roo.log(this.cells);
12812         this.textNodes = this.el.query('.fc-day-number');
12813         this.cells.addClassOnOver('fc-state-hover');
12814         
12815         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
12816         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
12817         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
12818         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
12819         
12820         this.on('monthchange', this.onMonthChange, this);
12821         
12822         this.update(new Date().clearTime());
12823     },
12824     
12825     resize : function() {
12826         var sz  = this.el.getSize();
12827         
12828         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
12829         this.el.select('.fc-day-content div',true).setHeight(34);
12830     },
12831     
12832     
12833     // private
12834     showPrevMonth : function(e){
12835         this.update(this.activeDate.add("mo", -1));
12836     },
12837     showToday : function(e){
12838         this.update(new Date().clearTime());
12839     },
12840     // private
12841     showNextMonth : function(e){
12842         this.update(this.activeDate.add("mo", 1));
12843     },
12844
12845     // private
12846     showPrevYear : function(){
12847         this.update(this.activeDate.add("y", -1));
12848     },
12849
12850     // private
12851     showNextYear : function(){
12852         this.update(this.activeDate.add("y", 1));
12853     },
12854
12855     
12856    // private
12857     update : function(date)
12858     {
12859         var vd = this.activeDate;
12860         this.activeDate = date;
12861 //        if(vd && this.el){
12862 //            var t = date.getTime();
12863 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
12864 //                Roo.log('using add remove');
12865 //                
12866 //                this.fireEvent('monthchange', this, date);
12867 //                
12868 //                this.cells.removeClass("fc-state-highlight");
12869 //                this.cells.each(function(c){
12870 //                   if(c.dateValue == t){
12871 //                       c.addClass("fc-state-highlight");
12872 //                       setTimeout(function(){
12873 //                            try{c.dom.firstChild.focus();}catch(e){}
12874 //                       }, 50);
12875 //                       return false;
12876 //                   }
12877 //                   return true;
12878 //                });
12879 //                return;
12880 //            }
12881 //        }
12882         
12883         var days = date.getDaysInMonth();
12884         
12885         var firstOfMonth = date.getFirstDateOfMonth();
12886         var startingPos = firstOfMonth.getDay()-this.startDay;
12887         
12888         if(startingPos < this.startDay){
12889             startingPos += 7;
12890         }
12891         
12892         var pm = date.add(Date.MONTH, -1);
12893         var prevStart = pm.getDaysInMonth()-startingPos;
12894 //        
12895         this.cells = this.el.select('.fc-day',true);
12896         this.textNodes = this.el.query('.fc-day-number');
12897         this.cells.addClassOnOver('fc-state-hover');
12898         
12899         var cells = this.cells.elements;
12900         var textEls = this.textNodes;
12901         
12902         Roo.each(cells, function(cell){
12903             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
12904         });
12905         
12906         days += startingPos;
12907
12908         // convert everything to numbers so it's fast
12909         var day = 86400000;
12910         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
12911         //Roo.log(d);
12912         //Roo.log(pm);
12913         //Roo.log(prevStart);
12914         
12915         var today = new Date().clearTime().getTime();
12916         var sel = date.clearTime().getTime();
12917         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
12918         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
12919         var ddMatch = this.disabledDatesRE;
12920         var ddText = this.disabledDatesText;
12921         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
12922         var ddaysText = this.disabledDaysText;
12923         var format = this.format;
12924         
12925         var setCellClass = function(cal, cell){
12926             cell.row = 0;
12927             cell.events = [];
12928             cell.more = [];
12929             //Roo.log('set Cell Class');
12930             cell.title = "";
12931             var t = d.getTime();
12932             
12933             //Roo.log(d);
12934             
12935             cell.dateValue = t;
12936             if(t == today){
12937                 cell.className += " fc-today";
12938                 cell.className += " fc-state-highlight";
12939                 cell.title = cal.todayText;
12940             }
12941             if(t == sel){
12942                 // disable highlight in other month..
12943                 //cell.className += " fc-state-highlight";
12944                 
12945             }
12946             // disabling
12947             if(t < min) {
12948                 cell.className = " fc-state-disabled";
12949                 cell.title = cal.minText;
12950                 return;
12951             }
12952             if(t > max) {
12953                 cell.className = " fc-state-disabled";
12954                 cell.title = cal.maxText;
12955                 return;
12956             }
12957             if(ddays){
12958                 if(ddays.indexOf(d.getDay()) != -1){
12959                     cell.title = ddaysText;
12960                     cell.className = " fc-state-disabled";
12961                 }
12962             }
12963             if(ddMatch && format){
12964                 var fvalue = d.dateFormat(format);
12965                 if(ddMatch.test(fvalue)){
12966                     cell.title = ddText.replace("%0", fvalue);
12967                     cell.className = " fc-state-disabled";
12968                 }
12969             }
12970             
12971             if (!cell.initialClassName) {
12972                 cell.initialClassName = cell.dom.className;
12973             }
12974             
12975             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
12976         };
12977
12978         var i = 0;
12979         
12980         for(; i < startingPos; i++) {
12981             textEls[i].innerHTML = (++prevStart);
12982             d.setDate(d.getDate()+1);
12983             
12984             cells[i].className = "fc-past fc-other-month";
12985             setCellClass(this, cells[i]);
12986         }
12987         
12988         var intDay = 0;
12989         
12990         for(; i < days; i++){
12991             intDay = i - startingPos + 1;
12992             textEls[i].innerHTML = (intDay);
12993             d.setDate(d.getDate()+1);
12994             
12995             cells[i].className = ''; // "x-date-active";
12996             setCellClass(this, cells[i]);
12997         }
12998         var extraDays = 0;
12999         
13000         for(; i < 42; i++) {
13001             textEls[i].innerHTML = (++extraDays);
13002             d.setDate(d.getDate()+1);
13003             
13004             cells[i].className = "fc-future fc-other-month";
13005             setCellClass(this, cells[i]);
13006         }
13007         
13008         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
13009         
13010         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
13011         
13012         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
13013         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
13014         
13015         if(totalRows != 6){
13016             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
13017             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
13018         }
13019         
13020         this.fireEvent('monthchange', this, date);
13021         
13022         
13023         /*
13024         if(!this.internalRender){
13025             var main = this.el.dom.firstChild;
13026             var w = main.offsetWidth;
13027             this.el.setWidth(w + this.el.getBorderWidth("lr"));
13028             Roo.fly(main).setWidth(w);
13029             this.internalRender = true;
13030             // opera does not respect the auto grow header center column
13031             // then, after it gets a width opera refuses to recalculate
13032             // without a second pass
13033             if(Roo.isOpera && !this.secondPass){
13034                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
13035                 this.secondPass = true;
13036                 this.update.defer(10, this, [date]);
13037             }
13038         }
13039         */
13040         
13041     },
13042     
13043     findCell : function(dt) {
13044         dt = dt.clearTime().getTime();
13045         var ret = false;
13046         this.cells.each(function(c){
13047             //Roo.log("check " +c.dateValue + '?=' + dt);
13048             if(c.dateValue == dt){
13049                 ret = c;
13050                 return false;
13051             }
13052             return true;
13053         });
13054         
13055         return ret;
13056     },
13057     
13058     findCells : function(ev) {
13059         var s = ev.start.clone().clearTime().getTime();
13060        // Roo.log(s);
13061         var e= ev.end.clone().clearTime().getTime();
13062        // Roo.log(e);
13063         var ret = [];
13064         this.cells.each(function(c){
13065              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
13066             
13067             if(c.dateValue > e){
13068                 return ;
13069             }
13070             if(c.dateValue < s){
13071                 return ;
13072             }
13073             ret.push(c);
13074         });
13075         
13076         return ret;    
13077     },
13078     
13079 //    findBestRow: function(cells)
13080 //    {
13081 //        var ret = 0;
13082 //        
13083 //        for (var i =0 ; i < cells.length;i++) {
13084 //            ret  = Math.max(cells[i].rows || 0,ret);
13085 //        }
13086 //        return ret;
13087 //        
13088 //    },
13089     
13090     
13091     addItem : function(ev)
13092     {
13093         // look for vertical location slot in
13094         var cells = this.findCells(ev);
13095         
13096 //        ev.row = this.findBestRow(cells);
13097         
13098         // work out the location.
13099         
13100         var crow = false;
13101         var rows = [];
13102         for(var i =0; i < cells.length; i++) {
13103             
13104             cells[i].row = cells[0].row;
13105             
13106             if(i == 0){
13107                 cells[i].row = cells[i].row + 1;
13108             }
13109             
13110             if (!crow) {
13111                 crow = {
13112                     start : cells[i],
13113                     end :  cells[i]
13114                 };
13115                 continue;
13116             }
13117             if (crow.start.getY() == cells[i].getY()) {
13118                 // on same row.
13119                 crow.end = cells[i];
13120                 continue;
13121             }
13122             // different row.
13123             rows.push(crow);
13124             crow = {
13125                 start: cells[i],
13126                 end : cells[i]
13127             };
13128             
13129         }
13130         
13131         rows.push(crow);
13132         ev.els = [];
13133         ev.rows = rows;
13134         ev.cells = cells;
13135         
13136         cells[0].events.push(ev);
13137         
13138         this.calevents.push(ev);
13139     },
13140     
13141     clearEvents: function() {
13142         
13143         if(!this.calevents){
13144             return;
13145         }
13146         
13147         Roo.each(this.cells.elements, function(c){
13148             c.row = 0;
13149             c.events = [];
13150             c.more = [];
13151         });
13152         
13153         Roo.each(this.calevents, function(e) {
13154             Roo.each(e.els, function(el) {
13155                 el.un('mouseenter' ,this.onEventEnter, this);
13156                 el.un('mouseleave' ,this.onEventLeave, this);
13157                 el.remove();
13158             },this);
13159         },this);
13160         
13161         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
13162             e.remove();
13163         });
13164         
13165     },
13166     
13167     renderEvents: function()
13168     {   
13169         var _this = this;
13170         
13171         this.cells.each(function(c) {
13172             
13173             if(c.row < 5){
13174                 return;
13175             }
13176             
13177             var ev = c.events;
13178             
13179             var r = 4;
13180             if(c.row != c.events.length){
13181                 r = 4 - (4 - (c.row - c.events.length));
13182             }
13183             
13184             c.events = ev.slice(0, r);
13185             c.more = ev.slice(r);
13186             
13187             if(c.more.length && c.more.length == 1){
13188                 c.events.push(c.more.pop());
13189             }
13190             
13191             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
13192             
13193         });
13194             
13195         this.cells.each(function(c) {
13196             
13197             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
13198             
13199             
13200             for (var e = 0; e < c.events.length; e++){
13201                 var ev = c.events[e];
13202                 var rows = ev.rows;
13203                 
13204                 for(var i = 0; i < rows.length; i++) {
13205                 
13206                     // how many rows should it span..
13207
13208                     var  cfg = {
13209                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
13210                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
13211
13212                         unselectable : "on",
13213                         cn : [
13214                             {
13215                                 cls: 'fc-event-inner',
13216                                 cn : [
13217     //                                {
13218     //                                  tag:'span',
13219     //                                  cls: 'fc-event-time',
13220     //                                  html : cells.length > 1 ? '' : ev.time
13221     //                                },
13222                                     {
13223                                       tag:'span',
13224                                       cls: 'fc-event-title',
13225                                       html : String.format('{0}', ev.title)
13226                                     }
13227
13228
13229                                 ]
13230                             },
13231                             {
13232                                 cls: 'ui-resizable-handle ui-resizable-e',
13233                                 html : '&nbsp;&nbsp;&nbsp'
13234                             }
13235
13236                         ]
13237                     };
13238
13239                     if (i == 0) {
13240                         cfg.cls += ' fc-event-start';
13241                     }
13242                     if ((i+1) == rows.length) {
13243                         cfg.cls += ' fc-event-end';
13244                     }
13245
13246                     var ctr = _this.el.select('.fc-event-container',true).first();
13247                     var cg = ctr.createChild(cfg);
13248
13249                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
13250                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
13251
13252                     var r = (c.more.length) ? 1 : 0;
13253                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
13254                     cg.setWidth(ebox.right - sbox.x -2);
13255
13256                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
13257                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
13258                     cg.on('click', _this.onEventClick, _this, ev);
13259
13260                     ev.els.push(cg);
13261                     
13262                 }
13263                 
13264             }
13265             
13266             
13267             if(c.more.length){
13268                 var  cfg = {
13269                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
13270                     style : 'position: absolute',
13271                     unselectable : "on",
13272                     cn : [
13273                         {
13274                             cls: 'fc-event-inner',
13275                             cn : [
13276                                 {
13277                                   tag:'span',
13278                                   cls: 'fc-event-title',
13279                                   html : 'More'
13280                                 }
13281
13282
13283                             ]
13284                         },
13285                         {
13286                             cls: 'ui-resizable-handle ui-resizable-e',
13287                             html : '&nbsp;&nbsp;&nbsp'
13288                         }
13289
13290                     ]
13291                 };
13292
13293                 var ctr = _this.el.select('.fc-event-container',true).first();
13294                 var cg = ctr.createChild(cfg);
13295
13296                 var sbox = c.select('.fc-day-content',true).first().getBox();
13297                 var ebox = c.select('.fc-day-content',true).first().getBox();
13298                 //Roo.log(cg);
13299                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
13300                 cg.setWidth(ebox.right - sbox.x -2);
13301
13302                 cg.on('click', _this.onMoreEventClick, _this, c.more);
13303                 
13304             }
13305             
13306         });
13307         
13308         
13309         
13310     },
13311     
13312     onEventEnter: function (e, el,event,d) {
13313         this.fireEvent('evententer', this, el, event);
13314     },
13315     
13316     onEventLeave: function (e, el,event,d) {
13317         this.fireEvent('eventleave', this, el, event);
13318     },
13319     
13320     onEventClick: function (e, el,event,d) {
13321         this.fireEvent('eventclick', this, el, event);
13322     },
13323     
13324     onMonthChange: function () {
13325         this.store.load();
13326     },
13327     
13328     onMoreEventClick: function(e, el, more)
13329     {
13330         var _this = this;
13331         
13332         this.calpopover.placement = 'right';
13333         this.calpopover.setTitle('More');
13334         
13335         this.calpopover.setContent('');
13336         
13337         var ctr = this.calpopover.el.select('.popover-content', true).first();
13338         
13339         Roo.each(more, function(m){
13340             var cfg = {
13341                 cls : 'fc-event-hori fc-event-draggable',
13342                 html : m.title
13343             }
13344             var cg = ctr.createChild(cfg);
13345             
13346             cg.on('click', _this.onEventClick, _this, m);
13347         });
13348         
13349         this.calpopover.show(el);
13350         
13351         
13352     },
13353     
13354     onLoad: function () 
13355     {   
13356         this.calevents = [];
13357         var cal = this;
13358         
13359         if(this.store.getCount() > 0){
13360             this.store.data.each(function(d){
13361                cal.addItem({
13362                     id : d.data.id,
13363                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
13364                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
13365                     time : d.data.start_time,
13366                     title : d.data.title,
13367                     description : d.data.description,
13368                     venue : d.data.venue
13369                 });
13370             });
13371         }
13372         
13373         this.renderEvents();
13374         
13375         if(this.calevents.length && this.loadMask){
13376             this.maskEl.hide();
13377         }
13378     },
13379     
13380     onBeforeLoad: function()
13381     {
13382         this.clearEvents();
13383         if(this.loadMask){
13384             this.maskEl.show();
13385         }
13386     }
13387 });
13388
13389  
13390  /*
13391  * - LGPL
13392  *
13393  * element
13394  * 
13395  */
13396
13397 /**
13398  * @class Roo.bootstrap.Popover
13399  * @extends Roo.bootstrap.Component
13400  * Bootstrap Popover class
13401  * @cfg {String} html contents of the popover   (or false to use children..)
13402  * @cfg {String} title of popover (or false to hide)
13403  * @cfg {String} placement how it is placed
13404  * @cfg {String} trigger click || hover (or false to trigger manually)
13405  * @cfg {String} over what (parent or false to trigger manually.)
13406  * 
13407  * @constructor
13408  * Create a new Popover
13409  * @param {Object} config The config object
13410  */
13411
13412 Roo.bootstrap.Popover = function(config){
13413     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
13414 };
13415
13416 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
13417     
13418     title: 'Fill in a title',
13419     html: false,
13420     
13421     placement : 'right',
13422     trigger : 'hover', // hover
13423     
13424     over: 'parent',
13425     
13426     can_build_overlaid : false,
13427     
13428     getChildContainer : function()
13429     {
13430         return this.el.select('.popover-content',true).first();
13431     },
13432     
13433     getAutoCreate : function(){
13434          Roo.log('make popover?');
13435         var cfg = {
13436            cls : 'popover roo-dynamic',
13437            style: 'display:block',
13438            cn : [
13439                 {
13440                     cls : 'arrow'
13441                 },
13442                 {
13443                     cls : 'popover-inner',
13444                     cn : [
13445                         {
13446                             tag: 'h3',
13447                             cls: 'popover-title',
13448                             html : this.title
13449                         },
13450                         {
13451                             cls : 'popover-content',
13452                             html : this.html
13453                         }
13454                     ]
13455                     
13456                 }
13457            ]
13458         };
13459         
13460         return cfg;
13461     },
13462     setTitle: function(str)
13463     {
13464         this.el.select('.popover-title',true).first().dom.innerHTML = str;
13465     },
13466     setContent: function(str)
13467     {
13468         this.el.select('.popover-content',true).first().dom.innerHTML = str;
13469     },
13470     // as it get's added to the bottom of the page.
13471     onRender : function(ct, position)
13472     {
13473         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
13474         if(!this.el){
13475             var cfg = Roo.apply({},  this.getAutoCreate());
13476             cfg.id = Roo.id();
13477             
13478             if (this.cls) {
13479                 cfg.cls += ' ' + this.cls;
13480             }
13481             if (this.style) {
13482                 cfg.style = this.style;
13483             }
13484             Roo.log("adding to ")
13485             this.el = Roo.get(document.body).createChild(cfg, position);
13486             Roo.log(this.el);
13487         }
13488         this.initEvents();
13489     },
13490     
13491     initEvents : function()
13492     {
13493         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
13494         this.el.enableDisplayMode('block');
13495         this.el.hide();
13496         if (this.over === false) {
13497             return; 
13498         }
13499         if (this.triggers === false) {
13500             return;
13501         }
13502         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13503         var triggers = this.trigger ? this.trigger.split(' ') : [];
13504         Roo.each(triggers, function(trigger) {
13505         
13506             if (trigger == 'click') {
13507                 on_el.on('click', this.toggle, this);
13508             } else if (trigger != 'manual') {
13509                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
13510                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
13511       
13512                 on_el.on(eventIn  ,this.enter, this);
13513                 on_el.on(eventOut, this.leave, this);
13514             }
13515         }, this);
13516         
13517     },
13518     
13519     
13520     // private
13521     timeout : null,
13522     hoverState : null,
13523     
13524     toggle : function () {
13525         this.hoverState == 'in' ? this.leave() : this.enter();
13526     },
13527     
13528     enter : function () {
13529        
13530     
13531         clearTimeout(this.timeout);
13532     
13533         this.hoverState = 'in'
13534     
13535         if (!this.delay || !this.delay.show) {
13536             this.show();
13537             return 
13538         }
13539         var _t = this;
13540         this.timeout = setTimeout(function () {
13541             if (_t.hoverState == 'in') {
13542                 _t.show();
13543             }
13544         }, this.delay.show)
13545     },
13546     leave : function() {
13547         clearTimeout(this.timeout);
13548     
13549         this.hoverState = 'out'
13550     
13551         if (!this.delay || !this.delay.hide) {
13552             this.hide();
13553             return 
13554         }
13555         var _t = this;
13556         this.timeout = setTimeout(function () {
13557             if (_t.hoverState == 'out') {
13558                 _t.hide();
13559             }
13560         }, this.delay.hide)
13561     },
13562     
13563     show : function (on_el)
13564     {
13565         if (!on_el) {
13566             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13567         }
13568         // set content.
13569         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
13570         if (this.html !== false) {
13571             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
13572         }
13573         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
13574         if (!this.title.length) {
13575             this.el.select('.popover-title',true).hide();
13576         }
13577         
13578         var placement = typeof this.placement == 'function' ?
13579             this.placement.call(this, this.el, on_el) :
13580             this.placement;
13581             
13582         var autoToken = /\s?auto?\s?/i;
13583         var autoPlace = autoToken.test(placement);
13584         if (autoPlace) {
13585             placement = placement.replace(autoToken, '') || 'top';
13586         }
13587         
13588         //this.el.detach()
13589         //this.el.setXY([0,0]);
13590         this.el.show();
13591         this.el.dom.style.display='block';
13592         this.el.addClass(placement);
13593         
13594         //this.el.appendTo(on_el);
13595         
13596         var p = this.getPosition();
13597         var box = this.el.getBox();
13598         
13599         if (autoPlace) {
13600             // fixme..
13601         }
13602         var align = Roo.bootstrap.Popover.alignment[placement]
13603         this.el.alignTo(on_el, align[0],align[1]);
13604         //var arrow = this.el.select('.arrow',true).first();
13605         //arrow.set(align[2], 
13606         
13607         this.el.addClass('in');
13608         this.hoverState = null;
13609         
13610         if (this.el.hasClass('fade')) {
13611             // fade it?
13612         }
13613         
13614     },
13615     hide : function()
13616     {
13617         this.el.setXY([0,0]);
13618         this.el.removeClass('in');
13619         this.el.hide();
13620         
13621     }
13622     
13623 });
13624
13625 Roo.bootstrap.Popover.alignment = {
13626     'left' : ['r-l', [-10,0], 'right'],
13627     'right' : ['l-r', [10,0], 'left'],
13628     'bottom' : ['t-b', [0,10], 'top'],
13629     'top' : [ 'b-t', [0,-10], 'bottom']
13630 };
13631
13632  /*
13633  * - LGPL
13634  *
13635  * Progress
13636  * 
13637  */
13638
13639 /**
13640  * @class Roo.bootstrap.Progress
13641  * @extends Roo.bootstrap.Component
13642  * Bootstrap Progress class
13643  * @cfg {Boolean} striped striped of the progress bar
13644  * @cfg {Boolean} active animated of the progress bar
13645  * 
13646  * 
13647  * @constructor
13648  * Create a new Progress
13649  * @param {Object} config The config object
13650  */
13651
13652 Roo.bootstrap.Progress = function(config){
13653     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
13654 };
13655
13656 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
13657     
13658     striped : false,
13659     active: false,
13660     
13661     getAutoCreate : function(){
13662         var cfg = {
13663             tag: 'div',
13664             cls: 'progress'
13665         };
13666         
13667         
13668         if(this.striped){
13669             cfg.cls += ' progress-striped';
13670         }
13671       
13672         if(this.active){
13673             cfg.cls += ' active';
13674         }
13675         
13676         
13677         return cfg;
13678     }
13679    
13680 });
13681
13682  
13683
13684  /*
13685  * - LGPL
13686  *
13687  * ProgressBar
13688  * 
13689  */
13690
13691 /**
13692  * @class Roo.bootstrap.ProgressBar
13693  * @extends Roo.bootstrap.Component
13694  * Bootstrap ProgressBar class
13695  * @cfg {Number} aria_valuenow aria-value now
13696  * @cfg {Number} aria_valuemin aria-value min
13697  * @cfg {Number} aria_valuemax aria-value max
13698  * @cfg {String} label label for the progress bar
13699  * @cfg {String} panel (success | info | warning | danger )
13700  * @cfg {String} role role of the progress bar
13701  * @cfg {String} sr_only text
13702  * 
13703  * 
13704  * @constructor
13705  * Create a new ProgressBar
13706  * @param {Object} config The config object
13707  */
13708
13709 Roo.bootstrap.ProgressBar = function(config){
13710     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
13711 };
13712
13713 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
13714     
13715     aria_valuenow : 0,
13716     aria_valuemin : 0,
13717     aria_valuemax : 100,
13718     label : false,
13719     panel : false,
13720     role : false,
13721     sr_only: false,
13722     
13723     getAutoCreate : function()
13724     {
13725         
13726         var cfg = {
13727             tag: 'div',
13728             cls: 'progress-bar',
13729             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
13730         };
13731         
13732         if(this.sr_only){
13733             cfg.cn = {
13734                 tag: 'span',
13735                 cls: 'sr-only',
13736                 html: this.sr_only
13737             }
13738         }
13739         
13740         if(this.role){
13741             cfg.role = this.role;
13742         }
13743         
13744         if(this.aria_valuenow){
13745             cfg['aria-valuenow'] = this.aria_valuenow;
13746         }
13747         
13748         if(this.aria_valuemin){
13749             cfg['aria-valuemin'] = this.aria_valuemin;
13750         }
13751         
13752         if(this.aria_valuemax){
13753             cfg['aria-valuemax'] = this.aria_valuemax;
13754         }
13755         
13756         if(this.label && !this.sr_only){
13757             cfg.html = this.label;
13758         }
13759         
13760         if(this.panel){
13761             cfg.cls += ' progress-bar-' + this.panel;
13762         }
13763         
13764         return cfg;
13765     },
13766     
13767     update : function(aria_valuenow)
13768     {
13769         this.aria_valuenow = aria_valuenow;
13770         
13771         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
13772     }
13773    
13774 });
13775
13776  
13777
13778  /*
13779  * - LGPL
13780  *
13781  * column
13782  * 
13783  */
13784
13785 /**
13786  * @class Roo.bootstrap.TabGroup
13787  * @extends Roo.bootstrap.Column
13788  * Bootstrap Column class
13789  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
13790  * @cfg {Boolean} carousel true to make the group behave like a carousel
13791  * 
13792  * @constructor
13793  * Create a new TabGroup
13794  * @param {Object} config The config object
13795  */
13796
13797 Roo.bootstrap.TabGroup = function(config){
13798     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
13799     if (!this.navId) {
13800         this.navId = Roo.id();
13801     }
13802     this.tabs = [];
13803     Roo.bootstrap.TabGroup.register(this);
13804     
13805 };
13806
13807 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
13808     
13809     carousel : false,
13810      
13811     getAutoCreate : function()
13812     {
13813         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
13814         
13815         cfg.cls += ' tab-content';
13816         
13817         if (this.carousel) {
13818             cfg.cls += ' carousel slide';
13819             cfg.cn = [{
13820                cls : 'carousel-inner'
13821             }]
13822         }
13823         
13824         
13825         return cfg;
13826     },
13827     getChildContainer : function()
13828     {
13829         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
13830     },
13831     
13832     /**
13833     * register a Navigation item
13834     * @param {Roo.bootstrap.NavItem} the navitem to add
13835     */
13836     register : function(item)
13837     {
13838         this.tabs.push( item);
13839         item.navId = this.navId; // not really needed..
13840     
13841     },
13842     
13843     getActivePanel : function()
13844     {
13845         var r = false;
13846         Roo.each(this.tabs, function(t) {
13847             if (t.active) {
13848                 r = t;
13849                 return false;
13850             }
13851             return null;
13852         });
13853         return r;
13854         
13855     },
13856     getPanelByName : function(n)
13857     {
13858         var r = false;
13859         Roo.each(this.tabs, function(t) {
13860             if (t.tabId == n) {
13861                 r = t;
13862                 return false;
13863             }
13864             return null;
13865         });
13866         return r;
13867     },
13868     indexOfPanel : function(p)
13869     {
13870         var r = false;
13871         Roo.each(this.tabs, function(t,i) {
13872             if (t.tabId == p.tabId) {
13873                 r = i;
13874                 return false;
13875             }
13876             return null;
13877         });
13878         return r;
13879     },
13880     /**
13881      * show a specific panel
13882      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
13883      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
13884      */
13885     showPanel : function (pan)
13886     {
13887         
13888         
13889         
13890         if (typeof(pan) == 'number') {
13891             pan = this.tabs[pan];
13892         }
13893         if (typeof(pan) == 'string') {
13894             pan = this.getPanelByName(pan);
13895         }
13896         if (pan.tabId == this.getActivePanel().tabId) {
13897             return true;
13898         }
13899         var cur = this.getActivePanel();
13900         
13901         if (false === cur.fireEvent('beforedeactivate')) {
13902             return false;
13903         }
13904         
13905         
13906         
13907         if (this.carousel) {
13908             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
13909             var lr = dir == 'next' ? 'left' : 'right';
13910             pan.el.addClass(dir); // or prev
13911             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
13912             cur.el.addClass(lr); // or right
13913             pan.el.addClass(lr);
13914             cur.el.on('transitionend', function() {
13915                 Roo.log("trans end?");
13916                 
13917                 pan.el.removeClass([lr,dir]);
13918                 pan.setActive(true);
13919                 
13920                 cur.el.removeClass([lr]);
13921                 cur.setActive(false);
13922                 
13923                 
13924             }, this, { single:  true } );
13925             return true;
13926         }
13927         
13928         cur.setActive(false);
13929         pan.setActive(true);
13930         return true;
13931         
13932     },
13933     showPanelNext : function()
13934     {
13935         var i = this.indexOfPanel(this.getActivePanel());
13936         if (i > this.tabs.length) {
13937             return;
13938         }
13939         this.showPanel(this.tabs[i+1]);
13940     },
13941     showPanelPrev : function()
13942     {
13943         var i = this.indexOfPanel(this.getActivePanel());
13944         if (i  < 1) {
13945             return;
13946         }
13947         this.showPanel(this.tabs[i-1]);
13948     }
13949     
13950     
13951   
13952 });
13953
13954  
13955
13956  
13957  
13958 Roo.apply(Roo.bootstrap.TabGroup, {
13959     
13960     groups: {},
13961      /**
13962     * register a Navigation Group
13963     * @param {Roo.bootstrap.NavGroup} the navgroup to add
13964     */
13965     register : function(navgrp)
13966     {
13967         this.groups[navgrp.navId] = navgrp;
13968         
13969     },
13970     /**
13971     * fetch a Navigation Group based on the navigation ID
13972     * if one does not exist , it will get created.
13973     * @param {string} the navgroup to add
13974     * @returns {Roo.bootstrap.NavGroup} the navgroup 
13975     */
13976     get: function(navId) {
13977         if (typeof(this.groups[navId]) == 'undefined') {
13978             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
13979         }
13980         return this.groups[navId] ;
13981     }
13982     
13983     
13984     
13985 });
13986
13987  /*
13988  * - LGPL
13989  *
13990  * TabPanel
13991  * 
13992  */
13993
13994 /**
13995  * @class Roo.bootstrap.TabPanel
13996  * @extends Roo.bootstrap.Component
13997  * Bootstrap TabPanel class
13998  * @cfg {Boolean} active panel active
13999  * @cfg {String} html panel content
14000  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
14001  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
14002  * 
14003  * 
14004  * @constructor
14005  * Create a new TabPanel
14006  * @param {Object} config The config object
14007  */
14008
14009 Roo.bootstrap.TabPanel = function(config){
14010     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
14011     this.addEvents({
14012         /**
14013              * @event changed
14014              * Fires when the active status changes
14015              * @param {Roo.bootstrap.TabPanel} this
14016              * @param {Boolean} state the new state
14017             
14018          */
14019         'changed': true,
14020         /**
14021              * @event beforedeactivate
14022              * Fires before a tab is de-activated - can be used to do validation on a form.
14023              * @param {Roo.bootstrap.TabPanel} this
14024              * @return {Boolean} false if there is an error
14025             
14026          */
14027         'beforedeactivate': true
14028      });
14029     
14030     this.tabId = this.tabId || Roo.id();
14031   
14032 };
14033
14034 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
14035     
14036     active: false,
14037     html: false,
14038     tabId: false,
14039     navId : false,
14040     
14041     getAutoCreate : function(){
14042         var cfg = {
14043             tag: 'div',
14044             // item is needed for carousel - not sure if it has any effect otherwise
14045             cls: 'tab-pane item',
14046             html: this.html || ''
14047         };
14048         
14049         if(this.active){
14050             cfg.cls += ' active';
14051         }
14052         
14053         if(this.tabId){
14054             cfg.tabId = this.tabId;
14055         }
14056         
14057         
14058         return cfg;
14059     },
14060     
14061     initEvents:  function()
14062     {
14063         Roo.log('-------- init events on tab panel ---------');
14064         
14065         var p = this.parent();
14066         this.navId = this.navId || p.navId;
14067         
14068         if (typeof(this.navId) != 'undefined') {
14069             // not really needed.. but just in case.. parent should be a NavGroup.
14070             var tg = Roo.bootstrap.TabGroup.get(this.navId);
14071             Roo.log(['register', tg, this]);
14072             tg.register(this);
14073         }
14074     },
14075     
14076     
14077     onRender : function(ct, position)
14078     {
14079        // Roo.log("Call onRender: " + this.xtype);
14080         
14081         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
14082         
14083         
14084         
14085         
14086         
14087     },
14088     
14089     setActive: function(state)
14090     {
14091         Roo.log("panel - set active " + this.tabId + "=" + state);
14092         
14093         this.active = state;
14094         if (!state) {
14095             this.el.removeClass('active');
14096             
14097         } else  if (!this.el.hasClass('active')) {
14098             this.el.addClass('active');
14099         }
14100         this.fireEvent('changed', this, state);
14101     }
14102     
14103     
14104 });
14105  
14106
14107  
14108
14109  /*
14110  * - LGPL
14111  *
14112  * DateField
14113  * 
14114  */
14115
14116 /**
14117  * @class Roo.bootstrap.DateField
14118  * @extends Roo.bootstrap.Input
14119  * Bootstrap DateField class
14120  * @cfg {Number} weekStart default 0
14121  * @cfg {Number} weekStart default 0
14122  * @cfg {Number} viewMode default empty, (months|years)
14123  * @cfg {Number} minViewMode default empty, (months|years)
14124  * @cfg {Number} startDate default -Infinity
14125  * @cfg {Number} endDate default Infinity
14126  * @cfg {Boolean} todayHighlight default false
14127  * @cfg {Boolean} todayBtn default false
14128  * @cfg {Boolean} calendarWeeks default false
14129  * @cfg {Object} daysOfWeekDisabled default empty
14130  * 
14131  * @cfg {Boolean} keyboardNavigation default true
14132  * @cfg {String} language default en
14133  * 
14134  * @constructor
14135  * Create a new DateField
14136  * @param {Object} config The config object
14137  */
14138
14139 Roo.bootstrap.DateField = function(config){
14140     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
14141      this.addEvents({
14142             /**
14143              * @event show
14144              * Fires when this field show.
14145              * @param {Roo.bootstrap.DateField} this
14146              * @param {Mixed} date The date value
14147              */
14148             show : true,
14149             /**
14150              * @event show
14151              * Fires when this field hide.
14152              * @param {Roo.bootstrap.DateField} this
14153              * @param {Mixed} date The date value
14154              */
14155             hide : true,
14156             /**
14157              * @event select
14158              * Fires when select a date.
14159              * @param {Roo.bootstrap.DateField} this
14160              * @param {Mixed} date The date value
14161              */
14162             select : true
14163         });
14164 };
14165
14166 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
14167     
14168     /**
14169      * @cfg {String} format
14170      * The default date format string which can be overriden for localization support.  The format must be
14171      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
14172      */
14173     format : "m/d/y",
14174     /**
14175      * @cfg {String} altFormats
14176      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
14177      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
14178      */
14179     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
14180     
14181     weekStart : 0,
14182     
14183     viewMode : '',
14184     
14185     minViewMode : '',
14186     
14187     todayHighlight : false,
14188     
14189     todayBtn: false,
14190     
14191     language: 'en',
14192     
14193     keyboardNavigation: true,
14194     
14195     calendarWeeks: false,
14196     
14197     startDate: -Infinity,
14198     
14199     endDate: Infinity,
14200     
14201     daysOfWeekDisabled: [],
14202     
14203     _events: [],
14204     
14205     UTCDate: function()
14206     {
14207         return new Date(Date.UTC.apply(Date, arguments));
14208     },
14209     
14210     UTCToday: function()
14211     {
14212         var today = new Date();
14213         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
14214     },
14215     
14216     getDate: function() {
14217             var d = this.getUTCDate();
14218             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
14219     },
14220     
14221     getUTCDate: function() {
14222             return this.date;
14223     },
14224     
14225     setDate: function(d) {
14226             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
14227     },
14228     
14229     setUTCDate: function(d) {
14230             this.date = d;
14231             this.setValue(this.formatDate(this.date));
14232     },
14233         
14234     onRender: function(ct, position)
14235     {
14236         
14237         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
14238         
14239         this.language = this.language || 'en';
14240         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
14241         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
14242         
14243         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
14244         this.format = this.format || 'm/d/y';
14245         this.isInline = false;
14246         this.isInput = true;
14247         this.component = this.el.select('.add-on', true).first() || false;
14248         this.component = (this.component && this.component.length === 0) ? false : this.component;
14249         this.hasInput = this.component && this.inputEL().length;
14250         
14251         if (typeof(this.minViewMode === 'string')) {
14252             switch (this.minViewMode) {
14253                 case 'months':
14254                     this.minViewMode = 1;
14255                     break;
14256                 case 'years':
14257                     this.minViewMode = 2;
14258                     break;
14259                 default:
14260                     this.minViewMode = 0;
14261                     break;
14262             }
14263         }
14264         
14265         if (typeof(this.viewMode === 'string')) {
14266             switch (this.viewMode) {
14267                 case 'months':
14268                     this.viewMode = 1;
14269                     break;
14270                 case 'years':
14271                     this.viewMode = 2;
14272                     break;
14273                 default:
14274                     this.viewMode = 0;
14275                     break;
14276             }
14277         }
14278                 
14279         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
14280         
14281         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14282         
14283         this.picker().on('mousedown', this.onMousedown, this);
14284         this.picker().on('click', this.onClick, this);
14285         
14286         this.picker().addClass('datepicker-dropdown');
14287         
14288         this.startViewMode = this.viewMode;
14289         
14290         
14291         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
14292             if(!this.calendarWeeks){
14293                 v.remove();
14294                 return;
14295             };
14296             
14297             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
14298             v.attr('colspan', function(i, val){
14299                 return parseInt(val) + 1;
14300             });
14301         })
14302                         
14303         
14304         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
14305         
14306         this.setStartDate(this.startDate);
14307         this.setEndDate(this.endDate);
14308         
14309         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
14310         
14311         this.fillDow();
14312         this.fillMonths();
14313         this.update();
14314         this.showMode();
14315         
14316         if(this.isInline) {
14317             this.show();
14318         }
14319     },
14320     
14321     picker : function()
14322     {
14323         return this.el.select('.datepicker', true).first();
14324     },
14325     
14326     fillDow: function()
14327     {
14328         var dowCnt = this.weekStart;
14329         
14330         var dow = {
14331             tag: 'tr',
14332             cn: [
14333                 
14334             ]
14335         };
14336         
14337         if(this.calendarWeeks){
14338             dow.cn.push({
14339                 tag: 'th',
14340                 cls: 'cw',
14341                 html: '&nbsp;'
14342             })
14343         }
14344         
14345         while (dowCnt < this.weekStart + 7) {
14346             dow.cn.push({
14347                 tag: 'th',
14348                 cls: 'dow',
14349                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
14350             });
14351         }
14352         
14353         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
14354     },
14355     
14356     fillMonths: function()
14357     {    
14358         var i = 0
14359         var months = this.picker().select('>.datepicker-months td', true).first();
14360         
14361         months.dom.innerHTML = '';
14362         
14363         while (i < 12) {
14364             var month = {
14365                 tag: 'span',
14366                 cls: 'month',
14367                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
14368             }
14369             
14370             months.createChild(month);
14371         }
14372         
14373     },
14374     
14375     update: function()
14376     {
14377         
14378         this.date = (typeof(this.date) === 'undefined') ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
14379         
14380         if (this.date < this.startDate) {
14381             this.viewDate = new Date(this.startDate);
14382         } else if (this.date > this.endDate) {
14383             this.viewDate = new Date(this.endDate);
14384         } else {
14385             this.viewDate = new Date(this.date);
14386         }
14387         
14388         this.fill();
14389     },
14390     
14391     fill: function() 
14392     {
14393         var d = new Date(this.viewDate),
14394                 year = d.getUTCFullYear(),
14395                 month = d.getUTCMonth(),
14396                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
14397                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
14398                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
14399                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
14400                 currentDate = this.date && this.date.valueOf(),
14401                 today = this.UTCToday();
14402         
14403         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
14404         
14405 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
14406         
14407 //        this.picker.select('>tfoot th.today').
14408 //                                              .text(dates[this.language].today)
14409 //                                              .toggle(this.todayBtn !== false);
14410     
14411         this.updateNavArrows();
14412         this.fillMonths();
14413                                                 
14414         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
14415         
14416         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
14417          
14418         prevMonth.setUTCDate(day);
14419         
14420         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
14421         
14422         var nextMonth = new Date(prevMonth);
14423         
14424         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
14425         
14426         nextMonth = nextMonth.valueOf();
14427         
14428         var fillMonths = false;
14429         
14430         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
14431         
14432         while(prevMonth.valueOf() < nextMonth) {
14433             var clsName = '';
14434             
14435             if (prevMonth.getUTCDay() === this.weekStart) {
14436                 if(fillMonths){
14437                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
14438                 }
14439                     
14440                 fillMonths = {
14441                     tag: 'tr',
14442                     cn: []
14443                 };
14444                 
14445                 if(this.calendarWeeks){
14446                     // ISO 8601: First week contains first thursday.
14447                     // ISO also states week starts on Monday, but we can be more abstract here.
14448                     var
14449                     // Start of current week: based on weekstart/current date
14450                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
14451                     // Thursday of this week
14452                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
14453                     // First Thursday of year, year from thursday
14454                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
14455                     // Calendar week: ms between thursdays, div ms per day, div 7 days
14456                     calWeek =  (th - yth) / 864e5 / 7 + 1;
14457                     
14458                     fillMonths.cn.push({
14459                         tag: 'td',
14460                         cls: 'cw',
14461                         html: calWeek
14462                     });
14463                 }
14464             }
14465             
14466             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
14467                 clsName += ' old';
14468             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
14469                 clsName += ' new';
14470             }
14471             if (this.todayHighlight &&
14472                 prevMonth.getUTCFullYear() == today.getFullYear() &&
14473                 prevMonth.getUTCMonth() == today.getMonth() &&
14474                 prevMonth.getUTCDate() == today.getDate()) {
14475                 clsName += ' today';
14476             }
14477             
14478             if (currentDate && prevMonth.valueOf() === currentDate) {
14479                 clsName += ' active';
14480             }
14481             
14482             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
14483                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
14484                     clsName += ' disabled';
14485             }
14486             
14487             fillMonths.cn.push({
14488                 tag: 'td',
14489                 cls: 'day ' + clsName,
14490                 html: prevMonth.getDate()
14491             })
14492             
14493             prevMonth.setDate(prevMonth.getDate()+1);
14494         }
14495           
14496         var currentYear = this.date && this.date.getUTCFullYear();
14497         var currentMonth = this.date && this.date.getUTCMonth();
14498         
14499         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
14500         
14501         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
14502             v.removeClass('active');
14503             
14504             if(currentYear === year && k === currentMonth){
14505                 v.addClass('active');
14506             }
14507             
14508             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
14509                 v.addClass('disabled');
14510             }
14511             
14512         });
14513         
14514         
14515         year = parseInt(year/10, 10) * 10;
14516         
14517         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
14518         
14519         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
14520         
14521         year -= 1;
14522         for (var i = -1; i < 11; i++) {
14523             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
14524                 tag: 'span',
14525                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
14526                 html: year
14527             })
14528             
14529             year += 1;
14530         }
14531     },
14532     
14533     showMode: function(dir) 
14534     {
14535         if (dir) {
14536             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
14537         }
14538         Roo.each(this.picker().select('>div',true).elements, function(v){
14539             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14540             v.hide();
14541         });
14542         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
14543     },
14544     
14545     place: function()
14546     {
14547         if(this.isInline) return;
14548         
14549         this.picker().removeClass(['bottom', 'top']);
14550         
14551         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
14552             /*
14553              * place to the top of element!
14554              *
14555              */
14556             
14557             this.picker().addClass('top');
14558             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
14559             
14560             return;
14561         }
14562         
14563         this.picker().addClass('bottom');
14564         
14565         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
14566     },
14567     
14568     parseDate : function(value)
14569     {
14570         if(!value || value instanceof Date){
14571             return value;
14572         }
14573         var v = Date.parseDate(value, this.format);
14574         if (!v && this.useIso) {
14575             v = Date.parseDate(value, 'Y-m-d');
14576         }
14577         if(!v && this.altFormats){
14578             if(!this.altFormatsArray){
14579                 this.altFormatsArray = this.altFormats.split("|");
14580             }
14581             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
14582                 v = Date.parseDate(value, this.altFormatsArray[i]);
14583             }
14584         }
14585         return v;
14586     },
14587     
14588     formatDate : function(date, fmt)
14589     {
14590         return (!date || !(date instanceof Date)) ?
14591         date : date.dateFormat(fmt || this.format);
14592     },
14593     
14594     onFocus : function()
14595     {
14596         Roo.bootstrap.DateField.superclass.onFocus.call(this);
14597         this.show();
14598     },
14599     
14600     onBlur : function()
14601     {
14602         Roo.bootstrap.DateField.superclass.onBlur.call(this);
14603         
14604         var d = this.inputEl().getValue();
14605         
14606         if(d && d.length){
14607             this.setValue(d);
14608         }
14609                 
14610         this.hide();
14611     },
14612     
14613     show : function()
14614     {
14615         this.picker().show();
14616         this.update();
14617         this.place();
14618         
14619         this.fireEvent('show', this, this.date);
14620     },
14621     
14622     hide : function()
14623     {
14624         if(this.isInline) return;
14625         this.picker().hide();
14626         this.viewMode = this.startViewMode;
14627         this.showMode();
14628         
14629         this.fireEvent('hide', this, this.date);
14630         
14631     },
14632     
14633     onMousedown: function(e)
14634     {
14635         e.stopPropagation();
14636         e.preventDefault();
14637     },
14638     
14639     keyup: function(e)
14640     {
14641         Roo.bootstrap.DateField.superclass.keyup.call(this);
14642         this.update();
14643     },
14644
14645     setValue: function(v)
14646     {
14647         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
14648         
14649         var d = new Date(v);
14650         
14651         if(isNaN(d.getTime())){
14652             return;
14653         }
14654         
14655         this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
14656
14657         this.update();
14658
14659         this.fireEvent('select', this, this.date);
14660         
14661     },
14662     
14663     getValue: function()
14664     {
14665         return this.formatDate(this.date);
14666     },
14667     
14668     fireKey: function(e)
14669     {
14670         if (!this.picker().isVisible()){
14671             if (e.keyCode == 27) // allow escape to hide and re-show picker
14672                 this.show();
14673             return;
14674         }
14675         
14676         var dateChanged = false,
14677         dir, day, month,
14678         newDate, newViewDate;
14679         
14680         switch(e.keyCode){
14681             case 27: // escape
14682                 this.hide();
14683                 e.preventDefault();
14684                 break;
14685             case 37: // left
14686             case 39: // right
14687                 if (!this.keyboardNavigation) break;
14688                 dir = e.keyCode == 37 ? -1 : 1;
14689                 
14690                 if (e.ctrlKey){
14691                     newDate = this.moveYear(this.date, dir);
14692                     newViewDate = this.moveYear(this.viewDate, dir);
14693                 } else if (e.shiftKey){
14694                     newDate = this.moveMonth(this.date, dir);
14695                     newViewDate = this.moveMonth(this.viewDate, dir);
14696                 } else {
14697                     newDate = new Date(this.date);
14698                     newDate.setUTCDate(this.date.getUTCDate() + dir);
14699                     newViewDate = new Date(this.viewDate);
14700                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
14701                 }
14702                 if (this.dateWithinRange(newDate)){
14703                     this.date = newDate;
14704                     this.viewDate = newViewDate;
14705                     this.setValue(this.formatDate(this.date));
14706 //                    this.update();
14707                     e.preventDefault();
14708                     dateChanged = true;
14709                 }
14710                 break;
14711             case 38: // up
14712             case 40: // down
14713                 if (!this.keyboardNavigation) break;
14714                 dir = e.keyCode == 38 ? -1 : 1;
14715                 if (e.ctrlKey){
14716                     newDate = this.moveYear(this.date, dir);
14717                     newViewDate = this.moveYear(this.viewDate, dir);
14718                 } else if (e.shiftKey){
14719                     newDate = this.moveMonth(this.date, dir);
14720                     newViewDate = this.moveMonth(this.viewDate, dir);
14721                 } else {
14722                     newDate = new Date(this.date);
14723                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
14724                     newViewDate = new Date(this.viewDate);
14725                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
14726                 }
14727                 if (this.dateWithinRange(newDate)){
14728                     this.date = newDate;
14729                     this.viewDate = newViewDate;
14730                     this.setValue(this.formatDate(this.date));
14731 //                    this.update();
14732                     e.preventDefault();
14733                     dateChanged = true;
14734                 }
14735                 break;
14736             case 13: // enter
14737                 this.setValue(this.formatDate(this.date));
14738                 this.hide();
14739                 e.preventDefault();
14740                 break;
14741             case 9: // tab
14742                 this.setValue(this.formatDate(this.date));
14743                 this.hide();
14744                 break;
14745             case 16: // shift
14746             case 17: // ctrl
14747             case 18: // alt
14748                 break;
14749             default :
14750                 this.hide();
14751                 
14752         }
14753     },
14754     
14755     
14756     onClick: function(e) 
14757     {
14758         e.stopPropagation();
14759         e.preventDefault();
14760         
14761         var target = e.getTarget();
14762         
14763         if(target.nodeName.toLowerCase() === 'i'){
14764             target = Roo.get(target).dom.parentNode;
14765         }
14766         
14767         var nodeName = target.nodeName;
14768         var className = target.className;
14769         var html = target.innerHTML;
14770         
14771         switch(nodeName.toLowerCase()) {
14772             case 'th':
14773                 switch(className) {
14774                     case 'switch':
14775                         this.showMode(1);
14776                         break;
14777                     case 'prev':
14778                     case 'next':
14779                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
14780                         switch(this.viewMode){
14781                                 case 0:
14782                                         this.viewDate = this.moveMonth(this.viewDate, dir);
14783                                         break;
14784                                 case 1:
14785                                 case 2:
14786                                         this.viewDate = this.moveYear(this.viewDate, dir);
14787                                         break;
14788                         }
14789                         this.fill();
14790                         break;
14791                     case 'today':
14792                         var date = new Date();
14793                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
14794 //                        this.fill()
14795                         this.setValue(this.formatDate(this.date));
14796                         
14797                         this.hide();
14798                         break;
14799                 }
14800                 break;
14801             case 'span':
14802                 if (className.indexOf('disabled') === -1) {
14803                     this.viewDate.setUTCDate(1);
14804                     if (className.indexOf('month') !== -1) {
14805                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
14806                     } else {
14807                         var year = parseInt(html, 10) || 0;
14808                         this.viewDate.setUTCFullYear(year);
14809                         
14810                     }
14811                     this.showMode(-1);
14812                     this.fill();
14813                 }
14814                 break;
14815                 
14816             case 'td':
14817                 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
14818                     var day = parseInt(html, 10) || 1;
14819                     var year = this.viewDate.getUTCFullYear(),
14820                         month = this.viewDate.getUTCMonth();
14821
14822                     if (className.indexOf('old') !== -1) {
14823                         if(month === 0 ){
14824                             month = 11;
14825                             year -= 1;
14826                         }else{
14827                             month -= 1;
14828                         }
14829                     } else if (className.indexOf('new') !== -1) {
14830                         if (month == 11) {
14831                             month = 0;
14832                             year += 1;
14833                         } else {
14834                             month += 1;
14835                         }
14836                     }
14837                     this.date = this.UTCDate(year, month, day,0,0,0,0);
14838                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
14839 //                    this.fill();
14840                     this.setValue(this.formatDate(this.date));
14841                     this.hide();
14842                 }
14843                 break;
14844         }
14845     },
14846     
14847     setStartDate: function(startDate)
14848     {
14849         this.startDate = startDate || -Infinity;
14850         if (this.startDate !== -Infinity) {
14851             this.startDate = this.parseDate(this.startDate);
14852         }
14853         this.update();
14854         this.updateNavArrows();
14855     },
14856
14857     setEndDate: function(endDate)
14858     {
14859         this.endDate = endDate || Infinity;
14860         if (this.endDate !== Infinity) {
14861             this.endDate = this.parseDate(this.endDate);
14862         }
14863         this.update();
14864         this.updateNavArrows();
14865     },
14866     
14867     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
14868     {
14869         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
14870         if (typeof(this.daysOfWeekDisabled) !== 'object') {
14871             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
14872         }
14873         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
14874             return parseInt(d, 10);
14875         });
14876         this.update();
14877         this.updateNavArrows();
14878     },
14879     
14880     updateNavArrows: function() 
14881     {
14882         var d = new Date(this.viewDate),
14883         year = d.getUTCFullYear(),
14884         month = d.getUTCMonth();
14885         
14886         Roo.each(this.picker().select('.prev', true).elements, function(v){
14887             v.show();
14888             switch (this.viewMode) {
14889                 case 0:
14890
14891                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
14892                         v.hide();
14893                     }
14894                     break;
14895                 case 1:
14896                 case 2:
14897                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
14898                         v.hide();
14899                     }
14900                     break;
14901             }
14902         });
14903         
14904         Roo.each(this.picker().select('.next', true).elements, function(v){
14905             v.show();
14906             switch (this.viewMode) {
14907                 case 0:
14908
14909                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
14910                         v.hide();
14911                     }
14912                     break;
14913                 case 1:
14914                 case 2:
14915                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
14916                         v.hide();
14917                     }
14918                     break;
14919             }
14920         })
14921     },
14922     
14923     moveMonth: function(date, dir)
14924     {
14925         if (!dir) return date;
14926         var new_date = new Date(date.valueOf()),
14927         day = new_date.getUTCDate(),
14928         month = new_date.getUTCMonth(),
14929         mag = Math.abs(dir),
14930         new_month, test;
14931         dir = dir > 0 ? 1 : -1;
14932         if (mag == 1){
14933             test = dir == -1
14934             // If going back one month, make sure month is not current month
14935             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
14936             ? function(){
14937                 return new_date.getUTCMonth() == month;
14938             }
14939             // If going forward one month, make sure month is as expected
14940             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
14941             : function(){
14942                 return new_date.getUTCMonth() != new_month;
14943             };
14944             new_month = month + dir;
14945             new_date.setUTCMonth(new_month);
14946             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
14947             if (new_month < 0 || new_month > 11)
14948                 new_month = (new_month + 12) % 12;
14949         } else {
14950             // For magnitudes >1, move one month at a time...
14951             for (var i=0; i<mag; i++)
14952                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
14953                 new_date = this.moveMonth(new_date, dir);
14954             // ...then reset the day, keeping it in the new month
14955             new_month = new_date.getUTCMonth();
14956             new_date.setUTCDate(day);
14957             test = function(){
14958                 return new_month != new_date.getUTCMonth();
14959             };
14960         }
14961         // Common date-resetting loop -- if date is beyond end of month, make it
14962         // end of month
14963         while (test()){
14964             new_date.setUTCDate(--day);
14965             new_date.setUTCMonth(new_month);
14966         }
14967         return new_date;
14968     },
14969
14970     moveYear: function(date, dir)
14971     {
14972         return this.moveMonth(date, dir*12);
14973     },
14974
14975     dateWithinRange: function(date)
14976     {
14977         return date >= this.startDate && date <= this.endDate;
14978     },
14979
14980     
14981     remove: function() 
14982     {
14983         this.picker().remove();
14984     }
14985    
14986 });
14987
14988 Roo.apply(Roo.bootstrap.DateField,  {
14989     
14990     head : {
14991         tag: 'thead',
14992         cn: [
14993         {
14994             tag: 'tr',
14995             cn: [
14996             {
14997                 tag: 'th',
14998                 cls: 'prev',
14999                 html: '<i class="fa fa-arrow-left"/>'
15000             },
15001             {
15002                 tag: 'th',
15003                 cls: 'switch',
15004                 colspan: '5'
15005             },
15006             {
15007                 tag: 'th',
15008                 cls: 'next',
15009                 html: '<i class="fa fa-arrow-right"/>'
15010             }
15011
15012             ]
15013         }
15014         ]
15015     },
15016     
15017     content : {
15018         tag: 'tbody',
15019         cn: [
15020         {
15021             tag: 'tr',
15022             cn: [
15023             {
15024                 tag: 'td',
15025                 colspan: '7'
15026             }
15027             ]
15028         }
15029         ]
15030     },
15031     
15032     footer : {
15033         tag: 'tfoot',
15034         cn: [
15035         {
15036             tag: 'tr',
15037             cn: [
15038             {
15039                 tag: 'th',
15040                 colspan: '7',
15041                 cls: 'today'
15042             }
15043                     
15044             ]
15045         }
15046         ]
15047     },
15048     
15049     dates:{
15050         en: {
15051             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
15052             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
15053             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
15054             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
15055             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
15056             today: "Today"
15057         }
15058     },
15059     
15060     modes: [
15061     {
15062         clsName: 'days',
15063         navFnc: 'Month',
15064         navStep: 1
15065     },
15066     {
15067         clsName: 'months',
15068         navFnc: 'FullYear',
15069         navStep: 1
15070     },
15071     {
15072         clsName: 'years',
15073         navFnc: 'FullYear',
15074         navStep: 10
15075     }]
15076 });
15077
15078 Roo.apply(Roo.bootstrap.DateField,  {
15079   
15080     template : {
15081         tag: 'div',
15082         cls: 'datepicker dropdown-menu',
15083         cn: [
15084         {
15085             tag: 'div',
15086             cls: 'datepicker-days',
15087             cn: [
15088             {
15089                 tag: 'table',
15090                 cls: 'table-condensed',
15091                 cn:[
15092                 Roo.bootstrap.DateField.head,
15093                 {
15094                     tag: 'tbody'
15095                 },
15096                 Roo.bootstrap.DateField.footer
15097                 ]
15098             }
15099             ]
15100         },
15101         {
15102             tag: 'div',
15103             cls: 'datepicker-months',
15104             cn: [
15105             {
15106                 tag: 'table',
15107                 cls: 'table-condensed',
15108                 cn:[
15109                 Roo.bootstrap.DateField.head,
15110                 Roo.bootstrap.DateField.content,
15111                 Roo.bootstrap.DateField.footer
15112                 ]
15113             }
15114             ]
15115         },
15116         {
15117             tag: 'div',
15118             cls: 'datepicker-years',
15119             cn: [
15120             {
15121                 tag: 'table',
15122                 cls: 'table-condensed',
15123                 cn:[
15124                 Roo.bootstrap.DateField.head,
15125                 Roo.bootstrap.DateField.content,
15126                 Roo.bootstrap.DateField.footer
15127                 ]
15128             }
15129             ]
15130         }
15131         ]
15132     }
15133 });
15134
15135  
15136
15137  /*
15138  * - LGPL
15139  *
15140  * TimeField
15141  * 
15142  */
15143
15144 /**
15145  * @class Roo.bootstrap.TimeField
15146  * @extends Roo.bootstrap.Input
15147  * Bootstrap DateField class
15148  * 
15149  * 
15150  * @constructor
15151  * Create a new TimeField
15152  * @param {Object} config The config object
15153  */
15154
15155 Roo.bootstrap.TimeField = function(config){
15156     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
15157     this.addEvents({
15158             /**
15159              * @event show
15160              * Fires when this field show.
15161              * @param {Roo.bootstrap.DateField} this
15162              * @param {Mixed} date The date value
15163              */
15164             show : true,
15165             /**
15166              * @event show
15167              * Fires when this field hide.
15168              * @param {Roo.bootstrap.DateField} this
15169              * @param {Mixed} date The date value
15170              */
15171             hide : true,
15172             /**
15173              * @event select
15174              * Fires when select a date.
15175              * @param {Roo.bootstrap.DateField} this
15176              * @param {Mixed} date The date value
15177              */
15178             select : true
15179         });
15180 };
15181
15182 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
15183     
15184     /**
15185      * @cfg {String} format
15186      * The default time format string which can be overriden for localization support.  The format must be
15187      * valid according to {@link Date#parseDate} (defaults to 'H:i').
15188      */
15189     format : "H:i",
15190        
15191     onRender: function(ct, position)
15192     {
15193         
15194         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
15195                 
15196         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
15197         
15198         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15199         
15200         this.pop = this.picker().select('>.datepicker-time',true).first();
15201         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block' 
15202         
15203         this.picker().on('mousedown', this.onMousedown, this);
15204         this.picker().on('click', this.onClick, this);
15205         
15206         this.picker().addClass('datepicker-dropdown');
15207     
15208         this.fillTime();
15209         this.update();
15210             
15211         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
15212         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
15213         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
15214         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
15215         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
15216         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
15217
15218     },
15219     
15220     fireKey: function(e){
15221         if (!this.picker().isVisible()){
15222             if (e.keyCode == 27) // allow escape to hide and re-show picker
15223                 this.show();
15224             return;
15225         }
15226
15227         e.preventDefault();
15228         
15229         switch(e.keyCode){
15230             case 27: // escape
15231                 this.hide();
15232                 break;
15233             case 37: // left
15234             case 39: // right
15235                 this.onTogglePeriod();
15236                 break;
15237             case 38: // up
15238                 this.onIncrementMinutes();
15239                 break;
15240             case 40: // down
15241                 this.onDecrementMinutes();
15242                 break;
15243             case 13: // enter
15244             case 9: // tab
15245                 this.setTime();
15246                 break;
15247         }
15248     },
15249     
15250     onClick: function(e) {
15251         e.stopPropagation();
15252         e.preventDefault();
15253     },
15254     
15255     picker : function()
15256     {
15257         return this.el.select('.datepicker', true).first();
15258     },
15259     
15260     fillTime: function()
15261     {    
15262         var time = this.pop.select('tbody', true).first();
15263         
15264         time.dom.innerHTML = '';
15265         
15266         time.createChild({
15267             tag: 'tr',
15268             cn: [
15269                 {
15270                     tag: 'td',
15271                     cn: [
15272                         {
15273                             tag: 'a',
15274                             href: '#',
15275                             cls: 'btn',
15276                             cn: [
15277                                 {
15278                                     tag: 'span',
15279                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
15280                                 }
15281                             ]
15282                         } 
15283                     ]
15284                 },
15285                 {
15286                     tag: 'td',
15287                     cls: 'separator'
15288                 },
15289                 {
15290                     tag: 'td',
15291                     cn: [
15292                         {
15293                             tag: 'a',
15294                             href: '#',
15295                             cls: 'btn',
15296                             cn: [
15297                                 {
15298                                     tag: 'span',
15299                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
15300                                 }
15301                             ]
15302                         }
15303                     ]
15304                 },
15305                 {
15306                     tag: 'td',
15307                     cls: 'separator'
15308                 }
15309             ]
15310         });
15311         
15312         time.createChild({
15313             tag: 'tr',
15314             cn: [
15315                 {
15316                     tag: 'td',
15317                     cn: [
15318                         {
15319                             tag: 'span',
15320                             cls: 'timepicker-hour',
15321                             html: '00'
15322                         }  
15323                     ]
15324                 },
15325                 {
15326                     tag: 'td',
15327                     cls: 'separator',
15328                     html: ':'
15329                 },
15330                 {
15331                     tag: 'td',
15332                     cn: [
15333                         {
15334                             tag: 'span',
15335                             cls: 'timepicker-minute',
15336                             html: '00'
15337                         }  
15338                     ]
15339                 },
15340                 {
15341                     tag: 'td',
15342                     cls: 'separator'
15343                 },
15344                 {
15345                     tag: 'td',
15346                     cn: [
15347                         {
15348                             tag: 'button',
15349                             type: 'button',
15350                             cls: 'btn btn-primary period',
15351                             html: 'AM'
15352                             
15353                         }
15354                     ]
15355                 }
15356             ]
15357         });
15358         
15359         time.createChild({
15360             tag: 'tr',
15361             cn: [
15362                 {
15363                     tag: 'td',
15364                     cn: [
15365                         {
15366                             tag: 'a',
15367                             href: '#',
15368                             cls: 'btn',
15369                             cn: [
15370                                 {
15371                                     tag: 'span',
15372                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
15373                                 }
15374                             ]
15375                         }
15376                     ]
15377                 },
15378                 {
15379                     tag: 'td',
15380                     cls: 'separator'
15381                 },
15382                 {
15383                     tag: 'td',
15384                     cn: [
15385                         {
15386                             tag: 'a',
15387                             href: '#',
15388                             cls: 'btn',
15389                             cn: [
15390                                 {
15391                                     tag: 'span',
15392                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
15393                                 }
15394                             ]
15395                         }
15396                     ]
15397                 },
15398                 {
15399                     tag: 'td',
15400                     cls: 'separator'
15401                 }
15402             ]
15403         });
15404         
15405     },
15406     
15407     update: function()
15408     {
15409         
15410         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
15411         
15412         this.fill();
15413     },
15414     
15415     fill: function() 
15416     {
15417         var hours = this.time.getHours();
15418         var minutes = this.time.getMinutes();
15419         var period = 'AM';
15420         
15421         if(hours > 11){
15422             period = 'PM';
15423         }
15424         
15425         if(hours == 0){
15426             hours = 12;
15427         }
15428         
15429         
15430         if(hours > 12){
15431             hours = hours - 12;
15432         }
15433         
15434         if(hours < 10){
15435             hours = '0' + hours;
15436         }
15437         
15438         if(minutes < 10){
15439             minutes = '0' + minutes;
15440         }
15441         
15442         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
15443         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
15444         this.pop.select('button', true).first().dom.innerHTML = period;
15445         
15446     },
15447     
15448     place: function()
15449     {   
15450         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
15451         
15452         var cls = ['bottom'];
15453         
15454         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
15455             cls.pop();
15456             cls.push('top');
15457         }
15458         
15459         cls.push('right');
15460         
15461         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
15462             cls.pop();
15463             cls.push('left');
15464         }
15465         
15466         this.picker().addClass(cls.join('-'));
15467         
15468         var _this = this;
15469         
15470         Roo.each(cls, function(c){
15471             if(c == 'bottom'){
15472                 _this.picker().setTop(_this.inputEl().getHeight());
15473                 return;
15474             }
15475             if(c == 'top'){
15476                 _this.picker().setTop(0 - _this.picker().getHeight());
15477                 return;
15478             }
15479             
15480             if(c == 'left'){
15481                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
15482                 return;
15483             }
15484             if(c == 'right'){
15485                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
15486                 return;
15487             }
15488         });
15489         
15490     },
15491   
15492     onFocus : function()
15493     {
15494         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
15495         this.show();
15496     },
15497     
15498     onBlur : function()
15499     {
15500         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
15501         this.hide();
15502     },
15503     
15504     show : function()
15505     {
15506         this.picker().show();
15507         this.pop.show();
15508         this.update();
15509         this.place();
15510         
15511         this.fireEvent('show', this, this.date);
15512     },
15513     
15514     hide : function()
15515     {
15516         this.picker().hide();
15517         this.pop.hide();
15518         
15519         this.fireEvent('hide', this, this.date);
15520     },
15521     
15522     setTime : function()
15523     {
15524         this.hide();
15525         this.setValue(this.time.format(this.format));
15526         
15527         this.fireEvent('select', this, this.date);
15528         
15529         
15530     },
15531     
15532     onMousedown: function(e){
15533         e.stopPropagation();
15534         e.preventDefault();
15535     },
15536     
15537     onIncrementHours: function()
15538     {
15539         Roo.log('onIncrementHours');
15540         this.time = this.time.add(Date.HOUR, 1);
15541         this.update();
15542         
15543     },
15544     
15545     onDecrementHours: function()
15546     {
15547         Roo.log('onDecrementHours');
15548         this.time = this.time.add(Date.HOUR, -1);
15549         this.update();
15550     },
15551     
15552     onIncrementMinutes: function()
15553     {
15554         Roo.log('onIncrementMinutes');
15555         this.time = this.time.add(Date.MINUTE, 1);
15556         this.update();
15557     },
15558     
15559     onDecrementMinutes: function()
15560     {
15561         Roo.log('onDecrementMinutes');
15562         this.time = this.time.add(Date.MINUTE, -1);
15563         this.update();
15564     },
15565     
15566     onTogglePeriod: function()
15567     {
15568         Roo.log('onTogglePeriod');
15569         this.time = this.time.add(Date.HOUR, 12);
15570         this.update();
15571     }
15572     
15573    
15574 });
15575
15576 Roo.apply(Roo.bootstrap.TimeField,  {
15577     
15578     content : {
15579         tag: 'tbody',
15580         cn: [
15581             {
15582                 tag: 'tr',
15583                 cn: [
15584                 {
15585                     tag: 'td',
15586                     colspan: '7'
15587                 }
15588                 ]
15589             }
15590         ]
15591     },
15592     
15593     footer : {
15594         tag: 'tfoot',
15595         cn: [
15596             {
15597                 tag: 'tr',
15598                 cn: [
15599                 {
15600                     tag: 'th',
15601                     colspan: '7',
15602                     cls: '',
15603                     cn: [
15604                         {
15605                             tag: 'button',
15606                             cls: 'btn btn-info ok',
15607                             html: 'OK'
15608                         }
15609                     ]
15610                 }
15611
15612                 ]
15613             }
15614         ]
15615     }
15616 });
15617
15618 Roo.apply(Roo.bootstrap.TimeField,  {
15619   
15620     template : {
15621         tag: 'div',
15622         cls: 'datepicker dropdown-menu',
15623         cn: [
15624             {
15625                 tag: 'div',
15626                 cls: 'datepicker-time',
15627                 cn: [
15628                 {
15629                     tag: 'table',
15630                     cls: 'table-condensed',
15631                     cn:[
15632                     Roo.bootstrap.TimeField.content,
15633                     Roo.bootstrap.TimeField.footer
15634                     ]
15635                 }
15636                 ]
15637             }
15638         ]
15639     }
15640 });
15641
15642  
15643
15644  /*
15645  * - LGPL
15646  *
15647  * CheckBox
15648  * 
15649  */
15650
15651 /**
15652  * @class Roo.bootstrap.CheckBox
15653  * @extends Roo.bootstrap.Input
15654  * Bootstrap CheckBox class
15655  * 
15656  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
15657  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
15658  * @cfg {String} boxLabel The text that appears beside the checkbox
15659  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
15660  * @cfg {Boolean} checked initnal the element
15661  * 
15662  * 
15663  * @constructor
15664  * Create a new CheckBox
15665  * @param {Object} config The config object
15666  */
15667
15668 Roo.bootstrap.CheckBox = function(config){
15669     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
15670    
15671         this.addEvents({
15672             /**
15673             * @event check
15674             * Fires when the element is checked or unchecked.
15675             * @param {Roo.bootstrap.CheckBox} this This input
15676             * @param {Boolean} checked The new checked value
15677             */
15678            check : true
15679         });
15680 };
15681
15682 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
15683     
15684     inputType: 'checkbox',
15685     inputValue: 1,
15686     valueOff: 0,
15687     boxLabel: false,
15688     checked: false,
15689     weight : false,
15690     
15691     getAutoCreate : function()
15692     {
15693         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
15694         
15695         var id = Roo.id();
15696         
15697         var cfg = {};
15698         
15699         cfg.cls = 'form-group checkbox' //input-group
15700         
15701         
15702         
15703         
15704         var input =  {
15705             tag: 'input',
15706             id : id,
15707             type : this.inputType,
15708             value : (!this.checked) ? this.valueOff : this.inputValue,
15709             cls : 'roo-checkbox', //'form-box',
15710             placeholder : this.placeholder || ''
15711             
15712         };
15713         
15714         if (this.weight) { // Validity check?
15715             cfg.cls += " checkbox-" + this.weight;
15716         }
15717         
15718         if (this.disabled) {
15719             input.disabled=true;
15720         }
15721         
15722         if(this.checked){
15723             input.checked = this.checked;
15724         }
15725         
15726         if (this.name) {
15727             input.name = this.name;
15728         }
15729         
15730         if (this.size) {
15731             input.cls += ' input-' + this.size;
15732         }
15733         
15734         var settings=this;
15735         ['xs','sm','md','lg'].map(function(size){
15736             if (settings[size]) {
15737                 cfg.cls += ' col-' + size + '-' + settings[size];
15738             }
15739         });
15740         
15741        
15742         
15743         var inputblock = input;
15744         
15745         
15746         
15747         
15748         if (this.before || this.after) {
15749             
15750             inputblock = {
15751                 cls : 'input-group',
15752                 cn :  [] 
15753             };
15754             if (this.before) {
15755                 inputblock.cn.push({
15756                     tag :'span',
15757                     cls : 'input-group-addon',
15758                     html : this.before
15759                 });
15760             }
15761             inputblock.cn.push(input);
15762             if (this.after) {
15763                 inputblock.cn.push({
15764                     tag :'span',
15765                     cls : 'input-group-addon',
15766                     html : this.after
15767                 });
15768             }
15769             
15770         };
15771         
15772         if (align ==='left' && this.fieldLabel.length) {
15773                 Roo.log("left and has label");
15774                 cfg.cn = [
15775                     
15776                     {
15777                         tag: 'label',
15778                         'for' :  id,
15779                         cls : 'control-label col-md-' + this.labelWidth,
15780                         html : this.fieldLabel
15781                         
15782                     },
15783                     {
15784                         cls : "col-md-" + (12 - this.labelWidth), 
15785                         cn: [
15786                             inputblock
15787                         ]
15788                     }
15789                     
15790                 ];
15791         } else if ( this.fieldLabel.length) {
15792                 Roo.log(" label");
15793                 cfg.cn = [
15794                    
15795                     {
15796                         tag: this.boxLabel ? 'span' : 'label',
15797                         'for': id,
15798                         cls: 'control-label box-input-label',
15799                         //cls : 'input-group-addon',
15800                         html : this.fieldLabel
15801                         
15802                     },
15803                     
15804                     inputblock
15805                     
15806                 ];
15807
15808         } else {
15809             
15810                 Roo.log(" no label && no align");
15811                 cfg.cn = [  inputblock ] ;
15812                 
15813                 
15814         };
15815          if(this.boxLabel){
15816             cfg.cn.push( {
15817                 tag: 'label',
15818                 'for': id,
15819                 cls: 'box-label',
15820                 html: this.boxLabel
15821                 
15822             });
15823         }
15824         
15825         
15826        
15827         return cfg;
15828         
15829     },
15830     
15831     /**
15832      * return the real input element.
15833      */
15834     inputEl: function ()
15835     {
15836         return this.el.select('input.roo-checkbox',true).first();
15837     },
15838     
15839     label: function()
15840     {
15841         return this.el.select('label.control-label',true).first();
15842     },
15843     
15844     initEvents : function()
15845     {
15846 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
15847         
15848         this.inputEl().on('click', this.onClick,  this);
15849         
15850     },
15851     
15852     onClick : function()
15853     {   
15854         this.setChecked(!this.checked);
15855     },
15856     
15857     setChecked : function(state,suppressEvent)
15858     {
15859         this.checked = state;
15860         
15861         this.inputEl().dom.checked = state;
15862         
15863         if(suppressEvent !== true){
15864             this.fireEvent('check', this, state);
15865         }
15866         
15867         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
15868         
15869     },
15870     
15871     setValue : function(v,suppressEvent)
15872     {
15873         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
15874     }
15875     
15876 });
15877
15878  
15879 /*
15880  * - LGPL
15881  *
15882  * Radio
15883  * 
15884  */
15885
15886 /**
15887  * @class Roo.bootstrap.Radio
15888  * @extends Roo.bootstrap.CheckBox
15889  * Bootstrap Radio class
15890
15891  * @constructor
15892  * Create a new Radio
15893  * @param {Object} config The config object
15894  */
15895
15896 Roo.bootstrap.Radio = function(config){
15897     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
15898    
15899 };
15900
15901 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
15902     
15903     inputType: 'radio',
15904     inputValue: '',
15905     valueOff: '',
15906     
15907     getAutoCreate : function()
15908     {
15909         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
15910         
15911         var id = Roo.id();
15912         
15913         var cfg = {};
15914         
15915         cfg.cls = 'form-group radio' //input-group
15916         
15917         var input =  {
15918             tag: 'input',
15919             id : id,
15920             type : this.inputType,
15921             value : (!this.checked) ? this.valueOff : this.inputValue,
15922             cls : 'roo-radio',
15923             placeholder : this.placeholder || ''
15924             
15925         };
15926           if (this.weight) { // Validity check?
15927             cfg.cls += " radio-" + this.weight;
15928         }
15929         if (this.disabled) {
15930             input.disabled=true;
15931         }
15932         
15933         if(this.checked){
15934             input.checked = this.checked;
15935         }
15936         
15937         if (this.name) {
15938             input.name = this.name;
15939         }
15940         
15941         if (this.size) {
15942             input.cls += ' input-' + this.size;
15943         }
15944         
15945         var settings=this;
15946         ['xs','sm','md','lg'].map(function(size){
15947             if (settings[size]) {
15948                 cfg.cls += ' col-' + size + '-' + settings[size];
15949             }
15950         });
15951         
15952         var inputblock = input;
15953         
15954         if (this.before || this.after) {
15955             
15956             inputblock = {
15957                 cls : 'input-group',
15958                 cn :  [] 
15959             };
15960             if (this.before) {
15961                 inputblock.cn.push({
15962                     tag :'span',
15963                     cls : 'input-group-addon',
15964                     html : this.before
15965                 });
15966             }
15967             inputblock.cn.push(input);
15968             if (this.after) {
15969                 inputblock.cn.push({
15970                     tag :'span',
15971                     cls : 'input-group-addon',
15972                     html : this.after
15973                 });
15974             }
15975             
15976         };
15977         
15978         if (align ==='left' && this.fieldLabel.length) {
15979                 Roo.log("left and has label");
15980                 cfg.cn = [
15981                     
15982                     {
15983                         tag: 'label',
15984                         'for' :  id,
15985                         cls : 'control-label col-md-' + this.labelWidth,
15986                         html : this.fieldLabel
15987                         
15988                     },
15989                     {
15990                         cls : "col-md-" + (12 - this.labelWidth), 
15991                         cn: [
15992                             inputblock
15993                         ]
15994                     }
15995                     
15996                 ];
15997         } else if ( this.fieldLabel.length) {
15998                 Roo.log(" label");
15999                  cfg.cn = [
16000                    
16001                     {
16002                         tag: 'label',
16003                         'for': id,
16004                         cls: 'control-label box-input-label',
16005                         //cls : 'input-group-addon',
16006                         html : this.fieldLabel
16007                         
16008                     },
16009                     
16010                     inputblock
16011                     
16012                 ];
16013
16014         } else {
16015             
16016                    Roo.log(" no label && no align");
16017                 cfg.cn = [
16018                     
16019                         inputblock
16020                     
16021                 ];
16022                 
16023                 
16024         };
16025         
16026         if(this.boxLabel){
16027             cfg.cn.push({
16028                 tag: 'label',
16029                 'for': id,
16030                 cls: 'box-label',
16031                 html: this.boxLabel
16032             })
16033         }
16034         
16035         return cfg;
16036         
16037     },
16038     inputEl: function ()
16039     {
16040         return this.el.select('input.roo-radio',true).first();
16041     },
16042     onClick : function()
16043     {   
16044         this.setChecked(true);
16045     },
16046     
16047     setChecked : function(state,suppressEvent)
16048     {
16049         if(state){
16050             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
16051                 v.dom.checked = false;
16052             });
16053         }
16054         
16055         this.checked = state;
16056         this.inputEl().dom.checked = state;
16057         
16058         if(suppressEvent !== true){
16059             this.fireEvent('check', this, state);
16060         }
16061         
16062         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
16063         
16064     },
16065     
16066     getGroupValue : function()
16067     {
16068         var value = ''
16069         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
16070             if(v.dom.checked == true){
16071                 value = v.dom.value;
16072             }
16073         });
16074         
16075         return value;
16076     },
16077     
16078     /**
16079      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
16080      * @return {Mixed} value The field value
16081      */
16082     getValue : function(){
16083         return this.getGroupValue();
16084     }
16085     
16086 });
16087
16088  
16089 //<script type="text/javascript">
16090
16091 /*
16092  * Based  Ext JS Library 1.1.1
16093  * Copyright(c) 2006-2007, Ext JS, LLC.
16094  * LGPL
16095  *
16096  */
16097  
16098 /**
16099  * @class Roo.HtmlEditorCore
16100  * @extends Roo.Component
16101  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
16102  *
16103  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
16104  */
16105
16106 Roo.HtmlEditorCore = function(config){
16107     
16108     
16109     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
16110     this.addEvents({
16111         /**
16112          * @event initialize
16113          * Fires when the editor is fully initialized (including the iframe)
16114          * @param {Roo.HtmlEditorCore} this
16115          */
16116         initialize: true,
16117         /**
16118          * @event activate
16119          * Fires when the editor is first receives the focus. Any insertion must wait
16120          * until after this event.
16121          * @param {Roo.HtmlEditorCore} this
16122          */
16123         activate: true,
16124          /**
16125          * @event beforesync
16126          * Fires before the textarea is updated with content from the editor iframe. Return false
16127          * to cancel the sync.
16128          * @param {Roo.HtmlEditorCore} this
16129          * @param {String} html
16130          */
16131         beforesync: true,
16132          /**
16133          * @event beforepush
16134          * Fires before the iframe editor is updated with content from the textarea. Return false
16135          * to cancel the push.
16136          * @param {Roo.HtmlEditorCore} this
16137          * @param {String} html
16138          */
16139         beforepush: true,
16140          /**
16141          * @event sync
16142          * Fires when the textarea is updated with content from the editor iframe.
16143          * @param {Roo.HtmlEditorCore} this
16144          * @param {String} html
16145          */
16146         sync: true,
16147          /**
16148          * @event push
16149          * Fires when the iframe editor is updated with content from the textarea.
16150          * @param {Roo.HtmlEditorCore} this
16151          * @param {String} html
16152          */
16153         push: true,
16154         
16155         /**
16156          * @event editorevent
16157          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
16158          * @param {Roo.HtmlEditorCore} this
16159          */
16160         editorevent: true
16161     });
16162      
16163 };
16164
16165
16166 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
16167
16168
16169      /**
16170      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
16171      */
16172     
16173     owner : false,
16174     
16175      /**
16176      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
16177      *                        Roo.resizable.
16178      */
16179     resizable : false,
16180      /**
16181      * @cfg {Number} height (in pixels)
16182      */   
16183     height: 300,
16184    /**
16185      * @cfg {Number} width (in pixels)
16186      */   
16187     width: 500,
16188     
16189     /**
16190      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
16191      * 
16192      */
16193     stylesheets: false,
16194     
16195     // id of frame..
16196     frameId: false,
16197     
16198     // private properties
16199     validationEvent : false,
16200     deferHeight: true,
16201     initialized : false,
16202     activated : false,
16203     sourceEditMode : false,
16204     onFocus : Roo.emptyFn,
16205     iframePad:3,
16206     hideMode:'offsets',
16207     
16208     clearUp: true,
16209     
16210      
16211     
16212
16213     /**
16214      * Protected method that will not generally be called directly. It
16215      * is called when the editor initializes the iframe with HTML contents. Override this method if you
16216      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
16217      */
16218     getDocMarkup : function(){
16219         // body styles..
16220         var st = '';
16221         Roo.log(this.stylesheets);
16222         
16223         // inherit styels from page...?? 
16224         if (this.stylesheets === false) {
16225             
16226             Roo.get(document.head).select('style').each(function(node) {
16227                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16228             });
16229             
16230             Roo.get(document.head).select('link').each(function(node) { 
16231                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16232             });
16233             
16234         } else if (!this.stylesheets.length) {
16235                 // simple..
16236                 st = '<style type="text/css">' +
16237                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16238                    '</style>';
16239         } else {
16240             Roo.each(this.stylesheets, function(s) {
16241                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
16242             });
16243             
16244         }
16245         
16246         st +=  '<style type="text/css">' +
16247             'IMG { cursor: pointer } ' +
16248         '</style>';
16249
16250         
16251         return '<html><head>' + st  +
16252             //<style type="text/css">' +
16253             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16254             //'</style>' +
16255             ' </head><body class="roo-htmleditor-body"></body></html>';
16256     },
16257
16258     // private
16259     onRender : function(ct, position)
16260     {
16261         var _t = this;
16262         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
16263         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
16264         
16265         
16266         this.el.dom.style.border = '0 none';
16267         this.el.dom.setAttribute('tabIndex', -1);
16268         this.el.addClass('x-hidden hide');
16269         
16270         
16271         
16272         if(Roo.isIE){ // fix IE 1px bogus margin
16273             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
16274         }
16275        
16276         
16277         this.frameId = Roo.id();
16278         
16279          
16280         
16281         var iframe = this.owner.wrap.createChild({
16282             tag: 'iframe',
16283             cls: 'form-control', // bootstrap..
16284             id: this.frameId,
16285             name: this.frameId,
16286             frameBorder : 'no',
16287             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
16288         }, this.el
16289         );
16290         
16291         
16292         this.iframe = iframe.dom;
16293
16294          this.assignDocWin();
16295         
16296         this.doc.designMode = 'on';
16297        
16298         this.doc.open();
16299         this.doc.write(this.getDocMarkup());
16300         this.doc.close();
16301
16302         
16303         var task = { // must defer to wait for browser to be ready
16304             run : function(){
16305                 //console.log("run task?" + this.doc.readyState);
16306                 this.assignDocWin();
16307                 if(this.doc.body || this.doc.readyState == 'complete'){
16308                     try {
16309                         this.doc.designMode="on";
16310                     } catch (e) {
16311                         return;
16312                     }
16313                     Roo.TaskMgr.stop(task);
16314                     this.initEditor.defer(10, this);
16315                 }
16316             },
16317             interval : 10,
16318             duration: 10000,
16319             scope: this
16320         };
16321         Roo.TaskMgr.start(task);
16322
16323         
16324          
16325     },
16326
16327     // private
16328     onResize : function(w, h)
16329     {
16330          Roo.log('resize: ' +w + ',' + h );
16331         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
16332         if(!this.iframe){
16333             return;
16334         }
16335         if(typeof w == 'number'){
16336             
16337             this.iframe.style.width = w + 'px';
16338         }
16339         if(typeof h == 'number'){
16340             
16341             this.iframe.style.height = h + 'px';
16342             if(this.doc){
16343                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
16344             }
16345         }
16346         
16347     },
16348
16349     /**
16350      * Toggles the editor between standard and source edit mode.
16351      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
16352      */
16353     toggleSourceEdit : function(sourceEditMode){
16354         
16355         this.sourceEditMode = sourceEditMode === true;
16356         
16357         if(this.sourceEditMode){
16358  
16359             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
16360             
16361         }else{
16362             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
16363             //this.iframe.className = '';
16364             this.deferFocus();
16365         }
16366         //this.setSize(this.owner.wrap.getSize());
16367         //this.fireEvent('editmodechange', this, this.sourceEditMode);
16368     },
16369
16370     
16371   
16372
16373     /**
16374      * Protected method that will not generally be called directly. If you need/want
16375      * custom HTML cleanup, this is the method you should override.
16376      * @param {String} html The HTML to be cleaned
16377      * return {String} The cleaned HTML
16378      */
16379     cleanHtml : function(html){
16380         html = String(html);
16381         if(html.length > 5){
16382             if(Roo.isSafari){ // strip safari nonsense
16383                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
16384             }
16385         }
16386         if(html == '&nbsp;'){
16387             html = '';
16388         }
16389         return html;
16390     },
16391
16392     /**
16393      * HTML Editor -> Textarea
16394      * Protected method that will not generally be called directly. Syncs the contents
16395      * of the editor iframe with the textarea.
16396      */
16397     syncValue : function(){
16398         if(this.initialized){
16399             var bd = (this.doc.body || this.doc.documentElement);
16400             //this.cleanUpPaste(); -- this is done else where and causes havoc..
16401             var html = bd.innerHTML;
16402             if(Roo.isSafari){
16403                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
16404                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
16405                 if(m && m[1]){
16406                     html = '<div style="'+m[0]+'">' + html + '</div>';
16407                 }
16408             }
16409             html = this.cleanHtml(html);
16410             // fix up the special chars.. normaly like back quotes in word...
16411             // however we do not want to do this with chinese..
16412             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
16413                 var cc = b.charCodeAt();
16414                 if (
16415                     (cc >= 0x4E00 && cc < 0xA000 ) ||
16416                     (cc >= 0x3400 && cc < 0x4E00 ) ||
16417                     (cc >= 0xf900 && cc < 0xfb00 )
16418                 ) {
16419                         return b;
16420                 }
16421                 return "&#"+cc+";" 
16422             });
16423             if(this.owner.fireEvent('beforesync', this, html) !== false){
16424                 this.el.dom.value = html;
16425                 this.owner.fireEvent('sync', this, html);
16426             }
16427         }
16428     },
16429
16430     /**
16431      * Protected method that will not generally be called directly. Pushes the value of the textarea
16432      * into the iframe editor.
16433      */
16434     pushValue : function(){
16435         if(this.initialized){
16436             var v = this.el.dom.value.trim();
16437             
16438 //            if(v.length < 1){
16439 //                v = '&#160;';
16440 //            }
16441             
16442             if(this.owner.fireEvent('beforepush', this, v) !== false){
16443                 var d = (this.doc.body || this.doc.documentElement);
16444                 d.innerHTML = v;
16445                 this.cleanUpPaste();
16446                 this.el.dom.value = d.innerHTML;
16447                 this.owner.fireEvent('push', this, v);
16448             }
16449         }
16450     },
16451
16452     // private
16453     deferFocus : function(){
16454         this.focus.defer(10, this);
16455     },
16456
16457     // doc'ed in Field
16458     focus : function(){
16459         if(this.win && !this.sourceEditMode){
16460             this.win.focus();
16461         }else{
16462             this.el.focus();
16463         }
16464     },
16465     
16466     assignDocWin: function()
16467     {
16468         var iframe = this.iframe;
16469         
16470          if(Roo.isIE){
16471             this.doc = iframe.contentWindow.document;
16472             this.win = iframe.contentWindow;
16473         } else {
16474             if (!Roo.get(this.frameId)) {
16475                 return;
16476             }
16477             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
16478             this.win = Roo.get(this.frameId).dom.contentWindow;
16479         }
16480     },
16481     
16482     // private
16483     initEditor : function(){
16484         //console.log("INIT EDITOR");
16485         this.assignDocWin();
16486         
16487         
16488         
16489         this.doc.designMode="on";
16490         this.doc.open();
16491         this.doc.write(this.getDocMarkup());
16492         this.doc.close();
16493         
16494         var dbody = (this.doc.body || this.doc.documentElement);
16495         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
16496         // this copies styles from the containing element into thsi one..
16497         // not sure why we need all of this..
16498         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
16499         
16500         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
16501         //ss['background-attachment'] = 'fixed'; // w3c
16502         dbody.bgProperties = 'fixed'; // ie
16503         //Roo.DomHelper.applyStyles(dbody, ss);
16504         Roo.EventManager.on(this.doc, {
16505             //'mousedown': this.onEditorEvent,
16506             'mouseup': this.onEditorEvent,
16507             'dblclick': this.onEditorEvent,
16508             'click': this.onEditorEvent,
16509             'keyup': this.onEditorEvent,
16510             buffer:100,
16511             scope: this
16512         });
16513         if(Roo.isGecko){
16514             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
16515         }
16516         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
16517             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
16518         }
16519         this.initialized = true;
16520
16521         this.owner.fireEvent('initialize', this);
16522         this.pushValue();
16523     },
16524
16525     // private
16526     onDestroy : function(){
16527         
16528         
16529         
16530         if(this.rendered){
16531             
16532             //for (var i =0; i < this.toolbars.length;i++) {
16533             //    // fixme - ask toolbars for heights?
16534             //    this.toolbars[i].onDestroy();
16535            // }
16536             
16537             //this.wrap.dom.innerHTML = '';
16538             //this.wrap.remove();
16539         }
16540     },
16541
16542     // private
16543     onFirstFocus : function(){
16544         
16545         this.assignDocWin();
16546         
16547         
16548         this.activated = true;
16549          
16550     
16551         if(Roo.isGecko){ // prevent silly gecko errors
16552             this.win.focus();
16553             var s = this.win.getSelection();
16554             if(!s.focusNode || s.focusNode.nodeType != 3){
16555                 var r = s.getRangeAt(0);
16556                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
16557                 r.collapse(true);
16558                 this.deferFocus();
16559             }
16560             try{
16561                 this.execCmd('useCSS', true);
16562                 this.execCmd('styleWithCSS', false);
16563             }catch(e){}
16564         }
16565         this.owner.fireEvent('activate', this);
16566     },
16567
16568     // private
16569     adjustFont: function(btn){
16570         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
16571         //if(Roo.isSafari){ // safari
16572         //    adjust *= 2;
16573        // }
16574         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
16575         if(Roo.isSafari){ // safari
16576             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
16577             v =  (v < 10) ? 10 : v;
16578             v =  (v > 48) ? 48 : v;
16579             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
16580             
16581         }
16582         
16583         
16584         v = Math.max(1, v+adjust);
16585         
16586         this.execCmd('FontSize', v  );
16587     },
16588
16589     onEditorEvent : function(e){
16590         this.owner.fireEvent('editorevent', this, e);
16591       //  this.updateToolbar();
16592         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
16593     },
16594
16595     insertTag : function(tg)
16596     {
16597         // could be a bit smarter... -> wrap the current selected tRoo..
16598         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
16599             
16600             range = this.createRange(this.getSelection());
16601             var wrappingNode = this.doc.createElement(tg.toLowerCase());
16602             wrappingNode.appendChild(range.extractContents());
16603             range.insertNode(wrappingNode);
16604
16605             return;
16606             
16607             
16608             
16609         }
16610         this.execCmd("formatblock",   tg);
16611         
16612     },
16613     
16614     insertText : function(txt)
16615     {
16616         
16617         
16618         var range = this.createRange();
16619         range.deleteContents();
16620                //alert(Sender.getAttribute('label'));
16621                
16622         range.insertNode(this.doc.createTextNode(txt));
16623     } ,
16624     
16625      
16626
16627     /**
16628      * Executes a Midas editor command on the editor document and performs necessary focus and
16629      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
16630      * @param {String} cmd The Midas command
16631      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16632      */
16633     relayCmd : function(cmd, value){
16634         this.win.focus();
16635         this.execCmd(cmd, value);
16636         this.owner.fireEvent('editorevent', this);
16637         //this.updateToolbar();
16638         this.owner.deferFocus();
16639     },
16640
16641     /**
16642      * Executes a Midas editor command directly on the editor document.
16643      * For visual commands, you should use {@link #relayCmd} instead.
16644      * <b>This should only be called after the editor is initialized.</b>
16645      * @param {String} cmd The Midas command
16646      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16647      */
16648     execCmd : function(cmd, value){
16649         this.doc.execCommand(cmd, false, value === undefined ? null : value);
16650         this.syncValue();
16651     },
16652  
16653  
16654    
16655     /**
16656      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
16657      * to insert tRoo.
16658      * @param {String} text | dom node.. 
16659      */
16660     insertAtCursor : function(text)
16661     {
16662         
16663         
16664         
16665         if(!this.activated){
16666             return;
16667         }
16668         /*
16669         if(Roo.isIE){
16670             this.win.focus();
16671             var r = this.doc.selection.createRange();
16672             if(r){
16673                 r.collapse(true);
16674                 r.pasteHTML(text);
16675                 this.syncValue();
16676                 this.deferFocus();
16677             
16678             }
16679             return;
16680         }
16681         */
16682         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
16683             this.win.focus();
16684             
16685             
16686             // from jquery ui (MIT licenced)
16687             var range, node;
16688             var win = this.win;
16689             
16690             if (win.getSelection && win.getSelection().getRangeAt) {
16691                 range = win.getSelection().getRangeAt(0);
16692                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
16693                 range.insertNode(node);
16694             } else if (win.document.selection && win.document.selection.createRange) {
16695                 // no firefox support
16696                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
16697                 win.document.selection.createRange().pasteHTML(txt);
16698             } else {
16699                 // no firefox support
16700                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
16701                 this.execCmd('InsertHTML', txt);
16702             } 
16703             
16704             this.syncValue();
16705             
16706             this.deferFocus();
16707         }
16708     },
16709  // private
16710     mozKeyPress : function(e){
16711         if(e.ctrlKey){
16712             var c = e.getCharCode(), cmd;
16713           
16714             if(c > 0){
16715                 c = String.fromCharCode(c).toLowerCase();
16716                 switch(c){
16717                     case 'b':
16718                         cmd = 'bold';
16719                         break;
16720                     case 'i':
16721                         cmd = 'italic';
16722                         break;
16723                     
16724                     case 'u':
16725                         cmd = 'underline';
16726                         break;
16727                     
16728                     case 'v':
16729                         this.cleanUpPaste.defer(100, this);
16730                         return;
16731                         
16732                 }
16733                 if(cmd){
16734                     this.win.focus();
16735                     this.execCmd(cmd);
16736                     this.deferFocus();
16737                     e.preventDefault();
16738                 }
16739                 
16740             }
16741         }
16742     },
16743
16744     // private
16745     fixKeys : function(){ // load time branching for fastest keydown performance
16746         if(Roo.isIE){
16747             return function(e){
16748                 var k = e.getKey(), r;
16749                 if(k == e.TAB){
16750                     e.stopEvent();
16751                     r = this.doc.selection.createRange();
16752                     if(r){
16753                         r.collapse(true);
16754                         r.pasteHTML('&#160;&#160;&#160;&#160;');
16755                         this.deferFocus();
16756                     }
16757                     return;
16758                 }
16759                 
16760                 if(k == e.ENTER){
16761                     r = this.doc.selection.createRange();
16762                     if(r){
16763                         var target = r.parentElement();
16764                         if(!target || target.tagName.toLowerCase() != 'li'){
16765                             e.stopEvent();
16766                             r.pasteHTML('<br />');
16767                             r.collapse(false);
16768                             r.select();
16769                         }
16770                     }
16771                 }
16772                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
16773                     this.cleanUpPaste.defer(100, this);
16774                     return;
16775                 }
16776                 
16777                 
16778             };
16779         }else if(Roo.isOpera){
16780             return function(e){
16781                 var k = e.getKey();
16782                 if(k == e.TAB){
16783                     e.stopEvent();
16784                     this.win.focus();
16785                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
16786                     this.deferFocus();
16787                 }
16788                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
16789                     this.cleanUpPaste.defer(100, this);
16790                     return;
16791                 }
16792                 
16793             };
16794         }else if(Roo.isSafari){
16795             return function(e){
16796                 var k = e.getKey();
16797                 
16798                 if(k == e.TAB){
16799                     e.stopEvent();
16800                     this.execCmd('InsertText','\t');
16801                     this.deferFocus();
16802                     return;
16803                 }
16804                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
16805                     this.cleanUpPaste.defer(100, this);
16806                     return;
16807                 }
16808                 
16809              };
16810         }
16811     }(),
16812     
16813     getAllAncestors: function()
16814     {
16815         var p = this.getSelectedNode();
16816         var a = [];
16817         if (!p) {
16818             a.push(p); // push blank onto stack..
16819             p = this.getParentElement();
16820         }
16821         
16822         
16823         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
16824             a.push(p);
16825             p = p.parentNode;
16826         }
16827         a.push(this.doc.body);
16828         return a;
16829     },
16830     lastSel : false,
16831     lastSelNode : false,
16832     
16833     
16834     getSelection : function() 
16835     {
16836         this.assignDocWin();
16837         return Roo.isIE ? this.doc.selection : this.win.getSelection();
16838     },
16839     
16840     getSelectedNode: function() 
16841     {
16842         // this may only work on Gecko!!!
16843         
16844         // should we cache this!!!!
16845         
16846         
16847         
16848          
16849         var range = this.createRange(this.getSelection()).cloneRange();
16850         
16851         if (Roo.isIE) {
16852             var parent = range.parentElement();
16853             while (true) {
16854                 var testRange = range.duplicate();
16855                 testRange.moveToElementText(parent);
16856                 if (testRange.inRange(range)) {
16857                     break;
16858                 }
16859                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
16860                     break;
16861                 }
16862                 parent = parent.parentElement;
16863             }
16864             return parent;
16865         }
16866         
16867         // is ancestor a text element.
16868         var ac =  range.commonAncestorContainer;
16869         if (ac.nodeType == 3) {
16870             ac = ac.parentNode;
16871         }
16872         
16873         var ar = ac.childNodes;
16874          
16875         var nodes = [];
16876         var other_nodes = [];
16877         var has_other_nodes = false;
16878         for (var i=0;i<ar.length;i++) {
16879             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
16880                 continue;
16881             }
16882             // fullly contained node.
16883             
16884             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
16885                 nodes.push(ar[i]);
16886                 continue;
16887             }
16888             
16889             // probably selected..
16890             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
16891                 other_nodes.push(ar[i]);
16892                 continue;
16893             }
16894             // outer..
16895             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
16896                 continue;
16897             }
16898             
16899             
16900             has_other_nodes = true;
16901         }
16902         if (!nodes.length && other_nodes.length) {
16903             nodes= other_nodes;
16904         }
16905         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
16906             return false;
16907         }
16908         
16909         return nodes[0];
16910     },
16911     createRange: function(sel)
16912     {
16913         // this has strange effects when using with 
16914         // top toolbar - not sure if it's a great idea.
16915         //this.editor.contentWindow.focus();
16916         if (typeof sel != "undefined") {
16917             try {
16918                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
16919             } catch(e) {
16920                 return this.doc.createRange();
16921             }
16922         } else {
16923             return this.doc.createRange();
16924         }
16925     },
16926     getParentElement: function()
16927     {
16928         
16929         this.assignDocWin();
16930         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
16931         
16932         var range = this.createRange(sel);
16933          
16934         try {
16935             var p = range.commonAncestorContainer;
16936             while (p.nodeType == 3) { // text node
16937                 p = p.parentNode;
16938             }
16939             return p;
16940         } catch (e) {
16941             return null;
16942         }
16943     
16944     },
16945     /***
16946      *
16947      * Range intersection.. the hard stuff...
16948      *  '-1' = before
16949      *  '0' = hits..
16950      *  '1' = after.
16951      *         [ -- selected range --- ]
16952      *   [fail]                        [fail]
16953      *
16954      *    basically..
16955      *      if end is before start or  hits it. fail.
16956      *      if start is after end or hits it fail.
16957      *
16958      *   if either hits (but other is outside. - then it's not 
16959      *   
16960      *    
16961      **/
16962     
16963     
16964     // @see http://www.thismuchiknow.co.uk/?p=64.
16965     rangeIntersectsNode : function(range, node)
16966     {
16967         var nodeRange = node.ownerDocument.createRange();
16968         try {
16969             nodeRange.selectNode(node);
16970         } catch (e) {
16971             nodeRange.selectNodeContents(node);
16972         }
16973     
16974         var rangeStartRange = range.cloneRange();
16975         rangeStartRange.collapse(true);
16976     
16977         var rangeEndRange = range.cloneRange();
16978         rangeEndRange.collapse(false);
16979     
16980         var nodeStartRange = nodeRange.cloneRange();
16981         nodeStartRange.collapse(true);
16982     
16983         var nodeEndRange = nodeRange.cloneRange();
16984         nodeEndRange.collapse(false);
16985     
16986         return rangeStartRange.compareBoundaryPoints(
16987                  Range.START_TO_START, nodeEndRange) == -1 &&
16988                rangeEndRange.compareBoundaryPoints(
16989                  Range.START_TO_START, nodeStartRange) == 1;
16990         
16991          
16992     },
16993     rangeCompareNode : function(range, node)
16994     {
16995         var nodeRange = node.ownerDocument.createRange();
16996         try {
16997             nodeRange.selectNode(node);
16998         } catch (e) {
16999             nodeRange.selectNodeContents(node);
17000         }
17001         
17002         
17003         range.collapse(true);
17004     
17005         nodeRange.collapse(true);
17006      
17007         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
17008         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
17009          
17010         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
17011         
17012         var nodeIsBefore   =  ss == 1;
17013         var nodeIsAfter    = ee == -1;
17014         
17015         if (nodeIsBefore && nodeIsAfter)
17016             return 0; // outer
17017         if (!nodeIsBefore && nodeIsAfter)
17018             return 1; //right trailed.
17019         
17020         if (nodeIsBefore && !nodeIsAfter)
17021             return 2;  // left trailed.
17022         // fully contined.
17023         return 3;
17024     },
17025
17026     // private? - in a new class?
17027     cleanUpPaste :  function()
17028     {
17029         // cleans up the whole document..
17030         Roo.log('cleanuppaste');
17031         
17032         this.cleanUpChildren(this.doc.body);
17033         var clean = this.cleanWordChars(this.doc.body.innerHTML);
17034         if (clean != this.doc.body.innerHTML) {
17035             this.doc.body.innerHTML = clean;
17036         }
17037         
17038     },
17039     
17040     cleanWordChars : function(input) {// change the chars to hex code
17041         var he = Roo.HtmlEditorCore;
17042         
17043         var output = input;
17044         Roo.each(he.swapCodes, function(sw) { 
17045             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
17046             
17047             output = output.replace(swapper, sw[1]);
17048         });
17049         
17050         return output;
17051     },
17052     
17053     
17054     cleanUpChildren : function (n)
17055     {
17056         if (!n.childNodes.length) {
17057             return;
17058         }
17059         for (var i = n.childNodes.length-1; i > -1 ; i--) {
17060            this.cleanUpChild(n.childNodes[i]);
17061         }
17062     },
17063     
17064     
17065         
17066     
17067     cleanUpChild : function (node)
17068     {
17069         var ed = this;
17070         //console.log(node);
17071         if (node.nodeName == "#text") {
17072             // clean up silly Windows -- stuff?
17073             return; 
17074         }
17075         if (node.nodeName == "#comment") {
17076             node.parentNode.removeChild(node);
17077             // clean up silly Windows -- stuff?
17078             return; 
17079         }
17080         
17081         if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1 && this.clearUp) {
17082             // remove node.
17083             node.parentNode.removeChild(node);
17084             return;
17085             
17086         }
17087         
17088         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
17089         
17090         // remove <a name=....> as rendering on yahoo mailer is borked with this.
17091         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
17092         
17093         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
17094         //    remove_keep_children = true;
17095         //}
17096         
17097         if (remove_keep_children) {
17098             this.cleanUpChildren(node);
17099             // inserts everything just before this node...
17100             while (node.childNodes.length) {
17101                 var cn = node.childNodes[0];
17102                 node.removeChild(cn);
17103                 node.parentNode.insertBefore(cn, node);
17104             }
17105             node.parentNode.removeChild(node);
17106             return;
17107         }
17108         
17109         if (!node.attributes || !node.attributes.length) {
17110             this.cleanUpChildren(node);
17111             return;
17112         }
17113         
17114         function cleanAttr(n,v)
17115         {
17116             
17117             if (v.match(/^\./) || v.match(/^\//)) {
17118                 return;
17119             }
17120             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
17121                 return;
17122             }
17123             if (v.match(/^#/)) {
17124                 return;
17125             }
17126 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
17127             node.removeAttribute(n);
17128             
17129         }
17130         
17131         function cleanStyle(n,v)
17132         {
17133             if (v.match(/expression/)) { //XSS?? should we even bother..
17134                 node.removeAttribute(n);
17135                 return;
17136             }
17137             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
17138             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
17139             
17140             
17141             var parts = v.split(/;/);
17142             var clean = [];
17143             
17144             Roo.each(parts, function(p) {
17145                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
17146                 if (!p.length) {
17147                     return true;
17148                 }
17149                 var l = p.split(':').shift().replace(/\s+/g,'');
17150                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
17151                 
17152                 if ( cblack.indexOf(l) > -1) {
17153 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17154                     //node.removeAttribute(n);
17155                     return true;
17156                 }
17157                 //Roo.log()
17158                 // only allow 'c whitelisted system attributes'
17159                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
17160 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17161                     //node.removeAttribute(n);
17162                     return true;
17163                 }
17164                 
17165                 
17166                  
17167                 
17168                 clean.push(p);
17169                 return true;
17170             });
17171             if (clean.length) { 
17172                 node.setAttribute(n, clean.join(';'));
17173             } else {
17174                 node.removeAttribute(n);
17175             }
17176             
17177         }
17178         
17179         
17180         for (var i = node.attributes.length-1; i > -1 ; i--) {
17181             var a = node.attributes[i];
17182             //console.log(a);
17183             
17184             if (a.name.toLowerCase().substr(0,2)=='on')  {
17185                 node.removeAttribute(a.name);
17186                 continue;
17187             }
17188             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
17189                 node.removeAttribute(a.name);
17190                 continue;
17191             }
17192             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
17193                 cleanAttr(a.name,a.value); // fixme..
17194                 continue;
17195             }
17196             if (a.name == 'style') {
17197                 cleanStyle(a.name,a.value);
17198                 continue;
17199             }
17200             /// clean up MS crap..
17201             // tecnically this should be a list of valid class'es..
17202             
17203             
17204             if (a.name == 'class') {
17205                 if (a.value.match(/^Mso/)) {
17206                     node.className = '';
17207                 }
17208                 
17209                 if (a.value.match(/body/)) {
17210                     node.className = '';
17211                 }
17212                 continue;
17213             }
17214             
17215             // style cleanup!?
17216             // class cleanup?
17217             
17218         }
17219         
17220         
17221         this.cleanUpChildren(node);
17222         
17223         
17224     },
17225     /**
17226      * Clean up MS wordisms...
17227      */
17228     cleanWord : function(node)
17229     {
17230         var _t = this;
17231         var cleanWordChildren = function()
17232         {
17233             if (!node.childNodes.length) {
17234                 return;
17235             }
17236             for (var i = node.childNodes.length-1; i > -1 ; i--) {
17237                _t.cleanWord(node.childNodes[i]);
17238             }
17239         }
17240         
17241         
17242         if (!node) {
17243             this.cleanWord(this.doc.body);
17244             return;
17245         }
17246         if (node.nodeName == "#text") {
17247             // clean up silly Windows -- stuff?
17248             return; 
17249         }
17250         if (node.nodeName == "#comment") {
17251             node.parentNode.removeChild(node);
17252             // clean up silly Windows -- stuff?
17253             return; 
17254         }
17255         
17256         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
17257             node.parentNode.removeChild(node);
17258             return;
17259         }
17260         
17261         // remove - but keep children..
17262         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
17263             while (node.childNodes.length) {
17264                 var cn = node.childNodes[0];
17265                 node.removeChild(cn);
17266                 node.parentNode.insertBefore(cn, node);
17267             }
17268             node.parentNode.removeChild(node);
17269             cleanWordChildren();
17270             return;
17271         }
17272         // clean styles
17273         if (node.className.length) {
17274             
17275             var cn = node.className.split(/\W+/);
17276             var cna = [];
17277             Roo.each(cn, function(cls) {
17278                 if (cls.match(/Mso[a-zA-Z]+/)) {
17279                     return;
17280                 }
17281                 cna.push(cls);
17282             });
17283             node.className = cna.length ? cna.join(' ') : '';
17284             if (!cna.length) {
17285                 node.removeAttribute("class");
17286             }
17287         }
17288         
17289         if (node.hasAttribute("lang")) {
17290             node.removeAttribute("lang");
17291         }
17292         
17293         if (node.hasAttribute("style")) {
17294             
17295             var styles = node.getAttribute("style").split(";");
17296             var nstyle = [];
17297             Roo.each(styles, function(s) {
17298                 if (!s.match(/:/)) {
17299                     return;
17300                 }
17301                 var kv = s.split(":");
17302                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
17303                     return;
17304                 }
17305                 // what ever is left... we allow.
17306                 nstyle.push(s);
17307             });
17308             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
17309             if (!nstyle.length) {
17310                 node.removeAttribute('style');
17311             }
17312         }
17313         
17314         cleanWordChildren();
17315         
17316         
17317     },
17318     domToHTML : function(currentElement, depth, nopadtext) {
17319         
17320             depth = depth || 0;
17321             nopadtext = nopadtext || false;
17322         
17323             if (!currentElement) {
17324                 return this.domToHTML(this.doc.body);
17325             }
17326             
17327             //Roo.log(currentElement);
17328             var j;
17329             var allText = false;
17330             var nodeName = currentElement.nodeName;
17331             var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
17332             
17333             if  (nodeName == '#text') {
17334                 return currentElement.nodeValue;
17335             }
17336             
17337             
17338             var ret = '';
17339             if (nodeName != 'BODY') {
17340                  
17341                 var i = 0;
17342                 // Prints the node tagName, such as <A>, <IMG>, etc
17343                 if (tagName) {
17344                     var attr = [];
17345                     for(i = 0; i < currentElement.attributes.length;i++) {
17346                         // quoting?
17347                         var aname = currentElement.attributes.item(i).name;
17348                         if (!currentElement.attributes.item(i).value.length) {
17349                             continue;
17350                         }
17351                         attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
17352                     }
17353                     
17354                     ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
17355                 } 
17356                 else {
17357                     
17358                     // eack
17359                 }
17360             } else {
17361                 tagName = false;
17362             }
17363             if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
17364                 return ret;
17365             }
17366             if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
17367                 nopadtext = true;
17368             }
17369             
17370             
17371             // Traverse the tree
17372             i = 0;
17373             var currentElementChild = currentElement.childNodes.item(i);
17374             var allText = true;
17375             var innerHTML  = '';
17376             lastnode = '';
17377             while (currentElementChild) {
17378                 // Formatting code (indent the tree so it looks nice on the screen)
17379                 var nopad = nopadtext;
17380                 if (lastnode == 'SPAN') {
17381                     nopad  = true;
17382                 }
17383                 // text
17384                 if  (currentElementChild.nodeName == '#text') {
17385                     var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
17386                     if (!nopad && toadd.length > 80) {
17387                         innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
17388                     }
17389                     innerHTML  += toadd;
17390                     
17391                     i++;
17392                     currentElementChild = currentElement.childNodes.item(i);
17393                     lastNode = '';
17394                     continue;
17395                 }
17396                 allText = false;
17397                 
17398                 innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
17399                     
17400                 // Recursively traverse the tree structure of the child node
17401                 innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
17402                 lastnode = currentElementChild.nodeName;
17403                 i++;
17404                 currentElementChild=currentElement.childNodes.item(i);
17405             }
17406             
17407             ret += innerHTML;
17408             
17409             if (!allText) {
17410                     // The remaining code is mostly for formatting the tree
17411                 ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
17412             }
17413             
17414             
17415             if (tagName) {
17416                 ret+= "</"+tagName+">";
17417             }
17418             return ret;
17419             
17420         }
17421     
17422     // hide stuff that is not compatible
17423     /**
17424      * @event blur
17425      * @hide
17426      */
17427     /**
17428      * @event change
17429      * @hide
17430      */
17431     /**
17432      * @event focus
17433      * @hide
17434      */
17435     /**
17436      * @event specialkey
17437      * @hide
17438      */
17439     /**
17440      * @cfg {String} fieldClass @hide
17441      */
17442     /**
17443      * @cfg {String} focusClass @hide
17444      */
17445     /**
17446      * @cfg {String} autoCreate @hide
17447      */
17448     /**
17449      * @cfg {String} inputType @hide
17450      */
17451     /**
17452      * @cfg {String} invalidClass @hide
17453      */
17454     /**
17455      * @cfg {String} invalidText @hide
17456      */
17457     /**
17458      * @cfg {String} msgFx @hide
17459      */
17460     /**
17461      * @cfg {String} validateOnBlur @hide
17462      */
17463 });
17464
17465 Roo.HtmlEditorCore.white = [
17466         'area', 'br', 'img', 'input', 'hr', 'wbr',
17467         
17468        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
17469        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
17470        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
17471        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
17472        'table',   'ul',         'xmp', 
17473        
17474        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
17475       'thead',   'tr', 
17476      
17477       'dir', 'menu', 'ol', 'ul', 'dl',
17478        
17479       'embed',  'object'
17480 ];
17481
17482
17483 Roo.HtmlEditorCore.black = [
17484     //    'embed',  'object', // enable - backend responsiblity to clean thiese
17485         'applet', // 
17486         'base',   'basefont', 'bgsound', 'blink',  'body', 
17487         'frame',  'frameset', 'head',    'html',   'ilayer', 
17488         'iframe', 'layer',  'link',     'meta',    'object',   
17489         'script', 'style' ,'title',  'xml' // clean later..
17490 ];
17491 Roo.HtmlEditorCore.clean = [
17492     'script', 'style', 'title', 'xml'
17493 ];
17494 Roo.HtmlEditorCore.remove = [
17495     'font'
17496 ];
17497 // attributes..
17498
17499 Roo.HtmlEditorCore.ablack = [
17500     'on'
17501 ];
17502     
17503 Roo.HtmlEditorCore.aclean = [ 
17504     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
17505 ];
17506
17507 // protocols..
17508 Roo.HtmlEditorCore.pwhite= [
17509         'http',  'https',  'mailto'
17510 ];
17511
17512 // white listed style attributes.
17513 Roo.HtmlEditorCore.cwhite= [
17514       //  'text-align', /// default is to allow most things..
17515       
17516          
17517 //        'font-size'//??
17518 ];
17519
17520 // black listed style attributes.
17521 Roo.HtmlEditorCore.cblack= [
17522       //  'font-size' -- this can be set by the project 
17523 ];
17524
17525
17526 Roo.HtmlEditorCore.swapCodes   =[ 
17527     [    8211, "--" ], 
17528     [    8212, "--" ], 
17529     [    8216,  "'" ],  
17530     [    8217, "'" ],  
17531     [    8220, '"' ],  
17532     [    8221, '"' ],  
17533     [    8226, "*" ],  
17534     [    8230, "..." ]
17535 ]; 
17536
17537     /*
17538  * - LGPL
17539  *
17540  * HtmlEditor
17541  * 
17542  */
17543
17544 /**
17545  * @class Roo.bootstrap.HtmlEditor
17546  * @extends Roo.bootstrap.TextArea
17547  * Bootstrap HtmlEditor class
17548
17549  * @constructor
17550  * Create a new HtmlEditor
17551  * @param {Object} config The config object
17552  */
17553
17554 Roo.bootstrap.HtmlEditor = function(config){
17555     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
17556     if (!this.toolbars) {
17557         this.toolbars = [];
17558     }
17559     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
17560     this.addEvents({
17561             /**
17562              * @event initialize
17563              * Fires when the editor is fully initialized (including the iframe)
17564              * @param {HtmlEditor} this
17565              */
17566             initialize: true,
17567             /**
17568              * @event activate
17569              * Fires when the editor is first receives the focus. Any insertion must wait
17570              * until after this event.
17571              * @param {HtmlEditor} this
17572              */
17573             activate: true,
17574              /**
17575              * @event beforesync
17576              * Fires before the textarea is updated with content from the editor iframe. Return false
17577              * to cancel the sync.
17578              * @param {HtmlEditor} this
17579              * @param {String} html
17580              */
17581             beforesync: true,
17582              /**
17583              * @event beforepush
17584              * Fires before the iframe editor is updated with content from the textarea. Return false
17585              * to cancel the push.
17586              * @param {HtmlEditor} this
17587              * @param {String} html
17588              */
17589             beforepush: true,
17590              /**
17591              * @event sync
17592              * Fires when the textarea is updated with content from the editor iframe.
17593              * @param {HtmlEditor} this
17594              * @param {String} html
17595              */
17596             sync: true,
17597              /**
17598              * @event push
17599              * Fires when the iframe editor is updated with content from the textarea.
17600              * @param {HtmlEditor} this
17601              * @param {String} html
17602              */
17603             push: true,
17604              /**
17605              * @event editmodechange
17606              * Fires when the editor switches edit modes
17607              * @param {HtmlEditor} this
17608              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
17609              */
17610             editmodechange: true,
17611             /**
17612              * @event editorevent
17613              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
17614              * @param {HtmlEditor} this
17615              */
17616             editorevent: true,
17617             /**
17618              * @event firstfocus
17619              * Fires when on first focus - needed by toolbars..
17620              * @param {HtmlEditor} this
17621              */
17622             firstfocus: true,
17623             /**
17624              * @event autosave
17625              * Auto save the htmlEditor value as a file into Events
17626              * @param {HtmlEditor} this
17627              */
17628             autosave: true,
17629             /**
17630              * @event savedpreview
17631              * preview the saved version of htmlEditor
17632              * @param {HtmlEditor} this
17633              */
17634             savedpreview: true
17635         });
17636 };
17637
17638
17639 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
17640     
17641     
17642       /**
17643      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
17644      */
17645     toolbars : false,
17646    
17647      /**
17648      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
17649      *                        Roo.resizable.
17650      */
17651     resizable : false,
17652      /**
17653      * @cfg {Number} height (in pixels)
17654      */   
17655     height: 300,
17656    /**
17657      * @cfg {Number} width (in pixels)
17658      */   
17659     width: false,
17660     
17661     /**
17662      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
17663      * 
17664      */
17665     stylesheets: false,
17666     
17667     // id of frame..
17668     frameId: false,
17669     
17670     // private properties
17671     validationEvent : false,
17672     deferHeight: true,
17673     initialized : false,
17674     activated : false,
17675     
17676     onFocus : Roo.emptyFn,
17677     iframePad:3,
17678     hideMode:'offsets',
17679     
17680     
17681     tbContainer : false,
17682     
17683     toolbarContainer :function() {
17684         return this.wrap.select('.x-html-editor-tb',true).first();
17685     },
17686
17687     /**
17688      * Protected method that will not generally be called directly. It
17689      * is called when the editor creates its toolbar. Override this method if you need to
17690      * add custom toolbar buttons.
17691      * @param {HtmlEditor} editor
17692      */
17693     createToolbar : function(){
17694         
17695         Roo.log("create toolbars");
17696         
17697         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
17698         this.toolbars[0].render(this.toolbarContainer());
17699         
17700         return;
17701         
17702 //        if (!editor.toolbars || !editor.toolbars.length) {
17703 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
17704 //        }
17705 //        
17706 //        for (var i =0 ; i < editor.toolbars.length;i++) {
17707 //            editor.toolbars[i] = Roo.factory(
17708 //                    typeof(editor.toolbars[i]) == 'string' ?
17709 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
17710 //                Roo.bootstrap.HtmlEditor);
17711 //            editor.toolbars[i].init(editor);
17712 //        }
17713     },
17714
17715      
17716     // private
17717     onRender : function(ct, position)
17718     {
17719        // Roo.log("Call onRender: " + this.xtype);
17720         var _t = this;
17721         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
17722       
17723         this.wrap = this.inputEl().wrap({
17724             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
17725         });
17726         
17727         this.editorcore.onRender(ct, position);
17728          
17729         if (this.resizable) {
17730             this.resizeEl = new Roo.Resizable(this.wrap, {
17731                 pinned : true,
17732                 wrap: true,
17733                 dynamic : true,
17734                 minHeight : this.height,
17735                 height: this.height,
17736                 handles : this.resizable,
17737                 width: this.width,
17738                 listeners : {
17739                     resize : function(r, w, h) {
17740                         _t.onResize(w,h); // -something
17741                     }
17742                 }
17743             });
17744             
17745         }
17746         this.createToolbar(this);
17747        
17748         
17749         if(!this.width && this.resizable){
17750             this.setSize(this.wrap.getSize());
17751         }
17752         if (this.resizeEl) {
17753             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
17754             // should trigger onReize..
17755         }
17756         
17757     },
17758
17759     // private
17760     onResize : function(w, h)
17761     {
17762         Roo.log('resize: ' +w + ',' + h );
17763         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
17764         var ew = false;
17765         var eh = false;
17766         
17767         if(this.inputEl() ){
17768             if(typeof w == 'number'){
17769                 var aw = w - this.wrap.getFrameWidth('lr');
17770                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
17771                 ew = aw;
17772             }
17773             if(typeof h == 'number'){
17774                  var tbh = -11;  // fixme it needs to tool bar size!
17775                 for (var i =0; i < this.toolbars.length;i++) {
17776                     // fixme - ask toolbars for heights?
17777                     tbh += this.toolbars[i].el.getHeight();
17778                     //if (this.toolbars[i].footer) {
17779                     //    tbh += this.toolbars[i].footer.el.getHeight();
17780                     //}
17781                 }
17782               
17783                 
17784                 
17785                 
17786                 
17787                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
17788                 ah -= 5; // knock a few pixes off for look..
17789                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
17790                 var eh = ah;
17791             }
17792         }
17793         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
17794         this.editorcore.onResize(ew,eh);
17795         
17796     },
17797
17798     /**
17799      * Toggles the editor between standard and source edit mode.
17800      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
17801      */
17802     toggleSourceEdit : function(sourceEditMode)
17803     {
17804         this.editorcore.toggleSourceEdit(sourceEditMode);
17805         
17806         if(this.editorcore.sourceEditMode){
17807             Roo.log('editor - showing textarea');
17808             
17809 //            Roo.log('in');
17810 //            Roo.log(this.syncValue());
17811             this.syncValue();
17812             this.inputEl().removeClass(['hide', 'x-hidden']);
17813             this.inputEl().dom.removeAttribute('tabIndex');
17814             this.inputEl().focus();
17815         }else{
17816             Roo.log('editor - hiding textarea');
17817 //            Roo.log('out')
17818 //            Roo.log(this.pushValue()); 
17819             this.pushValue();
17820             
17821             this.inputEl().addClass(['hide', 'x-hidden']);
17822             this.inputEl().dom.setAttribute('tabIndex', -1);
17823             //this.deferFocus();
17824         }
17825          
17826         if(this.resizable){
17827             this.setSize(this.wrap.getSize());
17828         }
17829         
17830         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
17831     },
17832  
17833     // private (for BoxComponent)
17834     adjustSize : Roo.BoxComponent.prototype.adjustSize,
17835
17836     // private (for BoxComponent)
17837     getResizeEl : function(){
17838         return this.wrap;
17839     },
17840
17841     // private (for BoxComponent)
17842     getPositionEl : function(){
17843         return this.wrap;
17844     },
17845
17846     // private
17847     initEvents : function(){
17848         this.originalValue = this.getValue();
17849     },
17850
17851 //    /**
17852 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
17853 //     * @method
17854 //     */
17855 //    markInvalid : Roo.emptyFn,
17856 //    /**
17857 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
17858 //     * @method
17859 //     */
17860 //    clearInvalid : Roo.emptyFn,
17861
17862     setValue : function(v){
17863         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
17864         this.editorcore.pushValue();
17865     },
17866
17867      
17868     // private
17869     deferFocus : function(){
17870         this.focus.defer(10, this);
17871     },
17872
17873     // doc'ed in Field
17874     focus : function(){
17875         this.editorcore.focus();
17876         
17877     },
17878       
17879
17880     // private
17881     onDestroy : function(){
17882         
17883         
17884         
17885         if(this.rendered){
17886             
17887             for (var i =0; i < this.toolbars.length;i++) {
17888                 // fixme - ask toolbars for heights?
17889                 this.toolbars[i].onDestroy();
17890             }
17891             
17892             this.wrap.dom.innerHTML = '';
17893             this.wrap.remove();
17894         }
17895     },
17896
17897     // private
17898     onFirstFocus : function(){
17899         //Roo.log("onFirstFocus");
17900         this.editorcore.onFirstFocus();
17901          for (var i =0; i < this.toolbars.length;i++) {
17902             this.toolbars[i].onFirstFocus();
17903         }
17904         
17905     },
17906     
17907     // private
17908     syncValue : function()
17909     {   
17910         this.editorcore.syncValue();
17911     },
17912     
17913     pushValue : function()
17914     {   
17915         this.editorcore.pushValue();
17916     }
17917      
17918     
17919     // hide stuff that is not compatible
17920     /**
17921      * @event blur
17922      * @hide
17923      */
17924     /**
17925      * @event change
17926      * @hide
17927      */
17928     /**
17929      * @event focus
17930      * @hide
17931      */
17932     /**
17933      * @event specialkey
17934      * @hide
17935      */
17936     /**
17937      * @cfg {String} fieldClass @hide
17938      */
17939     /**
17940      * @cfg {String} focusClass @hide
17941      */
17942     /**
17943      * @cfg {String} autoCreate @hide
17944      */
17945     /**
17946      * @cfg {String} inputType @hide
17947      */
17948     /**
17949      * @cfg {String} invalidClass @hide
17950      */
17951     /**
17952      * @cfg {String} invalidText @hide
17953      */
17954     /**
17955      * @cfg {String} msgFx @hide
17956      */
17957     /**
17958      * @cfg {String} validateOnBlur @hide
17959      */
17960 });
17961  
17962     
17963    
17964    
17965    
17966       
17967 Roo.namespace('Roo.bootstrap.htmleditor');
17968 /**
17969  * @class Roo.bootstrap.HtmlEditorToolbar1
17970  * Basic Toolbar
17971  * 
17972  * Usage:
17973  *
17974  new Roo.bootstrap.HtmlEditor({
17975     ....
17976     toolbars : [
17977         new Roo.bootstrap.HtmlEditorToolbar1({
17978             disable : { fonts: 1 , format: 1, ..., ... , ...],
17979             btns : [ .... ]
17980         })
17981     }
17982      
17983  * 
17984  * @cfg {Object} disable List of elements to disable..
17985  * @cfg {Array} btns List of additional buttons.
17986  * 
17987  * 
17988  * NEEDS Extra CSS? 
17989  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
17990  */
17991  
17992 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
17993 {
17994     
17995     Roo.apply(this, config);
17996     
17997     // default disabled, based on 'good practice'..
17998     this.disable = this.disable || {};
17999     Roo.applyIf(this.disable, {
18000         fontSize : true,
18001         colors : true,
18002         specialElements : true
18003     });
18004     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
18005     
18006     this.editor = config.editor;
18007     this.editorcore = config.editor.editorcore;
18008     
18009     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
18010     
18011     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
18012     // dont call parent... till later.
18013 }
18014 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
18015      
18016     bar : true,
18017     
18018     editor : false,
18019     editorcore : false,
18020     
18021     
18022     formats : [
18023         "p" ,  
18024         "h1","h2","h3","h4","h5","h6", 
18025         "pre", "code", 
18026         "abbr", "acronym", "address", "cite", "samp", "var",
18027         'div','span'
18028     ],
18029     
18030     onRender : function(ct, position)
18031     {
18032        // Roo.log("Call onRender: " + this.xtype);
18033         
18034        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
18035        Roo.log(this.el);
18036        this.el.dom.style.marginBottom = '0';
18037        var _this = this;
18038        var editorcore = this.editorcore;
18039        var editor= this.editor;
18040        
18041        var children = [];
18042        var btn = function(id,cmd , toggle, handler){
18043        
18044             var  event = toggle ? 'toggle' : 'click';
18045        
18046             var a = {
18047                 size : 'sm',
18048                 xtype: 'Button',
18049                 xns: Roo.bootstrap,
18050                 glyphicon : id,
18051                 cmd : id || cmd,
18052                 enableToggle:toggle !== false,
18053                 //html : 'submit'
18054                 pressed : toggle ? false : null,
18055                 listeners : {}
18056             }
18057             a.listeners[toggle ? 'toggle' : 'click'] = function() {
18058                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
18059             }
18060             children.push(a);
18061             return a;
18062        }
18063         
18064         var style = {
18065                 xtype: 'Button',
18066                 size : 'sm',
18067                 xns: Roo.bootstrap,
18068                 glyphicon : 'font',
18069                 //html : 'submit'
18070                 menu : {
18071                     xtype: 'Menu',
18072                     xns: Roo.bootstrap,
18073                     items:  []
18074                 }
18075         };
18076         Roo.each(this.formats, function(f) {
18077             style.menu.items.push({
18078                 xtype :'MenuItem',
18079                 xns: Roo.bootstrap,
18080                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
18081                 tagname : f,
18082                 listeners : {
18083                     click : function()
18084                     {
18085                         editorcore.insertTag(this.tagname);
18086                         editor.focus();
18087                     }
18088                 }
18089                 
18090             });
18091         });
18092          children.push(style);   
18093             
18094             
18095         btn('bold',false,true);
18096         btn('italic',false,true);
18097         btn('align-left', 'justifyleft',true);
18098         btn('align-center', 'justifycenter',true);
18099         btn('align-right' , 'justifyright',true);
18100         btn('link', false, false, function(btn) {
18101             //Roo.log("create link?");
18102             var url = prompt(this.createLinkText, this.defaultLinkValue);
18103             if(url && url != 'http:/'+'/'){
18104                 this.editorcore.relayCmd('createlink', url);
18105             }
18106         }),
18107         btn('list','insertunorderedlist',true);
18108         btn('pencil', false,true, function(btn){
18109                 Roo.log(this);
18110                 
18111                 this.toggleSourceEdit(btn.pressed);
18112         });
18113         /*
18114         var cog = {
18115                 xtype: 'Button',
18116                 size : 'sm',
18117                 xns: Roo.bootstrap,
18118                 glyphicon : 'cog',
18119                 //html : 'submit'
18120                 menu : {
18121                     xtype: 'Menu',
18122                     xns: Roo.bootstrap,
18123                     items:  []
18124                 }
18125         };
18126         
18127         cog.menu.items.push({
18128             xtype :'MenuItem',
18129             xns: Roo.bootstrap,
18130             html : Clean styles,
18131             tagname : f,
18132             listeners : {
18133                 click : function()
18134                 {
18135                     editorcore.insertTag(this.tagname);
18136                     editor.focus();
18137                 }
18138             }
18139             
18140         });
18141        */
18142         
18143          
18144        this.xtype = 'NavSimplebar';
18145         
18146         for(var i=0;i< children.length;i++) {
18147             
18148             this.buttons.add(this.addxtypeChild(children[i]));
18149             
18150         }
18151         
18152         editor.on('editorevent', this.updateToolbar, this);
18153     },
18154     onBtnClick : function(id)
18155     {
18156        this.editorcore.relayCmd(id);
18157        this.editorcore.focus();
18158     },
18159     
18160     /**
18161      * Protected method that will not generally be called directly. It triggers
18162      * a toolbar update by reading the markup state of the current selection in the editor.
18163      */
18164     updateToolbar: function(){
18165
18166         if(!this.editorcore.activated){
18167             this.editor.onFirstFocus(); // is this neeed?
18168             return;
18169         }
18170
18171         var btns = this.buttons; 
18172         var doc = this.editorcore.doc;
18173         btns.get('bold').setActive(doc.queryCommandState('bold'));
18174         btns.get('italic').setActive(doc.queryCommandState('italic'));
18175         //btns.get('underline').setActive(doc.queryCommandState('underline'));
18176         
18177         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
18178         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
18179         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
18180         
18181         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
18182         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
18183          /*
18184         
18185         var ans = this.editorcore.getAllAncestors();
18186         if (this.formatCombo) {
18187             
18188             
18189             var store = this.formatCombo.store;
18190             this.formatCombo.setValue("");
18191             for (var i =0; i < ans.length;i++) {
18192                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
18193                     // select it..
18194                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
18195                     break;
18196                 }
18197             }
18198         }
18199         
18200         
18201         
18202         // hides menus... - so this cant be on a menu...
18203         Roo.bootstrap.MenuMgr.hideAll();
18204         */
18205         Roo.bootstrap.MenuMgr.hideAll();
18206         //this.editorsyncValue();
18207     },
18208     onFirstFocus: function() {
18209         this.buttons.each(function(item){
18210            item.enable();
18211         });
18212     },
18213     toggleSourceEdit : function(sourceEditMode){
18214         
18215           
18216         if(sourceEditMode){
18217             Roo.log("disabling buttons");
18218            this.buttons.each( function(item){
18219                 if(item.cmd != 'pencil'){
18220                     item.disable();
18221                 }
18222             });
18223           
18224         }else{
18225             Roo.log("enabling buttons");
18226             if(this.editorcore.initialized){
18227                 this.buttons.each( function(item){
18228                     item.enable();
18229                 });
18230             }
18231             
18232         }
18233         Roo.log("calling toggole on editor");
18234         // tell the editor that it's been pressed..
18235         this.editor.toggleSourceEdit(sourceEditMode);
18236        
18237     }
18238 });
18239
18240
18241
18242
18243
18244 /**
18245  * @class Roo.bootstrap.Table.AbstractSelectionModel
18246  * @extends Roo.util.Observable
18247  * Abstract base class for grid SelectionModels.  It provides the interface that should be
18248  * implemented by descendant classes.  This class should not be directly instantiated.
18249  * @constructor
18250  */
18251 Roo.bootstrap.Table.AbstractSelectionModel = function(){
18252     this.locked = false;
18253     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
18254 };
18255
18256
18257 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
18258     /** @ignore Called by the grid automatically. Do not call directly. */
18259     init : function(grid){
18260         this.grid = grid;
18261         this.initEvents();
18262     },
18263
18264     /**
18265      * Locks the selections.
18266      */
18267     lock : function(){
18268         this.locked = true;
18269     },
18270
18271     /**
18272      * Unlocks the selections.
18273      */
18274     unlock : function(){
18275         this.locked = false;
18276     },
18277
18278     /**
18279      * Returns true if the selections are locked.
18280      * @return {Boolean}
18281      */
18282     isLocked : function(){
18283         return this.locked;
18284     }
18285 });
18286 /**
18287  * @extends Roo.bootstrap.Table.AbstractSelectionModel
18288  * @class Roo.bootstrap.Table.RowSelectionModel
18289  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
18290  * It supports multiple selections and keyboard selection/navigation. 
18291  * @constructor
18292  * @param {Object} config
18293  */
18294
18295 Roo.bootstrap.Table.RowSelectionModel = function(config){
18296     Roo.apply(this, config);
18297     this.selections = new Roo.util.MixedCollection(false, function(o){
18298         return o.id;
18299     });
18300
18301     this.last = false;
18302     this.lastActive = false;
18303
18304     this.addEvents({
18305         /**
18306              * @event selectionchange
18307              * Fires when the selection changes
18308              * @param {SelectionModel} this
18309              */
18310             "selectionchange" : true,
18311         /**
18312              * @event afterselectionchange
18313              * Fires after the selection changes (eg. by key press or clicking)
18314              * @param {SelectionModel} this
18315              */
18316             "afterselectionchange" : true,
18317         /**
18318              * @event beforerowselect
18319              * Fires when a row is selected being selected, return false to cancel.
18320              * @param {SelectionModel} this
18321              * @param {Number} rowIndex The selected index
18322              * @param {Boolean} keepExisting False if other selections will be cleared
18323              */
18324             "beforerowselect" : true,
18325         /**
18326              * @event rowselect
18327              * Fires when a row is selected.
18328              * @param {SelectionModel} this
18329              * @param {Number} rowIndex The selected index
18330              * @param {Roo.data.Record} r The record
18331              */
18332             "rowselect" : true,
18333         /**
18334              * @event rowdeselect
18335              * Fires when a row is deselected.
18336              * @param {SelectionModel} this
18337              * @param {Number} rowIndex The selected index
18338              */
18339         "rowdeselect" : true
18340     });
18341     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
18342     this.locked = false;
18343 };
18344
18345 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
18346     /**
18347      * @cfg {Boolean} singleSelect
18348      * True to allow selection of only one row at a time (defaults to false)
18349      */
18350     singleSelect : false,
18351
18352     // private
18353     initEvents : function(){
18354
18355         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
18356             this.grid.on("mousedown", this.handleMouseDown, this);
18357         }else{ // allow click to work like normal
18358             this.grid.on("rowclick", this.handleDragableRowClick, this);
18359         }
18360
18361         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
18362             "up" : function(e){
18363                 if(!e.shiftKey){
18364                     this.selectPrevious(e.shiftKey);
18365                 }else if(this.last !== false && this.lastActive !== false){
18366                     var last = this.last;
18367                     this.selectRange(this.last,  this.lastActive-1);
18368                     this.grid.getView().focusRow(this.lastActive);
18369                     if(last !== false){
18370                         this.last = last;
18371                     }
18372                 }else{
18373                     this.selectFirstRow();
18374                 }
18375                 this.fireEvent("afterselectionchange", this);
18376             },
18377             "down" : function(e){
18378                 if(!e.shiftKey){
18379                     this.selectNext(e.shiftKey);
18380                 }else if(this.last !== false && this.lastActive !== false){
18381                     var last = this.last;
18382                     this.selectRange(this.last,  this.lastActive+1);
18383                     this.grid.getView().focusRow(this.lastActive);
18384                     if(last !== false){
18385                         this.last = last;
18386                     }
18387                 }else{
18388                     this.selectFirstRow();
18389                 }
18390                 this.fireEvent("afterselectionchange", this);
18391             },
18392             scope: this
18393         });
18394
18395         var view = this.grid.view;
18396         view.on("refresh", this.onRefresh, this);
18397         view.on("rowupdated", this.onRowUpdated, this);
18398         view.on("rowremoved", this.onRemove, this);
18399     },
18400
18401     // private
18402     onRefresh : function(){
18403         var ds = this.grid.dataSource, i, v = this.grid.view;
18404         var s = this.selections;
18405         s.each(function(r){
18406             if((i = ds.indexOfId(r.id)) != -1){
18407                 v.onRowSelect(i);
18408             }else{
18409                 s.remove(r);
18410             }
18411         });
18412     },
18413
18414     // private
18415     onRemove : function(v, index, r){
18416         this.selections.remove(r);
18417     },
18418
18419     // private
18420     onRowUpdated : function(v, index, r){
18421         if(this.isSelected(r)){
18422             v.onRowSelect(index);
18423         }
18424     },
18425
18426     /**
18427      * Select records.
18428      * @param {Array} records The records to select
18429      * @param {Boolean} keepExisting (optional) True to keep existing selections
18430      */
18431     selectRecords : function(records, keepExisting){
18432         if(!keepExisting){
18433             this.clearSelections();
18434         }
18435         var ds = this.grid.dataSource;
18436         for(var i = 0, len = records.length; i < len; i++){
18437             this.selectRow(ds.indexOf(records[i]), true);
18438         }
18439     },
18440
18441     /**
18442      * Gets the number of selected rows.
18443      * @return {Number}
18444      */
18445     getCount : function(){
18446         return this.selections.length;
18447     },
18448
18449     /**
18450      * Selects the first row in the grid.
18451      */
18452     selectFirstRow : function(){
18453         this.selectRow(0);
18454     },
18455
18456     /**
18457      * Select the last row.
18458      * @param {Boolean} keepExisting (optional) True to keep existing selections
18459      */
18460     selectLastRow : function(keepExisting){
18461         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
18462     },
18463
18464     /**
18465      * Selects the row immediately following the last selected row.
18466      * @param {Boolean} keepExisting (optional) True to keep existing selections
18467      */
18468     selectNext : function(keepExisting){
18469         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
18470             this.selectRow(this.last+1, keepExisting);
18471             this.grid.getView().focusRow(this.last);
18472         }
18473     },
18474
18475     /**
18476      * Selects the row that precedes the last selected row.
18477      * @param {Boolean} keepExisting (optional) True to keep existing selections
18478      */
18479     selectPrevious : function(keepExisting){
18480         if(this.last){
18481             this.selectRow(this.last-1, keepExisting);
18482             this.grid.getView().focusRow(this.last);
18483         }
18484     },
18485
18486     /**
18487      * Returns the selected records
18488      * @return {Array} Array of selected records
18489      */
18490     getSelections : function(){
18491         return [].concat(this.selections.items);
18492     },
18493
18494     /**
18495      * Returns the first selected record.
18496      * @return {Record}
18497      */
18498     getSelected : function(){
18499         return this.selections.itemAt(0);
18500     },
18501
18502
18503     /**
18504      * Clears all selections.
18505      */
18506     clearSelections : function(fast){
18507         if(this.locked) return;
18508         if(fast !== true){
18509             var ds = this.grid.dataSource;
18510             var s = this.selections;
18511             s.each(function(r){
18512                 this.deselectRow(ds.indexOfId(r.id));
18513             }, this);
18514             s.clear();
18515         }else{
18516             this.selections.clear();
18517         }
18518         this.last = false;
18519     },
18520
18521
18522     /**
18523      * Selects all rows.
18524      */
18525     selectAll : function(){
18526         if(this.locked) return;
18527         this.selections.clear();
18528         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
18529             this.selectRow(i, true);
18530         }
18531     },
18532
18533     /**
18534      * Returns True if there is a selection.
18535      * @return {Boolean}
18536      */
18537     hasSelection : function(){
18538         return this.selections.length > 0;
18539     },
18540
18541     /**
18542      * Returns True if the specified row is selected.
18543      * @param {Number/Record} record The record or index of the record to check
18544      * @return {Boolean}
18545      */
18546     isSelected : function(index){
18547         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
18548         return (r && this.selections.key(r.id) ? true : false);
18549     },
18550
18551     /**
18552      * Returns True if the specified record id is selected.
18553      * @param {String} id The id of record to check
18554      * @return {Boolean}
18555      */
18556     isIdSelected : function(id){
18557         return (this.selections.key(id) ? true : false);
18558     },
18559
18560     // private
18561     handleMouseDown : function(e, t){
18562         var view = this.grid.getView(), rowIndex;
18563         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
18564             return;
18565         };
18566         if(e.shiftKey && this.last !== false){
18567             var last = this.last;
18568             this.selectRange(last, rowIndex, e.ctrlKey);
18569             this.last = last; // reset the last
18570             view.focusRow(rowIndex);
18571         }else{
18572             var isSelected = this.isSelected(rowIndex);
18573             if(e.button !== 0 && isSelected){
18574                 view.focusRow(rowIndex);
18575             }else if(e.ctrlKey && isSelected){
18576                 this.deselectRow(rowIndex);
18577             }else if(!isSelected){
18578                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
18579                 view.focusRow(rowIndex);
18580             }
18581         }
18582         this.fireEvent("afterselectionchange", this);
18583     },
18584     // private
18585     handleDragableRowClick :  function(grid, rowIndex, e) 
18586     {
18587         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
18588             this.selectRow(rowIndex, false);
18589             grid.view.focusRow(rowIndex);
18590              this.fireEvent("afterselectionchange", this);
18591         }
18592     },
18593     
18594     /**
18595      * Selects multiple rows.
18596      * @param {Array} rows Array of the indexes of the row to select
18597      * @param {Boolean} keepExisting (optional) True to keep existing selections
18598      */
18599     selectRows : function(rows, keepExisting){
18600         if(!keepExisting){
18601             this.clearSelections();
18602         }
18603         for(var i = 0, len = rows.length; i < len; i++){
18604             this.selectRow(rows[i], true);
18605         }
18606     },
18607
18608     /**
18609      * Selects a range of rows. All rows in between startRow and endRow are also selected.
18610      * @param {Number} startRow The index of the first row in the range
18611      * @param {Number} endRow The index of the last row in the range
18612      * @param {Boolean} keepExisting (optional) True to retain existing selections
18613      */
18614     selectRange : function(startRow, endRow, keepExisting){
18615         if(this.locked) return;
18616         if(!keepExisting){
18617             this.clearSelections();
18618         }
18619         if(startRow <= endRow){
18620             for(var i = startRow; i <= endRow; i++){
18621                 this.selectRow(i, true);
18622             }
18623         }else{
18624             for(var i = startRow; i >= endRow; i--){
18625                 this.selectRow(i, true);
18626             }
18627         }
18628     },
18629
18630     /**
18631      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
18632      * @param {Number} startRow The index of the first row in the range
18633      * @param {Number} endRow The index of the last row in the range
18634      */
18635     deselectRange : function(startRow, endRow, preventViewNotify){
18636         if(this.locked) return;
18637         for(var i = startRow; i <= endRow; i++){
18638             this.deselectRow(i, preventViewNotify);
18639         }
18640     },
18641
18642     /**
18643      * Selects a row.
18644      * @param {Number} row The index of the row to select
18645      * @param {Boolean} keepExisting (optional) True to keep existing selections
18646      */
18647     selectRow : function(index, keepExisting, preventViewNotify){
18648         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
18649         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
18650             if(!keepExisting || this.singleSelect){
18651                 this.clearSelections();
18652             }
18653             var r = this.grid.dataSource.getAt(index);
18654             this.selections.add(r);
18655             this.last = this.lastActive = index;
18656             if(!preventViewNotify){
18657                 this.grid.getView().onRowSelect(index);
18658             }
18659             this.fireEvent("rowselect", this, index, r);
18660             this.fireEvent("selectionchange", this);
18661         }
18662     },
18663
18664     /**
18665      * Deselects a row.
18666      * @param {Number} row The index of the row to deselect
18667      */
18668     deselectRow : function(index, preventViewNotify){
18669         if(this.locked) return;
18670         if(this.last == index){
18671             this.last = false;
18672         }
18673         if(this.lastActive == index){
18674             this.lastActive = false;
18675         }
18676         var r = this.grid.dataSource.getAt(index);
18677         this.selections.remove(r);
18678         if(!preventViewNotify){
18679             this.grid.getView().onRowDeselect(index);
18680         }
18681         this.fireEvent("rowdeselect", this, index);
18682         this.fireEvent("selectionchange", this);
18683     },
18684
18685     // private
18686     restoreLast : function(){
18687         if(this._last){
18688             this.last = this._last;
18689         }
18690     },
18691
18692     // private
18693     acceptsNav : function(row, col, cm){
18694         return !cm.isHidden(col) && cm.isCellEditable(col, row);
18695     },
18696
18697     // private
18698     onEditorKey : function(field, e){
18699         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
18700         if(k == e.TAB){
18701             e.stopEvent();
18702             ed.completeEdit();
18703             if(e.shiftKey){
18704                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
18705             }else{
18706                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
18707             }
18708         }else if(k == e.ENTER && !e.ctrlKey){
18709             e.stopEvent();
18710             ed.completeEdit();
18711             if(e.shiftKey){
18712                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
18713             }else{
18714                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
18715             }
18716         }else if(k == e.ESC){
18717             ed.cancelEdit();
18718         }
18719         if(newCell){
18720             g.startEditing(newCell[0], newCell[1]);
18721         }
18722     }
18723 });/*
18724  * Based on:
18725  * Ext JS Library 1.1.1
18726  * Copyright(c) 2006-2007, Ext JS, LLC.
18727  *
18728  * Originally Released Under LGPL - original licence link has changed is not relivant.
18729  *
18730  * Fork - LGPL
18731  * <script type="text/javascript">
18732  */
18733  
18734 /**
18735  * @class Roo.bootstrap.PagingToolbar
18736  * @extends Roo.Row
18737  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
18738  * @constructor
18739  * Create a new PagingToolbar
18740  * @param {Object} config The config object
18741  */
18742 Roo.bootstrap.PagingToolbar = function(config)
18743 {
18744     // old args format still supported... - xtype is prefered..
18745         // created from xtype...
18746     var ds = config.dataSource;
18747     this.toolbarItems = [];
18748     if (config.items) {
18749         this.toolbarItems = config.items;
18750 //        config.items = [];
18751     }
18752     
18753     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
18754     this.ds = ds;
18755     this.cursor = 0;
18756     if (ds) { 
18757         this.bind(ds);
18758     }
18759     
18760     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
18761     
18762 };
18763
18764 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
18765     /**
18766      * @cfg {Roo.data.Store} dataSource
18767      * The underlying data store providing the paged data
18768      */
18769     /**
18770      * @cfg {String/HTMLElement/Element} container
18771      * container The id or element that will contain the toolbar
18772      */
18773     /**
18774      * @cfg {Boolean} displayInfo
18775      * True to display the displayMsg (defaults to false)
18776      */
18777     /**
18778      * @cfg {Number} pageSize
18779      * The number of records to display per page (defaults to 20)
18780      */
18781     pageSize: 20,
18782     /**
18783      * @cfg {String} displayMsg
18784      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
18785      */
18786     displayMsg : 'Displaying {0} - {1} of {2}',
18787     /**
18788      * @cfg {String} emptyMsg
18789      * The message to display when no records are found (defaults to "No data to display")
18790      */
18791     emptyMsg : 'No data to display',
18792     /**
18793      * Customizable piece of the default paging text (defaults to "Page")
18794      * @type String
18795      */
18796     beforePageText : "Page",
18797     /**
18798      * Customizable piece of the default paging text (defaults to "of %0")
18799      * @type String
18800      */
18801     afterPageText : "of {0}",
18802     /**
18803      * Customizable piece of the default paging text (defaults to "First Page")
18804      * @type String
18805      */
18806     firstText : "First Page",
18807     /**
18808      * Customizable piece of the default paging text (defaults to "Previous Page")
18809      * @type String
18810      */
18811     prevText : "Previous Page",
18812     /**
18813      * Customizable piece of the default paging text (defaults to "Next Page")
18814      * @type String
18815      */
18816     nextText : "Next Page",
18817     /**
18818      * Customizable piece of the default paging text (defaults to "Last Page")
18819      * @type String
18820      */
18821     lastText : "Last Page",
18822     /**
18823      * Customizable piece of the default paging text (defaults to "Refresh")
18824      * @type String
18825      */
18826     refreshText : "Refresh",
18827
18828     buttons : false,
18829     // private
18830     onRender : function(ct, position) 
18831     {
18832         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
18833         this.navgroup.parentId = this.id;
18834         this.navgroup.onRender(this.el, null);
18835         // add the buttons to the navgroup
18836         
18837         if(this.displayInfo){
18838             Roo.log(this.el.select('ul.navbar-nav',true).first());
18839             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
18840             this.displayEl = this.el.select('.x-paging-info', true).first();
18841 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
18842 //            this.displayEl = navel.el.select('span',true).first();
18843         }
18844         
18845         var _this = this;
18846         
18847         if(this.buttons){
18848             Roo.each(_this.buttons, function(e){
18849                Roo.factory(e).onRender(_this.el, null);
18850             });
18851         }
18852             
18853         Roo.each(_this.toolbarItems, function(e) {
18854             _this.navgroup.addItem(e);
18855         });
18856         
18857         this.first = this.navgroup.addItem({
18858             tooltip: this.firstText,
18859             cls: "prev",
18860             icon : 'fa fa-backward',
18861             disabled: true,
18862             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
18863         });
18864         
18865         this.prev =  this.navgroup.addItem({
18866             tooltip: this.prevText,
18867             cls: "prev",
18868             icon : 'fa fa-step-backward',
18869             disabled: true,
18870             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
18871         });
18872     //this.addSeparator();
18873         
18874         
18875         var field = this.navgroup.addItem( {
18876             tagtype : 'span',
18877             cls : 'x-paging-position',
18878             
18879             html : this.beforePageText  +
18880                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
18881                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
18882          } ); //?? escaped?
18883         
18884         this.field = field.el.select('input', true).first();
18885         this.field.on("keydown", this.onPagingKeydown, this);
18886         this.field.on("focus", function(){this.dom.select();});
18887     
18888     
18889         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
18890         //this.field.setHeight(18);
18891         //this.addSeparator();
18892         this.next = this.navgroup.addItem({
18893             tooltip: this.nextText,
18894             cls: "next",
18895             html : ' <i class="fa fa-step-forward">',
18896             disabled: true,
18897             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
18898         });
18899         this.last = this.navgroup.addItem({
18900             tooltip: this.lastText,
18901             icon : 'fa fa-forward',
18902             cls: "next",
18903             disabled: true,
18904             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
18905         });
18906     //this.addSeparator();
18907         this.loading = this.navgroup.addItem({
18908             tooltip: this.refreshText,
18909             icon: 'fa fa-refresh',
18910             
18911             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
18912         });
18913
18914     },
18915
18916     // private
18917     updateInfo : function(){
18918         if(this.displayEl){
18919             var count = this.ds.getCount();
18920             var msg = count == 0 ?
18921                 this.emptyMsg :
18922                 String.format(
18923                     this.displayMsg,
18924                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
18925                 );
18926             this.displayEl.update(msg);
18927         }
18928     },
18929
18930     // private
18931     onLoad : function(ds, r, o){
18932        this.cursor = o.params ? o.params.start : 0;
18933        var d = this.getPageData(),
18934             ap = d.activePage,
18935             ps = d.pages;
18936         
18937        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
18938        this.field.dom.value = ap;
18939        this.first.setDisabled(ap == 1);
18940        this.prev.setDisabled(ap == 1);
18941        this.next.setDisabled(ap == ps);
18942        this.last.setDisabled(ap == ps);
18943        this.loading.enable();
18944        this.updateInfo();
18945     },
18946
18947     // private
18948     getPageData : function(){
18949         var total = this.ds.getTotalCount();
18950         return {
18951             total : total,
18952             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
18953             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
18954         };
18955     },
18956
18957     // private
18958     onLoadError : function(){
18959         this.loading.enable();
18960     },
18961
18962     // private
18963     onPagingKeydown : function(e){
18964         var k = e.getKey();
18965         var d = this.getPageData();
18966         if(k == e.RETURN){
18967             var v = this.field.dom.value, pageNum;
18968             if(!v || isNaN(pageNum = parseInt(v, 10))){
18969                 this.field.dom.value = d.activePage;
18970                 return;
18971             }
18972             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
18973             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
18974             e.stopEvent();
18975         }
18976         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))
18977         {
18978           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
18979           this.field.dom.value = pageNum;
18980           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
18981           e.stopEvent();
18982         }
18983         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
18984         {
18985           var v = this.field.dom.value, pageNum; 
18986           var increment = (e.shiftKey) ? 10 : 1;
18987           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
18988             increment *= -1;
18989           if(!v || isNaN(pageNum = parseInt(v, 10))) {
18990             this.field.dom.value = d.activePage;
18991             return;
18992           }
18993           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
18994           {
18995             this.field.dom.value = parseInt(v, 10) + increment;
18996             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
18997             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
18998           }
18999           e.stopEvent();
19000         }
19001     },
19002
19003     // private
19004     beforeLoad : function(){
19005         if(this.loading){
19006             this.loading.disable();
19007         }
19008     },
19009
19010     // private
19011     onClick : function(which){
19012         var ds = this.ds;
19013         if (!ds) {
19014             return;
19015         }
19016         switch(which){
19017             case "first":
19018                 ds.load({params:{start: 0, limit: this.pageSize}});
19019             break;
19020             case "prev":
19021                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
19022             break;
19023             case "next":
19024                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
19025             break;
19026             case "last":
19027                 var total = ds.getTotalCount();
19028                 var extra = total % this.pageSize;
19029                 var lastStart = extra ? (total - extra) : total-this.pageSize;
19030                 ds.load({params:{start: lastStart, limit: this.pageSize}});
19031             break;
19032             case "refresh":
19033                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
19034             break;
19035         }
19036     },
19037
19038     /**
19039      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
19040      * @param {Roo.data.Store} store The data store to unbind
19041      */
19042     unbind : function(ds){
19043         ds.un("beforeload", this.beforeLoad, this);
19044         ds.un("load", this.onLoad, this);
19045         ds.un("loadexception", this.onLoadError, this);
19046         ds.un("remove", this.updateInfo, this);
19047         ds.un("add", this.updateInfo, this);
19048         this.ds = undefined;
19049     },
19050
19051     /**
19052      * Binds the paging toolbar to the specified {@link Roo.data.Store}
19053      * @param {Roo.data.Store} store The data store to bind
19054      */
19055     bind : function(ds){
19056         ds.on("beforeload", this.beforeLoad, this);
19057         ds.on("load", this.onLoad, this);
19058         ds.on("loadexception", this.onLoadError, this);
19059         ds.on("remove", this.updateInfo, this);
19060         ds.on("add", this.updateInfo, this);
19061         this.ds = ds;
19062     }
19063 });/*
19064  * - LGPL
19065  *
19066  * element
19067  * 
19068  */
19069
19070 /**
19071  * @class Roo.bootstrap.MessageBar
19072  * @extends Roo.bootstrap.Component
19073  * Bootstrap MessageBar class
19074  * @cfg {String} html contents of the MessageBar
19075  * @cfg {String} weight (info | success | warning | danger) default info
19076  * @cfg {String} beforeClass insert the bar before the given class
19077  * @cfg {Boolean} closable (true | false) default false
19078  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
19079  * 
19080  * @constructor
19081  * Create a new Element
19082  * @param {Object} config The config object
19083  */
19084
19085 Roo.bootstrap.MessageBar = function(config){
19086     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
19087 };
19088
19089 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
19090     
19091     html: '',
19092     weight: 'info',
19093     closable: false,
19094     fixed: false,
19095     beforeClass: 'bootstrap-sticky-wrap',
19096     
19097     getAutoCreate : function(){
19098         
19099         var cfg = {
19100             tag: 'div',
19101             cls: 'alert alert-dismissable alert-' + this.weight,
19102             cn: [
19103                 {
19104                     tag: 'span',
19105                     cls: 'message',
19106                     html: this.html || ''
19107                 }
19108             ]
19109         }
19110         
19111         if(this.fixed){
19112             cfg.cls += ' alert-messages-fixed';
19113         }
19114         
19115         if(this.closable){
19116             cfg.cn.push({
19117                 tag: 'button',
19118                 cls: 'close',
19119                 html: 'x'
19120             });
19121         }
19122         
19123         return cfg;
19124     },
19125     
19126     onRender : function(ct, position)
19127     {
19128         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19129         
19130         if(!this.el){
19131             var cfg = Roo.apply({},  this.getAutoCreate());
19132             cfg.id = Roo.id();
19133             
19134             if (this.cls) {
19135                 cfg.cls += ' ' + this.cls;
19136             }
19137             if (this.style) {
19138                 cfg.style = this.style;
19139             }
19140             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
19141             
19142             this.el.setVisibilityMode(Roo.Element.DISPLAY);
19143         }
19144         
19145         this.el.select('>button.close').on('click', this.hide, this);
19146         
19147     },
19148     
19149     show : function()
19150     {
19151         if (!this.rendered) {
19152             this.render();
19153         }
19154         
19155         this.el.show();
19156         
19157         this.fireEvent('show', this);
19158         
19159     },
19160     
19161     hide : function()
19162     {
19163         if (!this.rendered) {
19164             this.render();
19165         }
19166         
19167         this.el.hide();
19168         
19169         this.fireEvent('hide', this);
19170     },
19171     
19172     update : function()
19173     {
19174 //        var e = this.el.dom.firstChild;
19175 //        
19176 //        if(this.closable){
19177 //            e = e.nextSibling;
19178 //        }
19179 //        
19180 //        e.data = this.html || '';
19181
19182         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
19183     }
19184    
19185 });
19186
19187  
19188
19189      /*
19190  * - LGPL
19191  *
19192  * Graph
19193  * 
19194  */
19195
19196
19197 /**
19198  * @class Roo.bootstrap.Graph
19199  * @extends Roo.bootstrap.Component
19200  * Bootstrap Graph class
19201 > Prameters
19202  -sm {number} sm 4
19203  -md {number} md 5
19204  @cfg {String} graphtype  bar | vbar | pie
19205  @cfg {number} g_x coodinator | centre x (pie)
19206  @cfg {number} g_y coodinator | centre y (pie)
19207  @cfg {number} g_r radius (pie)
19208  @cfg {number} g_height height of the chart (respected by all elements in the set)
19209  @cfg {number} g_width width of the chart (respected by all elements in the set)
19210  @cfg {Object} title The title of the chart
19211     
19212  -{Array}  values
19213  -opts (object) options for the chart 
19214      o {
19215      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
19216      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
19217      o vgutter (number)
19218      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.
19219      o stacked (boolean) whether or not to tread values as in a stacked bar chart
19220      o to
19221      o stretch (boolean)
19222      o }
19223  -opts (object) options for the pie
19224      o{
19225      o cut
19226      o startAngle (number)
19227      o endAngle (number)
19228      } 
19229  *
19230  * @constructor
19231  * Create a new Input
19232  * @param {Object} config The config object
19233  */
19234
19235 Roo.bootstrap.Graph = function(config){
19236     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
19237     
19238     this.addEvents({
19239         // img events
19240         /**
19241          * @event click
19242          * The img click event for the img.
19243          * @param {Roo.EventObject} e
19244          */
19245         "click" : true
19246     });
19247 };
19248
19249 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
19250     
19251     sm: 4,
19252     md: 5,
19253     graphtype: 'bar',
19254     g_height: 250,
19255     g_width: 400,
19256     g_x: 50,
19257     g_y: 50,
19258     g_r: 30,
19259     opts:{
19260         //g_colors: this.colors,
19261         g_type: 'soft',
19262         g_gutter: '20%'
19263
19264     },
19265     title : false,
19266
19267     getAutoCreate : function(){
19268         
19269         var cfg = {
19270             tag: 'div',
19271             html : null
19272         }
19273         
19274         
19275         return  cfg;
19276     },
19277
19278     onRender : function(ct,position){
19279         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
19280         this.raphael = Raphael(this.el.dom);
19281         
19282                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19283                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19284                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19285                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
19286                 /*
19287                 r.text(160, 10, "Single Series Chart").attr(txtattr);
19288                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
19289                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
19290                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
19291                 
19292                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
19293                 r.barchart(330, 10, 300, 220, data1);
19294                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
19295                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
19296                 */
19297                 
19298                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19299                 // r.barchart(30, 30, 560, 250,  xdata, {
19300                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
19301                 //     axis : "0 0 1 1",
19302                 //     axisxlabels :  xdata
19303                 //     //yvalues : cols,
19304                    
19305                 // });
19306 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19307 //        
19308 //        this.load(null,xdata,{
19309 //                axis : "0 0 1 1",
19310 //                axisxlabels :  xdata
19311 //                });
19312
19313     },
19314
19315     load : function(graphtype,xdata,opts){
19316         this.raphael.clear();
19317         if(!graphtype) {
19318             graphtype = this.graphtype;
19319         }
19320         if(!opts){
19321             opts = this.opts;
19322         }
19323         var r = this.raphael,
19324             fin = function () {
19325                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
19326             },
19327             fout = function () {
19328                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
19329             },
19330             pfin = function() {
19331                 this.sector.stop();
19332                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
19333
19334                 if (this.label) {
19335                     this.label[0].stop();
19336                     this.label[0].attr({ r: 7.5 });
19337                     this.label[1].attr({ "font-weight": 800 });
19338                 }
19339             },
19340             pfout = function() {
19341                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
19342
19343                 if (this.label) {
19344                     this.label[0].animate({ r: 5 }, 500, "bounce");
19345                     this.label[1].attr({ "font-weight": 400 });
19346                 }
19347             };
19348
19349         switch(graphtype){
19350             case 'bar':
19351                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
19352                 break;
19353             case 'hbar':
19354                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
19355                 break;
19356             case 'pie':
19357 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
19358 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
19359 //            
19360                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
19361                 
19362                 break;
19363
19364         }
19365         
19366         if(this.title){
19367             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
19368         }
19369         
19370     },
19371     
19372     setTitle: function(o)
19373     {
19374         this.title = o;
19375     },
19376     
19377     initEvents: function() {
19378         
19379         if(!this.href){
19380             this.el.on('click', this.onClick, this);
19381         }
19382     },
19383     
19384     onClick : function(e)
19385     {
19386         Roo.log('img onclick');
19387         this.fireEvent('click', this, e);
19388     }
19389    
19390 });
19391
19392  
19393 /*
19394  * - LGPL
19395  *
19396  * numberBox
19397  * 
19398  */
19399 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19400
19401 /**
19402  * @class Roo.bootstrap.dash.NumberBox
19403  * @extends Roo.bootstrap.Component
19404  * Bootstrap NumberBox class
19405  * @cfg {String} bgcolor Box background color, such as (aqua | green | yellow | red etc..) Default aqua
19406  * @cfg {String} headline Box headline
19407  * @cfg {String} content Box content
19408  * @cfg {String} icon Box icon
19409  * @cfg {String} footer Footer text
19410  * @cfg {String} fhref Footer href
19411  * 
19412  * @constructor
19413  * Create a new NumberBox
19414  * @param {Object} config The config object
19415  */
19416
19417
19418 Roo.bootstrap.dash.NumberBox = function(config){
19419     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
19420     
19421 };
19422
19423 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
19424     
19425     bgcolor : 'aqua',
19426     headline : '',
19427     content : '',
19428     icon : '',
19429     footer : '',
19430     fhref : '',
19431     ficon : '',
19432     
19433     getAutoCreate : function(){
19434         
19435         var cfg = {
19436             tag : 'div',
19437             cls : 'small-box bg-' + this.bgcolor,
19438             cn : [
19439                 {
19440                     tag : 'div',
19441                     cls : 'inner',
19442                     cn :[
19443                         {
19444                             tag : 'h3',
19445                             cls : 'roo-headline',
19446                             html : this.headline
19447                         },
19448                         {
19449                             tag : 'p',
19450                             cls : 'roo-content',
19451                             html : this.content
19452                         }
19453                     ]
19454                 }
19455             ]
19456         }
19457         
19458         if(this.icon){
19459             cfg.cn.push({
19460                 tag : 'div',
19461                 cls : 'icon',
19462                 cn :[
19463                     {
19464                         tag : 'i',
19465                         cls : 'ion ' + this.icon
19466                     }
19467                 ]
19468             });
19469         }
19470         
19471         if(this.footer){
19472             var footer = {
19473                 tag : 'a',
19474                 cls : 'small-box-footer',
19475                 href : this.fhref || '#',
19476                 html : this.footer
19477             };
19478             
19479             cfg.cn.push(footer);
19480             
19481         }
19482         
19483         return  cfg;
19484     },
19485
19486     onRender : function(ct,position){
19487         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
19488
19489
19490        
19491                 
19492     },
19493
19494     setHeadline: function (value)
19495     {
19496         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
19497     },
19498     
19499     setFooter: function (value, href)
19500     {
19501         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
19502         
19503         if(href){
19504             this.el.select('a.small-box-footer',true).first().attr('href', href);
19505         }
19506         
19507     },
19508
19509     setContent: function (value)
19510     {
19511         this.el.select('.roo-content',true).first().dom.innerHTML = value;
19512     },
19513
19514     initEvents: function() 
19515     {   
19516         
19517     }
19518     
19519 });
19520
19521  
19522 /*
19523  * - LGPL
19524  *
19525  * TabBox
19526  * 
19527  */
19528 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19529
19530 /**
19531  * @class Roo.bootstrap.dash.TabBox
19532  * @extends Roo.bootstrap.Component
19533  * Bootstrap TabBox class
19534  * @cfg {String} title Title of the TabBox
19535  * @cfg {String} icon Icon of the TabBox
19536  * 
19537  * @constructor
19538  * Create a new TabBox
19539  * @param {Object} config The config object
19540  */
19541
19542
19543 Roo.bootstrap.dash.TabBox = function(config){
19544     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
19545     this.addEvents({
19546         // raw events
19547         /**
19548          * @event addpane
19549          * When a pane is added
19550          * @param {Roo.bootstrap.dash.TabPane} pane
19551          */
19552         "addpane" : true
19553          
19554     });
19555 };
19556
19557 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
19558
19559     title : '',
19560     icon : false,
19561     
19562     getChildContainer : function()
19563     {
19564         return this.el.select('.tab-content', true).first();
19565     },
19566     
19567     getAutoCreate : function(){
19568         
19569         var header = {
19570             tag: 'li',
19571             cls: 'pull-left header',
19572             html: this.title,
19573             cn : []
19574         };
19575         
19576         if(this.icon){
19577             header.cn.push({
19578                 tag: 'i',
19579                 cls: 'fa ' + this.icon
19580             });
19581         }
19582         
19583         
19584         var cfg = {
19585             tag: 'div',
19586             cls: 'nav-tabs-custom',
19587             cn: [
19588                 {
19589                     tag: 'ul',
19590                     cls: 'nav nav-tabs pull-right',
19591                     cn: [
19592                         header
19593                     ]
19594                 },
19595                 {
19596                     tag: 'div',
19597                     cls: 'tab-content no-padding',
19598                     cn: []
19599                 }
19600             ]
19601         }
19602
19603         return  cfg;
19604     },
19605     initEvents : function()
19606     {
19607         //Roo.log('add add pane handler');
19608         this.on('addpane', this.onAddPane, this);
19609     },
19610      /**
19611      * Updates the box title
19612      * @param {String} html to set the title to.
19613      */
19614     setTitle : function(value)
19615     {
19616         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
19617     },
19618     onAddPane : function(pane)
19619     {
19620         //Roo.log('addpane');
19621         //Roo.log(pane);
19622         // tabs are rendere left to right..
19623         var ctr = this.el.select('.nav-tabs', true).first();
19624          
19625          
19626         var existing = ctr.select('.nav-tab',true);
19627         var qty = existing.getCount();;
19628         
19629         
19630         var tab = ctr.createChild({
19631             tag : 'li',
19632             cls : 'nav-tab' + (qty ? '' : ' active'),
19633             cn : [
19634                 {
19635                     tag : 'a',
19636                     href:'#',
19637                     html : pane.title
19638                 }
19639             ]
19640         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
19641         pane.tab = tab;
19642         
19643         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
19644         if (!qty) {
19645             pane.el.addClass('active');
19646         }
19647         
19648                 
19649     },
19650     onTabClick : function(ev,un,ob,pane)
19651     {
19652         //Roo.log('tab - prev default');
19653         ev.preventDefault();
19654         
19655         
19656         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
19657         pane.tab.addClass('active');
19658         //Roo.log(pane.title);
19659         this.getChildContainer().select('.tab-pane',true).removeClass('active');
19660         // technically we should have a deactivate event.. but maybe add later.
19661         // and it should not de-activate the selected tab...
19662         
19663         pane.el.addClass('active');
19664         pane.fireEvent('activate');
19665         
19666         
19667     }
19668     
19669     
19670 });
19671
19672  
19673 /*
19674  * - LGPL
19675  *
19676  * Tab pane
19677  * 
19678  */
19679 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19680 /**
19681  * @class Roo.bootstrap.TabPane
19682  * @extends Roo.bootstrap.Component
19683  * Bootstrap TabPane class
19684  * @cfg {Boolean} active (false | true) Default false
19685  * @cfg {String} title title of panel
19686
19687  * 
19688  * @constructor
19689  * Create a new TabPane
19690  * @param {Object} config The config object
19691  */
19692
19693 Roo.bootstrap.dash.TabPane = function(config){
19694     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
19695     
19696 };
19697
19698 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
19699     
19700     active : false,
19701     title : '',
19702     
19703     // the tabBox that this is attached to.
19704     tab : false,
19705      
19706     getAutoCreate : function() 
19707     {
19708         var cfg = {
19709             tag: 'div',
19710             cls: 'tab-pane'
19711         }
19712         
19713         if(this.active){
19714             cfg.cls += ' active';
19715         }
19716         
19717         return cfg;
19718     },
19719     initEvents  : function()
19720     {
19721         //Roo.log('trigger add pane handler');
19722         this.parent().fireEvent('addpane', this)
19723     },
19724     
19725      /**
19726      * Updates the tab title 
19727      * @param {String} html to set the title to.
19728      */
19729     setTitle: function(str)
19730     {
19731         if (!this.tab) {
19732             return;
19733         }
19734         this.title = str;
19735         this.tab.select('a'.true).first().dom.innerHTML = str;
19736         
19737     }
19738     
19739     
19740     
19741 });
19742
19743  
19744
19745
19746  /*
19747  * - LGPL
19748  *
19749  * menu
19750  * 
19751  */
19752 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
19753
19754 /**
19755  * @class Roo.bootstrap.menu.Menu
19756  * @extends Roo.bootstrap.Component
19757  * Bootstrap Menu class - container for Menu
19758  * @cfg {String} html Text of the menu
19759  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
19760  * @cfg {String} icon Font awesome icon
19761  * @cfg {String} pos Menu align to (top | bottom) default bottom
19762  * 
19763  * 
19764  * @constructor
19765  * Create a new Menu
19766  * @param {Object} config The config object
19767  */
19768
19769
19770 Roo.bootstrap.menu.Menu = function(config){
19771     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
19772     
19773     this.addEvents({
19774         /**
19775          * @event beforeshow
19776          * Fires before this menu is displayed
19777          * @param {Roo.bootstrap.menu.Menu} this
19778          */
19779         beforeshow : true,
19780         /**
19781          * @event beforehide
19782          * Fires before this menu is hidden
19783          * @param {Roo.bootstrap.menu.Menu} this
19784          */
19785         beforehide : true,
19786         /**
19787          * @event show
19788          * Fires after this menu is displayed
19789          * @param {Roo.bootstrap.menu.Menu} this
19790          */
19791         show : true,
19792         /**
19793          * @event hide
19794          * Fires after this menu is hidden
19795          * @param {Roo.bootstrap.menu.Menu} this
19796          */
19797         hide : true,
19798         /**
19799          * @event click
19800          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19801          * @param {Roo.bootstrap.menu.Menu} this
19802          * @param {Roo.EventObject} e
19803          */
19804         click : true
19805     });
19806     
19807 };
19808
19809 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
19810     
19811     submenu : false,
19812     html : '',
19813     weight : 'default',
19814     icon : false,
19815     pos : 'bottom',
19816     
19817     
19818     getChildContainer : function() {
19819         if(this.isSubMenu){
19820             return this.el;
19821         }
19822         
19823         return this.el.select('ul.dropdown-menu', true).first();  
19824     },
19825     
19826     getAutoCreate : function()
19827     {
19828         var text = [
19829             {
19830                 tag : 'span',
19831                 cls : 'roo-menu-text',
19832                 html : this.html
19833             }
19834         ];
19835         
19836         if(this.icon){
19837             text.unshift({
19838                 tag : 'i',
19839                 cls : 'fa ' + this.icon
19840             })
19841         }
19842         
19843         
19844         var cfg = {
19845             tag : 'div',
19846             cls : 'btn-group',
19847             cn : [
19848                 {
19849                     tag : 'button',
19850                     cls : 'dropdown-button btn btn-' + this.weight,
19851                     cn : text
19852                 },
19853                 {
19854                     tag : 'button',
19855                     cls : 'dropdown-toggle btn btn-' + this.weight,
19856                     cn : [
19857                         {
19858                             tag : 'span',
19859                             cls : 'caret'
19860                         }
19861                     ]
19862                 },
19863                 {
19864                     tag : 'ul',
19865                     cls : 'dropdown-menu'
19866                 }
19867             ]
19868             
19869         };
19870         
19871         if(this.pos == 'top'){
19872             cfg.cls += ' dropup';
19873         }
19874         
19875         if(this.isSubMenu){
19876             cfg = {
19877                 tag : 'ul',
19878                 cls : 'dropdown-menu'
19879             }
19880         }
19881         
19882         return cfg;
19883     },
19884     
19885     onRender : function(ct, position)
19886     {
19887         this.isSubMenu = ct.hasClass('dropdown-submenu');
19888         
19889         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
19890     },
19891     
19892     initEvents : function() 
19893     {
19894         if(this.isSubMenu){
19895             return;
19896         }
19897         
19898         this.hidden = true;
19899         
19900         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
19901         this.triggerEl.on('click', this.onTriggerPress, this);
19902         
19903         this.buttonEl = this.el.select('button.dropdown-button', true).first();
19904         this.buttonEl.on('click', this.onClick, this);
19905         
19906     },
19907     
19908     list : function()
19909     {
19910         if(this.isSubMenu){
19911             return this.el;
19912         }
19913         
19914         return this.el.select('ul.dropdown-menu', true).first();
19915     },
19916     
19917     onClick : function(e)
19918     {
19919         this.fireEvent("click", this, e);
19920     },
19921     
19922     onTriggerPress  : function(e)
19923     {   
19924         if (this.isVisible()) {
19925             this.hide();
19926         } else {
19927             this.show();
19928         }
19929     },
19930     
19931     isVisible : function(){
19932         return !this.hidden;
19933     },
19934     
19935     show : function()
19936     {
19937         this.fireEvent("beforeshow", this);
19938         
19939         this.hidden = false;
19940         this.el.addClass('open');
19941         
19942         Roo.get(document).on("mouseup", this.onMouseUp, this);
19943         
19944         this.fireEvent("show", this);
19945         
19946         
19947     },
19948     
19949     hide : function()
19950     {
19951         this.fireEvent("beforehide", this);
19952         
19953         this.hidden = true;
19954         this.el.removeClass('open');
19955         
19956         Roo.get(document).un("mouseup", this.onMouseUp);
19957         
19958         this.fireEvent("hide", this);
19959     },
19960     
19961     onMouseUp : function()
19962     {
19963         this.hide();
19964     }
19965     
19966 });
19967
19968  
19969  /*
19970  * - LGPL
19971  *
19972  * menu item
19973  * 
19974  */
19975 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
19976
19977 /**
19978  * @class Roo.bootstrap.menu.Item
19979  * @extends Roo.bootstrap.Component
19980  * Bootstrap MenuItem class
19981  * @cfg {Boolean} submenu (true | false) default false
19982  * @cfg {String} html text of the item
19983  * @cfg {String} href the link
19984  * @cfg {Boolean} disable (true | false) default false
19985  * @cfg {Boolean} preventDefault (true | false) default true
19986  * @cfg {String} icon Font awesome icon
19987  * @cfg {String} pos Submenu align to (left | right) default right 
19988  * 
19989  * 
19990  * @constructor
19991  * Create a new Item
19992  * @param {Object} config The config object
19993  */
19994
19995
19996 Roo.bootstrap.menu.Item = function(config){
19997     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
19998     this.addEvents({
19999         /**
20000          * @event mouseover
20001          * Fires when the mouse is hovering over this menu
20002          * @param {Roo.bootstrap.menu.Item} this
20003          * @param {Roo.EventObject} e
20004          */
20005         mouseover : true,
20006         /**
20007          * @event mouseout
20008          * Fires when the mouse exits this menu
20009          * @param {Roo.bootstrap.menu.Item} this
20010          * @param {Roo.EventObject} e
20011          */
20012         mouseout : true,
20013         // raw events
20014         /**
20015          * @event click
20016          * The raw click event for the entire grid.
20017          * @param {Roo.EventObject} e
20018          */
20019         click : true
20020     });
20021 };
20022
20023 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
20024     
20025     submenu : false,
20026     href : '',
20027     html : '',
20028     preventDefault: true,
20029     disable : false,
20030     icon : false,
20031     pos : 'right',
20032     
20033     getAutoCreate : function()
20034     {
20035         var text = [
20036             {
20037                 tag : 'span',
20038                 cls : 'roo-menu-item-text',
20039                 html : this.html
20040             }
20041         ];
20042         
20043         if(this.icon){
20044             text.unshift({
20045                 tag : 'i',
20046                 cls : 'fa ' + this.icon
20047             })
20048         }
20049         
20050         var cfg = {
20051             tag : 'li',
20052             cn : [
20053                 {
20054                     tag : 'a',
20055                     href : this.href || '#',
20056                     cn : text
20057                 }
20058             ]
20059         };
20060         
20061         if(this.disable){
20062             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
20063         }
20064         
20065         if(this.submenu){
20066             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
20067             
20068             if(this.pos == 'left'){
20069                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
20070             }
20071         }
20072         
20073         return cfg;
20074     },
20075     
20076     initEvents : function() 
20077     {
20078         this.el.on('mouseover', this.onMouseOver, this);
20079         this.el.on('mouseout', this.onMouseOut, this);
20080         
20081         this.el.select('a', true).first().on('click', this.onClick, this);
20082         
20083     },
20084     
20085     onClick : function(e)
20086     {
20087         if(this.preventDefault){
20088             e.preventDefault();
20089         }
20090         
20091         this.fireEvent("click", this, e);
20092     },
20093     
20094     onMouseOver : function(e)
20095     {
20096         if(this.submenu && this.pos == 'left'){
20097             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
20098         }
20099         
20100         this.fireEvent("mouseover", this, e);
20101     },
20102     
20103     onMouseOut : function(e)
20104     {
20105         this.fireEvent("mouseout", this, e);
20106     }
20107 });
20108
20109  
20110
20111  /*
20112  * - LGPL
20113  *
20114  * menu separator
20115  * 
20116  */
20117 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20118
20119 /**
20120  * @class Roo.bootstrap.menu.Separator
20121  * @extends Roo.bootstrap.Component
20122  * Bootstrap Separator class
20123  * 
20124  * @constructor
20125  * Create a new Separator
20126  * @param {Object} config The config object
20127  */
20128
20129
20130 Roo.bootstrap.menu.Separator = function(config){
20131     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
20132 };
20133
20134 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
20135     
20136     getAutoCreate : function(){
20137         var cfg = {
20138             tag : 'li',
20139             cls: 'divider'
20140         };
20141         
20142         return cfg;
20143     }
20144    
20145 });
20146
20147  
20148
20149