Roo/bootstrap/ComboBox.js
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * 
20  * @constructor
21  * Do not use directly - it does not do anything..
22  * @param {Object} config The config object
23  */
24
25
26
27 Roo.bootstrap.Component = function(config){
28     Roo.bootstrap.Component.superclass.constructor.call(this, config);
29 };
30
31 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
32     
33     
34     allowDomMove : false, // to stop relocations in parent onRender...
35     
36     cls : false,
37     
38     style : false,
39     
40     autoCreate : false,
41     
42     initEvents : function() {  },
43     
44     xattr : false,
45     
46     parentId : false,
47     
48     can_build_overlaid : true,
49     
50     dataId : false,
51     
52     name : false,
53     
54     parent: function() {
55         // returns the parent component..
56         return Roo.ComponentMgr.get(this.parentId)
57         
58         
59     },
60     
61     // private
62     onRender : function(ct, position)
63     {
64        // Roo.log("Call onRender: " + this.xtype);
65         
66         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
67         
68         if(this.el){
69             if (this.el.attr('xtype')) {
70                 this.el.attr('xtypex', this.el.attr('xtype'));
71                 this.el.dom.removeAttribute('xtype');
72                 
73                 this.initEvents();
74             }
75             
76             return;
77         }
78         
79          
80         
81         var cfg = Roo.apply({},  this.getAutoCreate());
82         cfg.id = Roo.id();
83         
84         // fill in the extra attributes 
85         if (this.xattr && typeof(this.xattr) =='object') {
86             for (var i in this.xattr) {
87                 cfg[i] = this.xattr[i];
88             }
89         }
90         
91         if(this.dataId){
92             cfg.dataId = this.dataId;
93         }
94         
95         if (this.cls) {
96             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
97         }
98         
99         if (this.style) { // fixme needs to support more complex style data.
100             cfg.style = this.style;
101         }
102         
103         if(this.name){
104             cfg.name = this.name;
105         }
106         
107         this.el = ct.createChild(cfg, position);
108         
109         if(this.tabIndex !== undefined){
110             this.el.dom.setAttribute('tabIndex', this.tabIndex);
111         }
112         this.initEvents();
113         
114         
115     },
116     
117     getChildContainer : function()
118     {
119         return this.el;
120     },
121     
122     
123     addxtype  : function(tree,cntr)
124     {
125         var cn = this;
126         
127         cn = Roo.factory(tree);
128            
129         cn.parentType = this.xtype; //??
130         cn.parentId = this.id;
131         
132         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
133         
134         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
135         
136         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
137         
138         var build_from_html =  Roo.XComponent.build_from_html;
139           
140         var is_body  = (tree.xtype == 'Body') ;
141           
142         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
143           
144         var self_cntr_el = Roo.get(this[cntr](false));
145         
146         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
147             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
148                 return this.addxtypeChild(tree,cntr);
149             }
150             
151             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
152                 
153             if(echild){
154                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
155             }
156             
157             Roo.log('skipping render');
158             return cn;
159             
160         }
161         
162         var ret = false;
163         
164         while (true) {
165             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
166             
167             if (!echild) {
168                 break;
169             }
170             
171             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
172                 break;
173             }
174             
175             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
176         }
177         return ret;
178     },
179     
180     addxtypeChild : function (tree, cntr)
181     {
182         Roo.log('addxtypeChild:' + cntr);
183         var cn = this;
184         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
185         
186         
187         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
188                     (typeof(tree['flexy:foreach']) != 'undefined');
189           
190         
191         
192         
193         // render the element if it's not BODY.
194         if (tree.xtype != 'Body') {
195            
196             cn = Roo.factory(tree);
197            
198             cn.parentType = this.xtype; //??
199             cn.parentId = this.id;
200             
201             var build_from_html =  Roo.XComponent.build_from_html;
202             
203             
204             // does the container contain child eleemnts with 'xtype' attributes.
205             // that match this xtype..
206             // note - when we render we create these as well..
207             // so we should check to see if body has xtype set.
208             if (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
209                
210                 var self_cntr_el = Roo.get(this[cntr](false));
211                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
212                 
213                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
214                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
215                   
216                   
217                   
218                     cn.el = echild;
219                   //  Roo.log("GOT");
220                     //echild.dom.removeAttribute('xtype');
221                 } else {
222                     Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
223                    
224                 }
225             }
226            
227             
228                
229             // if object has flexy:if - then it may or may not be rendered.
230             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
231                 // skip a flexy if element.
232                 Roo.log('skipping render');
233              } else {
234                  
235                 // actually if flexy:foreach is found, we really want to create 
236                 // multiple copies here...
237                 //Roo.log('render');
238                 //Roo.log(this[cntr]());
239                 cn.render(this[cntr](true));
240              }
241             // then add the element..
242         }
243         
244         
245         // handle the kids..
246         
247         var nitems = [];
248         /*
249         if (typeof (tree.menu) != 'undefined') {
250             tree.menu.parentType = cn.xtype;
251             tree.menu.triggerEl = cn.el;
252             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
253             
254         }
255         */
256         if (!tree.items || !tree.items.length) {
257             cn.items = nitems;
258             return cn;
259         }
260         var items = tree.items;
261         delete tree.items;
262         
263         //Roo.log(items.length);
264             // add the items..
265         for(var i =0;i < items.length;i++) {
266             nitems.push(cn.addxtype(Roo.apply({}, items[i])));
267         }
268         
269         cn.items = nitems;
270         
271         return cn;
272     }
273     
274     
275     
276     
277 });
278
279  /*
280  * - LGPL
281  *
282  * Body
283  * 
284  */
285
286 /**
287  * @class Roo.bootstrap.Body
288  * @extends Roo.bootstrap.Component
289  * Bootstrap Body class
290  * 
291  * @constructor
292  * Create a new body
293  * @param {Object} config The config object
294  */
295
296 Roo.bootstrap.Body = function(config){
297     Roo.bootstrap.Body.superclass.constructor.call(this, config);
298     this.el = Roo.get(document.body);
299     if (this.cls && this.cls.length) {
300         Roo.get(document.body).addClass(this.cls);
301     }
302 };
303
304 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
305       
306         autoCreate : {
307         cls: 'container'
308     },
309     onRender : function(ct, position)
310     {
311        /* Roo.log("Roo.bootstrap.Body - onRender");
312         if (this.cls && this.cls.length) {
313             Roo.get(document.body).addClass(this.cls);
314         }
315         // style??? xttr???
316         */
317     }
318     
319     
320  
321    
322 });
323
324  /*
325  * - LGPL
326  *
327  * button group
328  * 
329  */
330
331
332 /**
333  * @class Roo.bootstrap.ButtonGroup
334  * @extends Roo.bootstrap.Component
335  * Bootstrap ButtonGroup class
336  * @cfg {String} size lg | sm | xs (default empty normal)
337  * @cfg {String} align vertical | justified  (default none)
338  * @cfg {String} direction up | down (default down)
339  * @cfg {Boolean} toolbar false | true
340  * @cfg {Boolean} btn true | false
341  * 
342  * 
343  * @constructor
344  * Create a new Input
345  * @param {Object} config The config object
346  */
347
348 Roo.bootstrap.ButtonGroup = function(config){
349     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
350 };
351
352 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
353     
354     size: '',
355     align: '',
356     direction: '',
357     toolbar: false,
358     btn: true,
359
360     getAutoCreate : function(){
361         var cfg = {
362             cls: 'btn-group',
363             html : null
364         }
365         
366         cfg.html = this.html || cfg.html;
367         
368         if (this.toolbar) {
369             cfg = {
370                 cls: 'btn-toolbar',
371                 html: null
372             }
373             
374             return cfg;
375         }
376         
377         if (['vertical','justified'].indexOf(this.align)!==-1) {
378             cfg.cls = 'btn-group-' + this.align;
379             
380             if (this.align == 'justified') {
381                 console.log(this.items);
382             }
383         }
384         
385         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
386             cfg.cls += ' btn-group-' + this.size;
387         }
388         
389         if (this.direction == 'up') {
390             cfg.cls += ' dropup' ;
391         }
392         
393         return cfg;
394     }
395    
396 });
397
398  /*
399  * - LGPL
400  *
401  * button
402  * 
403  */
404
405 /**
406  * @class Roo.bootstrap.Button
407  * @extends Roo.bootstrap.Component
408  * Bootstrap Button class
409  * @cfg {String} html The button content
410  * @cfg {String} weight default (or empty) | primary | success | info | warning | danger | link
411  * @cfg {String} size empty | lg | sm | xs
412  * @cfg {String} tag empty | a | input | submit
413  * @cfg {String} href empty or href
414  * @cfg {Boolean} disabled false | true
415  * @cfg {Boolean} isClose false | true
416  * @cfg {String} glyphicon empty | adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out
417  * @cfg {String} badge text for badge
418  * @cfg {String} theme default (or empty) | glow
419  * @cfg {Boolean} inverse false | true
420  * @cfg {Boolean} toggle false | true
421  * @cfg {String} ontext text for on toggle state
422  * @cfg {String} offtext text for off toggle state
423  * @cfg {Boolean} defaulton true | false
424  * @cfg {Boolean} preventDefault (true | false) default true
425  * @cfg {Boolean} removeClass true | false remove the standard class..
426  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
427  * 
428  * @constructor
429  * Create a new button
430  * @param {Object} config The config object
431  */
432
433
434 Roo.bootstrap.Button = function(config){
435     Roo.bootstrap.Button.superclass.constructor.call(this, config);
436     this.addEvents({
437         // raw events
438         /**
439          * @event click
440          * When a butotn is pressed
441          * @param {Roo.EventObject} e
442          */
443         "click" : true,
444          /**
445          * @event toggle
446          * After the button has been toggles
447          * @param {Roo.EventObject} e
448          * @param {boolean} pressed (also available as button.pressed)
449          */
450         "toggle" : true
451     });
452 };
453
454 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
455     html: false,
456     active: false,
457     weight: '',
458     size: '',
459     tag: 'button',
460     href: '',
461     disabled: false,
462     isClose: false,
463     glyphicon: '',
464     badge: '',
465     theme: 'default',
466     inverse: false,
467     
468     toggle: false,
469     ontext: 'ON',
470     offtext: 'OFF',
471     defaulton: true,
472     preventDefault: true,
473     removeClass: false,
474     name: false,
475     target: false,
476     
477     
478     pressed : null,
479      
480     
481     getAutoCreate : function(){
482         
483         var cfg = {
484             tag : 'button',
485             cls : 'roo-button',
486             html: ''
487         };
488         
489         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
490             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
491             this.tag = 'button';
492         } else {
493             cfg.tag = this.tag;
494         }
495         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
496         
497         if (this.toggle == true) {
498             cfg={
499                 tag: 'div',
500                 cls: 'slider-frame roo-button',
501                 cn: [
502                     {
503                         tag: 'span',
504                         'data-on-text':'ON',
505                         'data-off-text':'OFF',
506                         cls: 'slider-button',
507                         html: this.offtext
508                     }
509                 ]
510             };
511             
512             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
513                 cfg.cls += ' '+this.weight;
514             }
515             
516             return cfg;
517         }
518         
519         if (this.isClose) {
520             cfg.cls += ' close';
521             
522             cfg["aria-hidden"] = true;
523             
524             cfg.html = "&times;";
525             
526             return cfg;
527         }
528         
529          
530         if (this.theme==='default') {
531             cfg.cls = 'btn roo-button';
532             
533             //if (this.parentType != 'Navbar') {
534             this.weight = this.weight.length ?  this.weight : 'default';
535             //}
536             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
537                 
538                 cfg.cls += ' btn-' + this.weight;
539             }
540         } else if (this.theme==='glow') {
541             
542             cfg.tag = 'a';
543             cfg.cls = 'btn-glow roo-button';
544             
545             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
546                 
547                 cfg.cls += ' ' + this.weight;
548             }
549         }
550    
551         
552         if (this.inverse) {
553             this.cls += ' inverse';
554         }
555         
556         
557         if (this.active) {
558             cfg.cls += ' active';
559         }
560         
561         if (this.disabled) {
562             cfg.disabled = 'disabled';
563         }
564         
565         if (this.items) {
566             Roo.log('changing to ul' );
567             cfg.tag = 'ul';
568             this.glyphicon = 'caret';
569         }
570         
571         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
572          
573         //gsRoo.log(this.parentType);
574         if (this.parentType === 'Navbar' && !this.parent().bar) {
575             Roo.log('changing to li?');
576             
577             cfg.tag = 'li';
578             
579             cfg.cls = '';
580             cfg.cn =  [{
581                 tag : 'a',
582                 cls : 'roo-button',
583                 html : this.html,
584                 href : this.href || '#'
585             }];
586             if (this.menu) {
587                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
588                 cfg.cls += ' dropdown';
589             }   
590             
591             delete cfg.html;
592             
593         }
594         
595        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
596         
597         if (this.glyphicon) {
598             cfg.html = ' ' + cfg.html;
599             
600             cfg.cn = [
601                 {
602                     tag: 'span',
603                     cls: 'glyphicon glyphicon-' + this.glyphicon
604                 }
605             ];
606         }
607         
608         if (this.badge) {
609             cfg.html += ' ';
610             
611             cfg.tag = 'a';
612             
613 //            cfg.cls='btn roo-button';
614             
615             cfg.href=this.href;
616             
617             var value = cfg.html;
618             
619             if(this.glyphicon){
620                 value = {
621                             tag: 'span',
622                             cls: 'glyphicon glyphicon-' + this.glyphicon,
623                             html: this.html
624                         };
625                 
626             }
627             
628             cfg.cn = [
629                 value,
630                 {
631                     tag: 'span',
632                     cls: 'badge',
633                     html: this.badge
634                 }
635             ];
636             
637             cfg.html='';
638         }
639         
640         if (this.menu) {
641             cfg.cls += ' dropdown';
642             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
643         }
644         
645         if (cfg.tag !== 'a' && this.href !== '') {
646             throw "Tag must be a to set href.";
647         } else if (this.href.length > 0) {
648             cfg.href = this.href;
649         }
650         
651         if(this.removeClass){
652             cfg.cls = '';
653         }
654         
655         if(this.target){
656             cfg.target = this.target;
657         }
658         
659         return cfg;
660     },
661     initEvents: function() {
662        // Roo.log('init events?');
663 //        Roo.log(this.el.dom);
664         // add the menu...
665         
666         if (typeof (this.menu) != 'undefined') {
667             this.menu.parentType = this.xtype;
668             this.menu.triggerEl = this.el;
669             this.addxtype(Roo.apply({}, this.menu));
670         }
671
672
673        if (this.el.hasClass('roo-button')) {
674             this.el.on('click', this.onClick, this);
675        } else {
676             this.el.select('.roo-button').on('click', this.onClick, this);
677        }
678        
679        if(this.removeClass){
680            this.el.on('click', this.onClick, this);
681        }
682        
683        this.el.enableDisplayMode();
684         
685     },
686     onClick : function(e)
687     {
688         if (this.disabled) {
689             return;
690         }
691         
692         Roo.log('button on click ');
693         if(this.preventDefault){
694             e.preventDefault();
695         }
696         if (this.pressed === true || this.pressed === false) {
697             this.pressed = !this.pressed;
698             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
699             this.fireEvent('toggle', this, e, this.pressed);
700         }
701         
702         
703         this.fireEvent('click', this, e);
704     },
705     
706     /**
707      * Enables this button
708      */
709     enable : function()
710     {
711         this.disabled = false;
712         this.el.removeClass('disabled');
713     },
714     
715     /**
716      * Disable this button
717      */
718     disable : function()
719     {
720         this.disabled = true;
721         this.el.addClass('disabled');
722     },
723      /**
724      * sets the active state on/off, 
725      * @param {Boolean} state (optional) Force a particular state
726      */
727     setActive : function(v) {
728         
729         this.el[v ? 'addClass' : 'removeClass']('active');
730     },
731      /**
732      * toggles the current active state 
733      */
734     toggleActive : function()
735     {
736        var active = this.el.hasClass('active');
737        this.setActive(!active);
738        
739         
740     },
741     setText : function(str)
742     {
743         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
744     },
745     getText : function()
746     {
747         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
748     },
749     hide: function() {
750        
751      
752         this.el.hide();   
753     },
754     show: function() {
755        
756         this.el.show();   
757     }
758     
759     
760 });
761
762  /*
763  * - LGPL
764  *
765  * column
766  * 
767  */
768
769 /**
770  * @class Roo.bootstrap.Column
771  * @extends Roo.bootstrap.Component
772  * Bootstrap Column class
773  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
774  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
775  * @cfg {Number} md colspan out of 12 for computer-sized screens
776  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
777  * @cfg {String} html content of column.
778  * 
779  * @constructor
780  * Create a new Column
781  * @param {Object} config The config object
782  */
783
784 Roo.bootstrap.Column = function(config){
785     Roo.bootstrap.Column.superclass.constructor.call(this, config);
786 };
787
788 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
789     
790     xs: null,
791     sm: null,
792     md: null,
793     lg: null,
794     html: '',
795     offset: 0,
796     
797     getAutoCreate : function(){
798         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
799         
800         cfg = {
801             tag: 'div',
802             cls: 'column'
803         };
804         
805         var settings=this;
806         ['xs','sm','md','lg'].map(function(size){
807             if (settings[size]) {
808                 cfg.cls += ' col-' + size + '-' + settings[size];
809             }
810         });
811         if (this.html.length) {
812             cfg.html = this.html;
813         }
814         
815         return cfg;
816     }
817    
818 });
819
820  
821
822  /*
823  * - LGPL
824  *
825  * page container.
826  * 
827  */
828
829
830 /**
831  * @class Roo.bootstrap.Container
832  * @extends Roo.bootstrap.Component
833  * Bootstrap Container class
834  * @cfg {Boolean} jumbotron is it a jumbotron element
835  * @cfg {String} html content of element
836  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
837  * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
838  * @cfg {String} header content of header (for panel)
839  * @cfg {String} footer content of footer (for panel)
840  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
841  * @cfg {String} tag (header|aside|section) type of HTML tag.
842
843  *     
844  * @constructor
845  * Create a new Container
846  * @param {Object} config The config object
847  */
848
849 Roo.bootstrap.Container = function(config){
850     Roo.bootstrap.Container.superclass.constructor.call(this, config);
851 };
852
853 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
854     
855     jumbotron : false,
856     well: '',
857     panel : '',
858     header: '',
859     footer : '',
860     sticky: '',
861     tag : false,
862   
863      
864     getChildContainer : function() {
865         
866         if(!this.el){
867             return false;
868         }
869         
870         if (this.panel.length) {
871             return this.el.select('.panel-body',true).first();
872         }
873         
874         return this.el;
875     },
876     
877     
878     getAutoCreate : function(){
879         
880         var cfg = {
881             tag : this.tag || 'div',
882             html : '',
883             cls : ''
884         };
885         if (this.jumbotron) {
886             cfg.cls = 'jumbotron';
887         }
888         // - this is applied by the parent..
889         //if (this.cls) {
890         //    cfg.cls = this.cls + '';
891         //}
892         
893         if (this.sticky.length) {
894             
895             var bd = Roo.get(document.body);
896             if (!bd.hasClass('bootstrap-sticky')) {
897                 bd.addClass('bootstrap-sticky');
898                 Roo.select('html',true).setStyle('height', '100%');
899             }
900              
901             cfg.cls += 'bootstrap-sticky-' + this.sticky;
902         }
903         
904         
905         if (this.well.length) {
906             switch (this.well) {
907                 case 'lg':
908                 case 'sm':
909                     cfg.cls +=' well well-' +this.well;
910                     break;
911                 default:
912                     cfg.cls +=' well';
913                     break;
914             }
915         }
916         
917         var body = cfg;
918         
919         if (this.panel.length) {
920             cfg.cls += ' panel panel-' + this.panel;
921             cfg.cn = [];
922             if (this.header.length) {
923                 cfg.cn.push({
924                     
925                     cls : 'panel-heading',
926                     cn : [{
927                         tag: 'h3',
928                         cls : 'panel-title',
929                         html : this.header
930                     }]
931                     
932                 });
933             }
934             body = false;
935             cfg.cn.push({
936                 cls : 'panel-body',
937                 html : this.html
938             });
939             
940             
941             if (this.footer.length) {
942                 cfg.cn.push({
943                     cls : 'panel-footer',
944                     html : this.footer
945                     
946                 });
947             }
948             
949         }
950         
951         if (body) {
952             body.html = this.html || cfg.html;
953         }
954         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
955             cfg.cls =  'container';
956         }
957         
958         return cfg;
959     },
960     
961     titleEl : function()
962     {
963         if(!this.el || !this.panel.length || !this.header.length){
964             return;
965         }
966         
967         return this.el.select('.panel-title',true).first();
968     },
969     
970     setTitle : function(v)
971     {
972         var titleEl = this.titleEl();
973         
974         if(!titleEl){
975             return;
976         }
977         
978         titleEl.dom.innerHTML = v;
979     },
980     
981     getTitle : function()
982     {
983         
984         var titleEl = this.titleEl();
985         
986         if(!titleEl){
987             return '';
988         }
989         
990         return titleEl.dom.innerHTML;
991     }
992    
993 });
994
995  /*
996  * - LGPL
997  *
998  * image
999  * 
1000  */
1001
1002
1003 /**
1004  * @class Roo.bootstrap.Img
1005  * @extends Roo.bootstrap.Component
1006  * Bootstrap Img class
1007  * @cfg {Boolean} imgResponsive false | true
1008  * @cfg {String} border rounded | circle | thumbnail
1009  * @cfg {String} src image source
1010  * @cfg {String} alt image alternative text
1011  * @cfg {String} href a tag href
1012  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1013  * 
1014  * @constructor
1015  * Create a new Input
1016  * @param {Object} config The config object
1017  */
1018
1019 Roo.bootstrap.Img = function(config){
1020     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1021     
1022     this.addEvents({
1023         // img events
1024         /**
1025          * @event click
1026          * The img click event for the img.
1027          * @param {Roo.EventObject} e
1028          */
1029         "click" : true
1030     });
1031 };
1032
1033 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1034     
1035     imgResponsive: true,
1036     border: '',
1037     src: '',
1038     href: false,
1039     target: false,
1040
1041     getAutoCreate : function(){
1042         
1043         var cfg = {
1044             tag: 'img',
1045             cls: (this.imgResponsive) ? 'img-responsive' : '',
1046             html : null
1047         }
1048         
1049         cfg.html = this.html || cfg.html;
1050         
1051         cfg.src = this.src || cfg.src;
1052         
1053         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1054             cfg.cls += ' img-' + this.border;
1055         }
1056         
1057         if(this.alt){
1058             cfg.alt = this.alt;
1059         }
1060         
1061         if(this.href){
1062             var a = {
1063                 tag: 'a',
1064                 href: this.href,
1065                 cn: [
1066                     cfg
1067                 ]
1068             }
1069             
1070             if(this.target){
1071                 a.target = this.target;
1072             }
1073             
1074         }
1075         
1076         
1077         return (this.href) ? a : cfg;
1078     },
1079     
1080     initEvents: function() {
1081         
1082         if(!this.href){
1083             this.el.on('click', this.onClick, this);
1084         }
1085     },
1086     
1087     onClick : function(e)
1088     {
1089         Roo.log('img onclick');
1090         this.fireEvent('click', this, e);
1091     }
1092    
1093 });
1094
1095  /*
1096  * - LGPL
1097  *
1098  * image
1099  * 
1100  */
1101
1102
1103 /**
1104  * @class Roo.bootstrap.Link
1105  * @extends Roo.bootstrap.Component
1106  * Bootstrap Link Class
1107  * @cfg {String} alt image alternative text
1108  * @cfg {String} href a tag href
1109  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1110  * @cfg {String} html the content of the link.
1111  * @cfg {Boolean} preventDefault (true | false) default false
1112
1113  * 
1114  * @constructor
1115  * Create a new Input
1116  * @param {Object} config The config object
1117  */
1118
1119 Roo.bootstrap.Link = function(config){
1120     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1121     
1122     this.addEvents({
1123         // img events
1124         /**
1125          * @event click
1126          * The img click event for the img.
1127          * @param {Roo.EventObject} e
1128          */
1129         "click" : true
1130     });
1131 };
1132
1133 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1134     
1135     href: false,
1136     target: false,
1137     preventDefault: false,
1138
1139     getAutoCreate : function(){
1140         
1141         var cfg = {
1142             tag: 'a',
1143             html : this.html || 'html-missing'
1144         }
1145         
1146         
1147         if(this.alt){
1148             cfg.alt = this.alt;
1149         }
1150         cfg.href = this.href || '#';
1151         if(this.target){
1152             cfg.target = this.target;
1153         }
1154         
1155         return cfg;
1156     },
1157     
1158     initEvents: function() {
1159         
1160         if(!this.href || this.preventDefault){
1161             this.el.on('click', this.onClick, this);
1162         }
1163     },
1164     
1165     onClick : function(e)
1166     {
1167         if(this.preventDefault){
1168             e.preventDefault();
1169         }
1170         //Roo.log('img onclick');
1171         this.fireEvent('click', this, e);
1172     }
1173    
1174 });
1175
1176  /*
1177  * - LGPL
1178  *
1179  * header
1180  * 
1181  */
1182
1183 /**
1184  * @class Roo.bootstrap.Header
1185  * @extends Roo.bootstrap.Component
1186  * Bootstrap Header class
1187  * @cfg {String} html content of header
1188  * @cfg {Number} level (1|2|3|4|5|6) default 1
1189  * 
1190  * @constructor
1191  * Create a new Header
1192  * @param {Object} config The config object
1193  */
1194
1195
1196 Roo.bootstrap.Header  = function(config){
1197     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1198 };
1199
1200 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1201     
1202     //href : false,
1203     html : false,
1204     level : 1,
1205     
1206     
1207     
1208     getAutoCreate : function(){
1209         
1210         var cfg = {
1211             tag: 'h' + (1 *this.level),
1212             html: this.html || 'fill in html'
1213         } ;
1214         
1215         return cfg;
1216     }
1217    
1218 });
1219
1220  
1221
1222  /*
1223  * Based on:
1224  * Ext JS Library 1.1.1
1225  * Copyright(c) 2006-2007, Ext JS, LLC.
1226  *
1227  * Originally Released Under LGPL - original licence link has changed is not relivant.
1228  *
1229  * Fork - LGPL
1230  * <script type="text/javascript">
1231  */
1232  
1233 /**
1234  * @class Roo.bootstrap.MenuMgr
1235  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1236  * @singleton
1237  */
1238 Roo.bootstrap.MenuMgr = function(){
1239    var menus, active, groups = {}, attached = false, lastShow = new Date();
1240
1241    // private - called when first menu is created
1242    function init(){
1243        menus = {};
1244        active = new Roo.util.MixedCollection();
1245        Roo.get(document).addKeyListener(27, function(){
1246            if(active.length > 0){
1247                hideAll();
1248            }
1249        });
1250    }
1251
1252    // private
1253    function hideAll(){
1254        if(active && active.length > 0){
1255            var c = active.clone();
1256            c.each(function(m){
1257                m.hide();
1258            });
1259        }
1260    }
1261
1262    // private
1263    function onHide(m){
1264        active.remove(m);
1265        if(active.length < 1){
1266            Roo.get(document).un("mouseup", onMouseDown);
1267             
1268            attached = false;
1269        }
1270    }
1271
1272    // private
1273    function onShow(m){
1274        var last = active.last();
1275        lastShow = new Date();
1276        active.add(m);
1277        if(!attached){
1278           Roo.get(document).on("mouseup", onMouseDown);
1279            
1280            attached = true;
1281        }
1282        if(m.parentMenu){
1283           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1284           m.parentMenu.activeChild = m;
1285        }else if(last && last.isVisible()){
1286           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1287        }
1288    }
1289
1290    // private
1291    function onBeforeHide(m){
1292        if(m.activeChild){
1293            m.activeChild.hide();
1294        }
1295        if(m.autoHideTimer){
1296            clearTimeout(m.autoHideTimer);
1297            delete m.autoHideTimer;
1298        }
1299    }
1300
1301    // private
1302    function onBeforeShow(m){
1303        var pm = m.parentMenu;
1304        if(!pm && !m.allowOtherMenus){
1305            hideAll();
1306        }else if(pm && pm.activeChild && active != m){
1307            pm.activeChild.hide();
1308        }
1309    }
1310
1311    // private
1312    function onMouseDown(e){
1313         Roo.log("on MouseDown");
1314         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
1315            hideAll();
1316         }
1317         
1318         
1319    }
1320
1321    // private
1322    function onBeforeCheck(mi, state){
1323        if(state){
1324            var g = groups[mi.group];
1325            for(var i = 0, l = g.length; i < l; i++){
1326                if(g[i] != mi){
1327                    g[i].setChecked(false);
1328                }
1329            }
1330        }
1331    }
1332
1333    return {
1334
1335        /**
1336         * Hides all menus that are currently visible
1337         */
1338        hideAll : function(){
1339             hideAll();  
1340        },
1341
1342        // private
1343        register : function(menu){
1344            if(!menus){
1345                init();
1346            }
1347            menus[menu.id] = menu;
1348            menu.on("beforehide", onBeforeHide);
1349            menu.on("hide", onHide);
1350            menu.on("beforeshow", onBeforeShow);
1351            menu.on("show", onShow);
1352            var g = menu.group;
1353            if(g && menu.events["checkchange"]){
1354                if(!groups[g]){
1355                    groups[g] = [];
1356                }
1357                groups[g].push(menu);
1358                menu.on("checkchange", onCheck);
1359            }
1360        },
1361
1362         /**
1363          * Returns a {@link Roo.menu.Menu} object
1364          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1365          * be used to generate and return a new Menu instance.
1366          */
1367        get : function(menu){
1368            if(typeof menu == "string"){ // menu id
1369                return menus[menu];
1370            }else if(menu.events){  // menu instance
1371                return menu;
1372            }
1373            /*else if(typeof menu.length == 'number'){ // array of menu items?
1374                return new Roo.bootstrap.Menu({items:menu});
1375            }else{ // otherwise, must be a config
1376                return new Roo.bootstrap.Menu(menu);
1377            }
1378            */
1379            return false;
1380        },
1381
1382        // private
1383        unregister : function(menu){
1384            delete menus[menu.id];
1385            menu.un("beforehide", onBeforeHide);
1386            menu.un("hide", onHide);
1387            menu.un("beforeshow", onBeforeShow);
1388            menu.un("show", onShow);
1389            var g = menu.group;
1390            if(g && menu.events["checkchange"]){
1391                groups[g].remove(menu);
1392                menu.un("checkchange", onCheck);
1393            }
1394        },
1395
1396        // private
1397        registerCheckable : function(menuItem){
1398            var g = menuItem.group;
1399            if(g){
1400                if(!groups[g]){
1401                    groups[g] = [];
1402                }
1403                groups[g].push(menuItem);
1404                menuItem.on("beforecheckchange", onBeforeCheck);
1405            }
1406        },
1407
1408        // private
1409        unregisterCheckable : function(menuItem){
1410            var g = menuItem.group;
1411            if(g){
1412                groups[g].remove(menuItem);
1413                menuItem.un("beforecheckchange", onBeforeCheck);
1414            }
1415        }
1416    };
1417 }();/*
1418  * - LGPL
1419  *
1420  * menu
1421  * 
1422  */
1423
1424 /**
1425  * @class Roo.bootstrap.Menu
1426  * @extends Roo.bootstrap.Component
1427  * Bootstrap Menu class - container for MenuItems
1428  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1429  * 
1430  * @constructor
1431  * Create a new Menu
1432  * @param {Object} config The config object
1433  */
1434
1435
1436 Roo.bootstrap.Menu = function(config){
1437     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1438     if (this.registerMenu) {
1439         Roo.bootstrap.MenuMgr.register(this);
1440     }
1441     this.addEvents({
1442         /**
1443          * @event beforeshow
1444          * Fires before this menu is displayed
1445          * @param {Roo.menu.Menu} this
1446          */
1447         beforeshow : true,
1448         /**
1449          * @event beforehide
1450          * Fires before this menu is hidden
1451          * @param {Roo.menu.Menu} this
1452          */
1453         beforehide : true,
1454         /**
1455          * @event show
1456          * Fires after this menu is displayed
1457          * @param {Roo.menu.Menu} this
1458          */
1459         show : true,
1460         /**
1461          * @event hide
1462          * Fires after this menu is hidden
1463          * @param {Roo.menu.Menu} this
1464          */
1465         hide : true,
1466         /**
1467          * @event click
1468          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1469          * @param {Roo.menu.Menu} this
1470          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1471          * @param {Roo.EventObject} e
1472          */
1473         click : true,
1474         /**
1475          * @event mouseover
1476          * Fires when the mouse is hovering over this menu
1477          * @param {Roo.menu.Menu} this
1478          * @param {Roo.EventObject} e
1479          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1480          */
1481         mouseover : true,
1482         /**
1483          * @event mouseout
1484          * Fires when the mouse exits this menu
1485          * @param {Roo.menu.Menu} this
1486          * @param {Roo.EventObject} e
1487          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1488          */
1489         mouseout : true,
1490         /**
1491          * @event itemclick
1492          * Fires when a menu item contained in this menu is clicked
1493          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1494          * @param {Roo.EventObject} e
1495          */
1496         itemclick: true
1497     });
1498     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1499 };
1500
1501 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1502     
1503    /// html : false,
1504     //align : '',
1505     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1506     type: false,
1507     /**
1508      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1509      */
1510     registerMenu : true,
1511     
1512     menuItems :false, // stores the menu items..
1513     
1514     hidden:true,
1515     
1516     parentMenu : false,
1517     
1518     getChildContainer : function() {
1519         return this.el;  
1520     },
1521     
1522     getAutoCreate : function(){
1523          
1524         //if (['right'].indexOf(this.align)!==-1) {
1525         //    cfg.cn[1].cls += ' pull-right'
1526         //}
1527         
1528         
1529         var cfg = {
1530             tag : 'ul',
1531             cls : 'dropdown-menu' ,
1532             style : 'z-index:1000'
1533             
1534         }
1535         
1536         if (this.type === 'submenu') {
1537             cfg.cls = 'submenu active';
1538         }
1539         if (this.type === 'treeview') {
1540             cfg.cls = 'treeview-menu';
1541         }
1542         
1543         return cfg;
1544     },
1545     initEvents : function() {
1546         
1547        // Roo.log("ADD event");
1548        // Roo.log(this.triggerEl.dom);
1549         this.triggerEl.on('click', this.onTriggerPress, this);
1550         this.triggerEl.addClass('dropdown-toggle');
1551         this.el.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
1552
1553         this.el.on("mouseover", this.onMouseOver, this);
1554         this.el.on("mouseout", this.onMouseOut, this);
1555         
1556         
1557     },
1558     findTargetItem : function(e){
1559         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
1560         if(!t){
1561             return false;
1562         }
1563         //Roo.log(t);         Roo.log(t.id);
1564         if(t && t.id){
1565             //Roo.log(this.menuitems);
1566             return this.menuitems.get(t.id);
1567             
1568             //return this.items.get(t.menuItemId);
1569         }
1570         
1571         return false;
1572     },
1573     onClick : function(e){
1574         Roo.log("menu.onClick");
1575         var t = this.findTargetItem(e);
1576         if(!t){
1577             return;
1578         }
1579         Roo.log(e);
1580         /*
1581         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
1582             if(t == this.activeItem && t.shouldDeactivate(e)){
1583                 this.activeItem.deactivate();
1584                 delete this.activeItem;
1585                 return;
1586             }
1587             if(t.canActivate){
1588                 this.setActiveItem(t, true);
1589             }
1590             return;
1591             
1592             
1593         }
1594         */
1595         Roo.log('pass click event');
1596         
1597         t.onClick(e);
1598         
1599         this.fireEvent("click", this, t, e);
1600         
1601         this.hide();
1602     },
1603      onMouseOver : function(e){
1604         var t  = this.findTargetItem(e);
1605         //Roo.log(t);
1606         //if(t){
1607         //    if(t.canActivate && !t.disabled){
1608         //        this.setActiveItem(t, true);
1609         //    }
1610         //}
1611         
1612         this.fireEvent("mouseover", this, e, t);
1613     },
1614     isVisible : function(){
1615         return !this.hidden;
1616     },
1617      onMouseOut : function(e){
1618         var t  = this.findTargetItem(e);
1619         
1620         //if(t ){
1621         //    if(t == this.activeItem && t.shouldDeactivate(e)){
1622         //        this.activeItem.deactivate();
1623         //        delete this.activeItem;
1624         //    }
1625         //}
1626         this.fireEvent("mouseout", this, e, t);
1627     },
1628     
1629     
1630     /**
1631      * Displays this menu relative to another element
1632      * @param {String/HTMLElement/Roo.Element} element The element to align to
1633      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
1634      * the element (defaults to this.defaultAlign)
1635      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1636      */
1637     show : function(el, pos, parentMenu){
1638         this.parentMenu = parentMenu;
1639         if(!this.el){
1640             this.render();
1641         }
1642         this.fireEvent("beforeshow", this);
1643         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
1644     },
1645      /**
1646      * Displays this menu at a specific xy position
1647      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
1648      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1649      */
1650     showAt : function(xy, parentMenu, /* private: */_e){
1651         this.parentMenu = parentMenu;
1652         if(!this.el){
1653             this.render();
1654         }
1655         if(_e !== false){
1656             this.fireEvent("beforeshow", this);
1657             
1658             //xy = this.el.adjustForConstraints(xy);
1659         }
1660         //this.el.setXY(xy);
1661         //this.el.show();
1662         this.hideMenuItems();
1663         this.hidden = false;
1664         this.triggerEl.addClass('open');
1665         this.focus();
1666         this.fireEvent("show", this);
1667     },
1668     
1669     focus : function(){
1670         return;
1671         if(!this.hidden){
1672             this.doFocus.defer(50, this);
1673         }
1674     },
1675
1676     doFocus : function(){
1677         if(!this.hidden){
1678             this.focusEl.focus();
1679         }
1680     },
1681
1682     /**
1683      * Hides this menu and optionally all parent menus
1684      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
1685      */
1686     hide : function(deep){
1687         
1688         this.hideMenuItems();
1689         if(this.el && this.isVisible()){
1690             this.fireEvent("beforehide", this);
1691             if(this.activeItem){
1692                 this.activeItem.deactivate();
1693                 this.activeItem = null;
1694             }
1695             this.triggerEl.removeClass('open');;
1696             this.hidden = true;
1697             this.fireEvent("hide", this);
1698         }
1699         if(deep === true && this.parentMenu){
1700             this.parentMenu.hide(true);
1701         }
1702     },
1703     
1704     onTriggerPress  : function(e)
1705     {
1706         
1707         Roo.log('trigger press');
1708         //Roo.log(e.getTarget());
1709        // Roo.log(this.triggerEl.dom);
1710         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
1711             return;
1712         }
1713         if (this.isVisible()) {
1714             Roo.log('hide');
1715             this.hide();
1716         } else {
1717             this.show(this.triggerEl, false, false);
1718         }
1719         
1720         
1721     },
1722     
1723          
1724        
1725     
1726     hideMenuItems : function()
1727     {
1728         //$(backdrop).remove()
1729         Roo.select('.open',true).each(function(aa) {
1730             
1731             aa.removeClass('open');
1732           //var parent = getParent($(this))
1733           //var relatedTarget = { relatedTarget: this }
1734           
1735            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
1736           //if (e.isDefaultPrevented()) return
1737            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
1738         })
1739     },
1740     addxtypeChild : function (tree, cntr) {
1741         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
1742           
1743         this.menuitems.add(comp);
1744         return comp;
1745
1746     },
1747     getEl : function()
1748     {
1749         Roo.log(this.el);
1750         return this.el;
1751     }
1752 });
1753
1754  
1755  /*
1756  * - LGPL
1757  *
1758  * menu item
1759  * 
1760  */
1761
1762
1763 /**
1764  * @class Roo.bootstrap.MenuItem
1765  * @extends Roo.bootstrap.Component
1766  * Bootstrap MenuItem class
1767  * @cfg {String} html the menu label
1768  * @cfg {String} href the link
1769  * @cfg {Boolean} preventDefault (true | false) default true
1770  * 
1771  * 
1772  * @constructor
1773  * Create a new MenuItem
1774  * @param {Object} config The config object
1775  */
1776
1777
1778 Roo.bootstrap.MenuItem = function(config){
1779     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
1780     this.addEvents({
1781         // raw events
1782         /**
1783          * @event click
1784          * The raw click event for the entire grid.
1785          * @param {Roo.EventObject} e
1786          */
1787         "click" : true
1788     });
1789 };
1790
1791 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
1792     
1793     href : false,
1794     html : false,
1795     preventDefault: true,
1796     
1797     getAutoCreate : function(){
1798         var cfg= {
1799             tag: 'li',
1800             cls: 'dropdown-menu-item',
1801             cn: [
1802                     {
1803                         tag : 'a',
1804                         href : '#',
1805                         html : 'Link'
1806                     }
1807                 ]
1808         };
1809         if (this.parent().type == 'treeview') {
1810             cfg.cls = 'treeview-menu';
1811         }
1812         
1813         cfg.cn[0].href = this.href || cfg.cn[0].href ;
1814         cfg.cn[0].html = this.html || cfg.cn[0].html ;
1815         return cfg;
1816     },
1817     
1818     initEvents: function() {
1819         
1820         //this.el.select('a').on('click', this.onClick, this);
1821         
1822     },
1823     onClick : function(e)
1824     {
1825         Roo.log('item on click ');
1826         //if(this.preventDefault){
1827         //    e.preventDefault();
1828         //}
1829         //this.parent().hideMenuItems();
1830         
1831         this.fireEvent('click', this, e);
1832     },
1833     getEl : function()
1834     {
1835         return this.el;
1836     }
1837 });
1838
1839  
1840
1841  /*
1842  * - LGPL
1843  *
1844  * menu separator
1845  * 
1846  */
1847
1848
1849 /**
1850  * @class Roo.bootstrap.MenuSeparator
1851  * @extends Roo.bootstrap.Component
1852  * Bootstrap MenuSeparator class
1853  * 
1854  * @constructor
1855  * Create a new MenuItem
1856  * @param {Object} config The config object
1857  */
1858
1859
1860 Roo.bootstrap.MenuSeparator = function(config){
1861     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
1862 };
1863
1864 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
1865     
1866     getAutoCreate : function(){
1867         var cfg = {
1868             cls: 'divider',
1869             tag : 'li'
1870         };
1871         
1872         return cfg;
1873     }
1874    
1875 });
1876
1877  
1878
1879  
1880 /*
1881 <div class="modal fade">
1882   <div class="modal-dialog">
1883     <div class="modal-content">
1884       <div class="modal-header">
1885         <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
1886         <h4 class="modal-title">Modal title</h4>
1887       </div>
1888       <div class="modal-body">
1889         <p>One fine body&hellip;</p>
1890       </div>
1891       <div class="modal-footer">
1892         <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
1893         <button type="button" class="btn btn-primary">Save changes</button>
1894       </div>
1895     </div><!-- /.modal-content -->
1896   </div><!-- /.modal-dialog -->
1897 </div><!-- /.modal -->
1898 */
1899 /*
1900  * - LGPL
1901  *
1902  * page contgainer.
1903  * 
1904  */
1905
1906 /**
1907  * @class Roo.bootstrap.Modal
1908  * @extends Roo.bootstrap.Component
1909  * Bootstrap Modal class
1910  * @cfg {String} title Title of dialog
1911  * @cfg {Boolean} specificTitle (true|false) default false
1912  * @cfg {Array} buttons Array of buttons or standard button set..
1913  * @cfg {String} buttonPosition (left|right|center) default right
1914  * 
1915  * @constructor
1916  * Create a new Modal Dialog
1917  * @param {Object} config The config object
1918  */
1919
1920 Roo.bootstrap.Modal = function(config){
1921     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
1922     this.addEvents({
1923         // raw events
1924         /**
1925          * @event btnclick
1926          * The raw btnclick event for the button
1927          * @param {Roo.EventObject} e
1928          */
1929         "btnclick" : true
1930     });
1931     this.buttons = this.buttons || [];
1932 };
1933
1934 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
1935     
1936     title : 'test dialog',
1937    
1938     buttons : false,
1939     
1940     // set on load...
1941     body:  false,
1942     
1943     specificTitle: false,
1944     
1945     buttonPosition: 'right',
1946     
1947     onRender : function(ct, position)
1948     {
1949         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
1950      
1951         if(!this.el){
1952             var cfg = Roo.apply({},  this.getAutoCreate());
1953             cfg.id = Roo.id();
1954             //if(!cfg.name){
1955             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
1956             //}
1957             //if (!cfg.name.length) {
1958             //    delete cfg.name;
1959            // }
1960             if (this.cls) {
1961                 cfg.cls += ' ' + this.cls;
1962             }
1963             if (this.style) {
1964                 cfg.style = this.style;
1965             }
1966             this.el = Roo.get(document.body).createChild(cfg, position);
1967         }
1968         //var type = this.el.dom.type;
1969         
1970         if(this.tabIndex !== undefined){
1971             this.el.dom.setAttribute('tabIndex', this.tabIndex);
1972         }
1973         
1974         
1975         
1976         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
1977         this.maskEl.enableDisplayMode("block");
1978         this.maskEl.hide();
1979         //this.el.addClass("x-dlg-modal");
1980     
1981         if (this.buttons.length) {
1982             Roo.each(this.buttons, function(bb) {
1983                 b = Roo.apply({}, bb);
1984                 b.xns = b.xns || Roo.bootstrap;
1985                 b.xtype = b.xtype || 'Button';
1986                 if (typeof(b.listeners) == 'undefined') {
1987                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
1988                 }
1989                 
1990                 var btn = Roo.factory(b);
1991                 
1992                 btn.onRender(this.el.select('.modal-footer div').first());
1993                 
1994             },this);
1995         }
1996         // render the children.
1997         var nitems = [];
1998         
1999         if(typeof(this.items) != 'undefined'){
2000             var items = this.items;
2001             delete this.items;
2002
2003             for(var i =0;i < items.length;i++) {
2004                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2005             }
2006         }
2007         
2008         this.items = nitems;
2009         
2010         this.body = this.el.select('.modal-body',true).first();
2011         this.close = this.el.select('.modal-header .close', true).first();
2012         this.footer = this.el.select('.modal-footer',true).first();
2013         this.initEvents();
2014         //this.el.addClass([this.fieldClass, this.cls]);
2015         
2016     },
2017     getAutoCreate : function(){
2018         
2019         
2020         var bdy = {
2021                 cls : 'modal-body',
2022                 html : this.html || ''
2023         };
2024         
2025         var title = {
2026             tag: 'h4',
2027             cls : 'modal-title',
2028             html : this.title
2029         };
2030         
2031         if(this.specificTitle){
2032             title = this.title;
2033         };
2034         
2035         return modal = {
2036             cls: "modal fade",
2037             style : 'display: none',
2038             cn : [
2039                 {
2040                     cls: "modal-dialog",
2041                     cn : [
2042                         {
2043                             cls : "modal-content",
2044                             cn : [
2045                                 {
2046                                     cls : 'modal-header',
2047                                     cn : [
2048                                         {
2049                                             tag: 'button',
2050                                             cls : 'close',
2051                                             html : '&times'
2052                                         },
2053                                         title
2054                                     ]
2055                                 },
2056                                 bdy,
2057                                 {
2058                                     cls : 'modal-footer',
2059                                     cn : [
2060                                         {
2061                                             tag: 'div',
2062                                             cls: 'btn-' + this.buttonPosition
2063                                         }
2064                                     ]
2065                                     
2066                                 }
2067                                 
2068                                 
2069                             ]
2070                             
2071                         }
2072                     ]
2073                         
2074                 }
2075             ]
2076             
2077             
2078         };
2079           
2080     },
2081     getChildContainer : function() {
2082          
2083          return this.el.select('.modal-body',true).first();
2084         
2085     },
2086     getButtonContainer : function() {
2087          return this.el.select('.modal-footer div',true).first();
2088         
2089     },
2090     initEvents : function()
2091     {
2092         this.el.select('.modal-header .close').on('click', this.hide, this);
2093 //        
2094 //        this.addxtype(this);
2095     },
2096     show : function() {
2097         
2098         if (!this.rendered) {
2099             this.render();
2100         }
2101        
2102         this.el.addClass('on');
2103         this.el.removeClass('fade');
2104         this.el.setStyle('display', 'block');
2105         Roo.get(document.body).addClass("x-body-masked");
2106         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2107         this.maskEl.show();
2108         this.el.setStyle('zIndex', '10001');
2109         this.fireEvent('show', this);
2110         
2111         
2112     },
2113     hide : function()
2114     {
2115         Roo.log('Modal hide?!');
2116         this.maskEl.hide();
2117         Roo.get(document.body).removeClass("x-body-masked");
2118         this.el.removeClass('on');
2119         this.el.addClass('fade');
2120         this.el.setStyle('display', 'none');
2121         this.fireEvent('hide', this);
2122     },
2123     
2124     addButton : function(str, cb)
2125     {
2126          
2127         
2128         var b = Roo.apply({}, { html : str } );
2129         b.xns = b.xns || Roo.bootstrap;
2130         b.xtype = b.xtype || 'Button';
2131         if (typeof(b.listeners) == 'undefined') {
2132             b.listeners = { click : cb.createDelegate(this)  };
2133         }
2134         
2135         var btn = Roo.factory(b);
2136            
2137         btn.onRender(this.el.select('.modal-footer div').first());
2138         
2139         return btn;   
2140        
2141     },
2142     
2143     setDefaultButton : function(btn)
2144     {
2145         //this.el.select('.modal-footer').()
2146     },
2147     resizeTo: function(w,h)
2148     {
2149         // skip..
2150     },
2151     setContentSize  : function(w, h)
2152     {
2153         
2154     },
2155     onButtonClick: function(btn,e)
2156     {
2157         //Roo.log([a,b,c]);
2158         this.fireEvent('btnclick', btn.name, e);
2159     },
2160     setTitle: function(str) {
2161         this.el.select('.modal-title',true).first().dom.innerHTML = str;
2162         
2163     }
2164 });
2165
2166
2167 Roo.apply(Roo.bootstrap.Modal,  {
2168     /**
2169          * Button config that displays a single OK button
2170          * @type Object
2171          */
2172         OK :  [{
2173             name : 'ok',
2174             weight : 'primary',
2175             html : 'OK'
2176         }], 
2177         /**
2178          * Button config that displays Yes and No buttons
2179          * @type Object
2180          */
2181         YESNO : [
2182             {
2183                 name  : 'no',
2184                 html : 'No'
2185             },
2186             {
2187                 name  :'yes',
2188                 weight : 'primary',
2189                 html : 'Yes'
2190             }
2191         ],
2192         
2193         /**
2194          * Button config that displays OK and Cancel buttons
2195          * @type Object
2196          */
2197         OKCANCEL : [
2198             {
2199                name : 'cancel',
2200                 html : 'Cancel'
2201             },
2202             {
2203                 name : 'ok',
2204                 weight : 'primary',
2205                 html : 'OK'
2206             }
2207         ],
2208         /**
2209          * Button config that displays Yes, No and Cancel buttons
2210          * @type Object
2211          */
2212         YESNOCANCEL : [
2213             {
2214                 name : 'yes',
2215                 weight : 'primary',
2216                 html : 'Yes'
2217             },
2218             {
2219                 name : 'no',
2220                 html : 'No'
2221             },
2222             {
2223                 name : 'cancel',
2224                 html : 'Cancel'
2225             }
2226         ]
2227 });
2228  /*
2229  * - LGPL
2230  *
2231  * messagebox - can be used as a replace
2232  * 
2233  */
2234 /**
2235  * @class Roo.MessageBox
2236  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2237  * Example usage:
2238  *<pre><code>
2239 // Basic alert:
2240 Roo.Msg.alert('Status', 'Changes saved successfully.');
2241
2242 // Prompt for user data:
2243 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2244     if (btn == 'ok'){
2245         // process text value...
2246     }
2247 });
2248
2249 // Show a dialog using config options:
2250 Roo.Msg.show({
2251    title:'Save Changes?',
2252    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2253    buttons: Roo.Msg.YESNOCANCEL,
2254    fn: processResult,
2255    animEl: 'elId'
2256 });
2257 </code></pre>
2258  * @singleton
2259  */
2260 Roo.bootstrap.MessageBox = function(){
2261     var dlg, opt, mask, waitTimer;
2262     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2263     var buttons, activeTextEl, bwidth;
2264
2265     
2266     // private
2267     var handleButton = function(button){
2268         dlg.hide();
2269         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2270     };
2271
2272     // private
2273     var handleHide = function(){
2274         if(opt && opt.cls){
2275             dlg.el.removeClass(opt.cls);
2276         }
2277         //if(waitTimer){
2278         //    Roo.TaskMgr.stop(waitTimer);
2279         //    waitTimer = null;
2280         //}
2281     };
2282
2283     // private
2284     var updateButtons = function(b){
2285         var width = 0;
2286         if(!b){
2287             buttons["ok"].hide();
2288             buttons["cancel"].hide();
2289             buttons["yes"].hide();
2290             buttons["no"].hide();
2291             //dlg.footer.dom.style.display = 'none';
2292             return width;
2293         }
2294         dlg.footer.dom.style.display = '';
2295         for(var k in buttons){
2296             if(typeof buttons[k] != "function"){
2297                 if(b[k]){
2298                     buttons[k].show();
2299                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2300                     width += buttons[k].el.getWidth()+15;
2301                 }else{
2302                     buttons[k].hide();
2303                 }
2304             }
2305         }
2306         return width;
2307     };
2308
2309     // private
2310     var handleEsc = function(d, k, e){
2311         if(opt && opt.closable !== false){
2312             dlg.hide();
2313         }
2314         if(e){
2315             e.stopEvent();
2316         }
2317     };
2318
2319     return {
2320         /**
2321          * Returns a reference to the underlying {@link Roo.BasicDialog} element
2322          * @return {Roo.BasicDialog} The BasicDialog element
2323          */
2324         getDialog : function(){
2325            if(!dlg){
2326                 dlg = new Roo.bootstrap.Modal( {
2327                     //draggable: true,
2328                     //resizable:false,
2329                     //constraintoviewport:false,
2330                     //fixedcenter:true,
2331                     //collapsible : false,
2332                     //shim:true,
2333                     //modal: true,
2334                   //  width:400,
2335                   //  height:100,
2336                     //buttonAlign:"center",
2337                     closeClick : function(){
2338                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2339                             handleButton("no");
2340                         }else{
2341                             handleButton("cancel");
2342                         }
2343                     }
2344                 });
2345                 dlg.render();
2346                 dlg.on("hide", handleHide);
2347                 mask = dlg.mask;
2348                 //dlg.addKeyListener(27, handleEsc);
2349                 buttons = {};
2350                 this.buttons = buttons;
2351                 var bt = this.buttonText;
2352                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2353                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2354                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2355                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2356                 Roo.log(buttons)
2357                 bodyEl = dlg.body.createChild({
2358
2359                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2360                         '<textarea class="roo-mb-textarea"></textarea>' +
2361                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
2362                 });
2363                 msgEl = bodyEl.dom.firstChild;
2364                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2365                 textboxEl.enableDisplayMode();
2366                 textboxEl.addKeyListener([10,13], function(){
2367                     if(dlg.isVisible() && opt && opt.buttons){
2368                         if(opt.buttons.ok){
2369                             handleButton("ok");
2370                         }else if(opt.buttons.yes){
2371                             handleButton("yes");
2372                         }
2373                     }
2374                 });
2375                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2376                 textareaEl.enableDisplayMode();
2377                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2378                 progressEl.enableDisplayMode();
2379                 var pf = progressEl.dom.firstChild;
2380                 if (pf) {
2381                     pp = Roo.get(pf.firstChild);
2382                     pp.setHeight(pf.offsetHeight);
2383                 }
2384                 
2385             }
2386             return dlg;
2387         },
2388
2389         /**
2390          * Updates the message box body text
2391          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2392          * the XHTML-compliant non-breaking space character '&amp;#160;')
2393          * @return {Roo.MessageBox} This message box
2394          */
2395         updateText : function(text){
2396             if(!dlg.isVisible() && !opt.width){
2397                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2398             }
2399             msgEl.innerHTML = text || '&#160;';
2400       
2401             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2402             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2403             var w = Math.max(
2404                     Math.min(opt.width || cw , this.maxWidth), 
2405                     Math.max(opt.minWidth || this.minWidth, bwidth)
2406             );
2407             if(opt.prompt){
2408                 activeTextEl.setWidth(w);
2409             }
2410             if(dlg.isVisible()){
2411                 dlg.fixedcenter = false;
2412             }
2413             // to big, make it scroll. = But as usual stupid IE does not support
2414             // !important..
2415             
2416             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2417                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2418                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2419             } else {
2420                 bodyEl.dom.style.height = '';
2421                 bodyEl.dom.style.overflowY = '';
2422             }
2423             if (cw > w) {
2424                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2425             } else {
2426                 bodyEl.dom.style.overflowX = '';
2427             }
2428             
2429             dlg.setContentSize(w, bodyEl.getHeight());
2430             if(dlg.isVisible()){
2431                 dlg.fixedcenter = true;
2432             }
2433             return this;
2434         },
2435
2436         /**
2437          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
2438          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2439          * @param {Number} value Any number between 0 and 1 (e.g., .5)
2440          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2441          * @return {Roo.MessageBox} This message box
2442          */
2443         updateProgress : function(value, text){
2444             if(text){
2445                 this.updateText(text);
2446             }
2447             if (pp) { // weird bug on my firefox - for some reason this is not defined
2448                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2449             }
2450             return this;
2451         },        
2452
2453         /**
2454          * Returns true if the message box is currently displayed
2455          * @return {Boolean} True if the message box is visible, else false
2456          */
2457         isVisible : function(){
2458             return dlg && dlg.isVisible();  
2459         },
2460
2461         /**
2462          * Hides the message box if it is displayed
2463          */
2464         hide : function(){
2465             if(this.isVisible()){
2466                 dlg.hide();
2467             }  
2468         },
2469
2470         /**
2471          * Displays a new message box, or reinitializes an existing message box, based on the config options
2472          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2473          * The following config object properties are supported:
2474          * <pre>
2475 Property    Type             Description
2476 ----------  ---------------  ------------------------------------------------------------------------------------
2477 animEl            String/Element   An id or Element from which the message box should animate as it opens and
2478                                    closes (defaults to undefined)
2479 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
2480                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
2481 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
2482                                    progress and wait dialogs will ignore this property and always hide the
2483                                    close button as they can only be closed programmatically.
2484 cls               String           A custom CSS class to apply to the message box element
2485 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
2486                                    displayed (defaults to 75)
2487 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
2488                                    function will be btn (the name of the button that was clicked, if applicable,
2489                                    e.g. "ok"), and text (the value of the active text field, if applicable).
2490                                    Progress and wait dialogs will ignore this option since they do not respond to
2491                                    user actions and can only be closed programmatically, so any required function
2492                                    should be called by the same code after it closes the dialog.
2493 icon              String           A CSS class that provides a background image to be used as an icon for
2494                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
2495 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
2496 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
2497 modal             Boolean          False to allow user interaction with the page while the message box is
2498                                    displayed (defaults to true)
2499 msg               String           A string that will replace the existing message box body text (defaults
2500                                    to the XHTML-compliant non-breaking space character '&#160;')
2501 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
2502 progress          Boolean          True to display a progress bar (defaults to false)
2503 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
2504 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
2505 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
2506 title             String           The title text
2507 value             String           The string value to set into the active textbox element if displayed
2508 wait              Boolean          True to display a progress bar (defaults to false)
2509 width             Number           The width of the dialog in pixels
2510 </pre>
2511          *
2512          * Example usage:
2513          * <pre><code>
2514 Roo.Msg.show({
2515    title: 'Address',
2516    msg: 'Please enter your address:',
2517    width: 300,
2518    buttons: Roo.MessageBox.OKCANCEL,
2519    multiline: true,
2520    fn: saveAddress,
2521    animEl: 'addAddressBtn'
2522 });
2523 </code></pre>
2524          * @param {Object} config Configuration options
2525          * @return {Roo.MessageBox} This message box
2526          */
2527         show : function(options)
2528         {
2529             
2530             // this causes nightmares if you show one dialog after another
2531             // especially on callbacks..
2532              
2533             if(this.isVisible()){
2534                 
2535                 this.hide();
2536                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
2537                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
2538                 Roo.log("New Dialog Message:" +  options.msg )
2539                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
2540                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
2541                 
2542             }
2543             var d = this.getDialog();
2544             opt = options;
2545             d.setTitle(opt.title || "&#160;");
2546             d.close.setDisplayed(opt.closable !== false);
2547             activeTextEl = textboxEl;
2548             opt.prompt = opt.prompt || (opt.multiline ? true : false);
2549             if(opt.prompt){
2550                 if(opt.multiline){
2551                     textboxEl.hide();
2552                     textareaEl.show();
2553                     textareaEl.setHeight(typeof opt.multiline == "number" ?
2554                         opt.multiline : this.defaultTextHeight);
2555                     activeTextEl = textareaEl;
2556                 }else{
2557                     textboxEl.show();
2558                     textareaEl.hide();
2559                 }
2560             }else{
2561                 textboxEl.hide();
2562                 textareaEl.hide();
2563             }
2564             progressEl.setDisplayed(opt.progress === true);
2565             this.updateProgress(0);
2566             activeTextEl.dom.value = opt.value || "";
2567             if(opt.prompt){
2568                 dlg.setDefaultButton(activeTextEl);
2569             }else{
2570                 var bs = opt.buttons;
2571                 var db = null;
2572                 if(bs && bs.ok){
2573                     db = buttons["ok"];
2574                 }else if(bs && bs.yes){
2575                     db = buttons["yes"];
2576                 }
2577                 dlg.setDefaultButton(db);
2578             }
2579             bwidth = updateButtons(opt.buttons);
2580             this.updateText(opt.msg);
2581             if(opt.cls){
2582                 d.el.addClass(opt.cls);
2583             }
2584             d.proxyDrag = opt.proxyDrag === true;
2585             d.modal = opt.modal !== false;
2586             d.mask = opt.modal !== false ? mask : false;
2587             if(!d.isVisible()){
2588                 // force it to the end of the z-index stack so it gets a cursor in FF
2589                 document.body.appendChild(dlg.el.dom);
2590                 d.animateTarget = null;
2591                 d.show(options.animEl);
2592             }
2593             return this;
2594         },
2595
2596         /**
2597          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
2598          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
2599          * and closing the message box when the process is complete.
2600          * @param {String} title The title bar text
2601          * @param {String} msg The message box body text
2602          * @return {Roo.MessageBox} This message box
2603          */
2604         progress : function(title, msg){
2605             this.show({
2606                 title : title,
2607                 msg : msg,
2608                 buttons: false,
2609                 progress:true,
2610                 closable:false,
2611                 minWidth: this.minProgressWidth,
2612                 modal : true
2613             });
2614             return this;
2615         },
2616
2617         /**
2618          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
2619          * If a callback function is passed it will be called after the user clicks the button, and the
2620          * id of the button that was clicked will be passed as the only parameter to the callback
2621          * (could also be the top-right close button).
2622          * @param {String} title The title bar text
2623          * @param {String} msg The message box body text
2624          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2625          * @param {Object} scope (optional) The scope of the callback function
2626          * @return {Roo.MessageBox} This message box
2627          */
2628         alert : function(title, msg, fn, scope){
2629             this.show({
2630                 title : title,
2631                 msg : msg,
2632                 buttons: this.OK,
2633                 fn: fn,
2634                 scope : scope,
2635                 modal : true
2636             });
2637             return this;
2638         },
2639
2640         /**
2641          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
2642          * interaction while waiting for a long-running process to complete that does not have defined intervals.
2643          * You are responsible for closing the message box when the process is complete.
2644          * @param {String} msg The message box body text
2645          * @param {String} title (optional) The title bar text
2646          * @return {Roo.MessageBox} This message box
2647          */
2648         wait : function(msg, title){
2649             this.show({
2650                 title : title,
2651                 msg : msg,
2652                 buttons: false,
2653                 closable:false,
2654                 progress:true,
2655                 modal:true,
2656                 width:300,
2657                 wait:true
2658             });
2659             waitTimer = Roo.TaskMgr.start({
2660                 run: function(i){
2661                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
2662                 },
2663                 interval: 1000
2664             });
2665             return this;
2666         },
2667
2668         /**
2669          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
2670          * If a callback function is passed it will be called after the user clicks either button, and the id of the
2671          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
2672          * @param {String} title The title bar text
2673          * @param {String} msg The message box body text
2674          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2675          * @param {Object} scope (optional) The scope of the callback function
2676          * @return {Roo.MessageBox} This message box
2677          */
2678         confirm : function(title, msg, fn, scope){
2679             this.show({
2680                 title : title,
2681                 msg : msg,
2682                 buttons: this.YESNO,
2683                 fn: fn,
2684                 scope : scope,
2685                 modal : true
2686             });
2687             return this;
2688         },
2689
2690         /**
2691          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
2692          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
2693          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
2694          * (could also be the top-right close button) and the text that was entered will be passed as the two
2695          * parameters to the callback.
2696          * @param {String} title The title bar text
2697          * @param {String} msg The message box body text
2698          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2699          * @param {Object} scope (optional) The scope of the callback function
2700          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
2701          * property, or the height in pixels to create the textbox (defaults to false / single-line)
2702          * @return {Roo.MessageBox} This message box
2703          */
2704         prompt : function(title, msg, fn, scope, multiline){
2705             this.show({
2706                 title : title,
2707                 msg : msg,
2708                 buttons: this.OKCANCEL,
2709                 fn: fn,
2710                 minWidth:250,
2711                 scope : scope,
2712                 prompt:true,
2713                 multiline: multiline,
2714                 modal : true
2715             });
2716             return this;
2717         },
2718
2719         /**
2720          * Button config that displays a single OK button
2721          * @type Object
2722          */
2723         OK : {ok:true},
2724         /**
2725          * Button config that displays Yes and No buttons
2726          * @type Object
2727          */
2728         YESNO : {yes:true, no:true},
2729         /**
2730          * Button config that displays OK and Cancel buttons
2731          * @type Object
2732          */
2733         OKCANCEL : {ok:true, cancel:true},
2734         /**
2735          * Button config that displays Yes, No and Cancel buttons
2736          * @type Object
2737          */
2738         YESNOCANCEL : {yes:true, no:true, cancel:true},
2739
2740         /**
2741          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
2742          * @type Number
2743          */
2744         defaultTextHeight : 75,
2745         /**
2746          * The maximum width in pixels of the message box (defaults to 600)
2747          * @type Number
2748          */
2749         maxWidth : 600,
2750         /**
2751          * The minimum width in pixels of the message box (defaults to 100)
2752          * @type Number
2753          */
2754         minWidth : 100,
2755         /**
2756          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
2757          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
2758          * @type Number
2759          */
2760         minProgressWidth : 250,
2761         /**
2762          * An object containing the default button text strings that can be overriden for localized language support.
2763          * Supported properties are: ok, cancel, yes and no.
2764          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
2765          * @type Object
2766          */
2767         buttonText : {
2768             ok : "OK",
2769             cancel : "Cancel",
2770             yes : "Yes",
2771             no : "No"
2772         }
2773     };
2774 }();
2775
2776 /**
2777  * Shorthand for {@link Roo.MessageBox}
2778  */
2779 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox 
2780 Roo.Msg = Roo.Msg || Roo.MessageBox;
2781 /*
2782  * - LGPL
2783  *
2784  * navbar
2785  * 
2786  */
2787
2788 /**
2789  * @class Roo.bootstrap.Navbar
2790  * @extends Roo.bootstrap.Component
2791  * Bootstrap Navbar class
2792
2793  * @constructor
2794  * Create a new Navbar
2795  * @param {Object} config The config object
2796  */
2797
2798
2799 Roo.bootstrap.Navbar = function(config){
2800     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
2801     
2802 };
2803
2804 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
2805     
2806     
2807    
2808     // private
2809     navItems : false,
2810     loadMask : false,
2811     
2812     
2813     getAutoCreate : function(){
2814         
2815         
2816         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
2817         
2818     },
2819     
2820     initEvents :function ()
2821     {
2822         //Roo.log(this.el.select('.navbar-toggle',true));
2823         this.el.select('.navbar-toggle',true).on('click', function() {
2824            // Roo.log('click');
2825             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
2826         }, this);
2827         
2828         var mark = {
2829             tag: "div",
2830             cls:"x-dlg-mask"
2831         }
2832         
2833         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
2834         
2835         var size = this.el.getSize();
2836         this.maskEl.setSize(size.width, size.height);
2837         this.maskEl.enableDisplayMode("block");
2838         this.maskEl.hide();
2839         
2840         if(this.loadMask){
2841             this.maskEl.show();
2842         }
2843     },
2844     
2845     
2846     getChildContainer : function()
2847     {
2848         if (this.el.select('.collapse').getCount()) {
2849             return this.el.select('.collapse',true).first();
2850         }
2851         
2852         return this.el;
2853     },
2854     
2855     mask : function()
2856     {
2857         this.maskEl.show();
2858     },
2859     
2860     unmask : function()
2861     {
2862         this.maskEl.hide();
2863     } 
2864     
2865     
2866     
2867     
2868 });
2869
2870
2871
2872  
2873
2874  /*
2875  * - LGPL
2876  *
2877  * navbar
2878  * 
2879  */
2880
2881 /**
2882  * @class Roo.bootstrap.NavSimplebar
2883  * @extends Roo.bootstrap.Navbar
2884  * Bootstrap Sidebar class
2885  *
2886  * @cfg {Boolean} inverse is inverted color
2887  * 
2888  * @cfg {String} type (nav | pills | tabs)
2889  * @cfg {Boolean} arrangement stacked | justified
2890  * @cfg {String} align (left | right) alignment
2891  * 
2892  * @cfg {Boolean} main (true|false) main nav bar? default false
2893  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
2894  * 
2895  * @cfg {String} tag (header|footer|nav|div) default is nav 
2896
2897  * 
2898  * 
2899  * 
2900  * @constructor
2901  * Create a new Sidebar
2902  * @param {Object} config The config object
2903  */
2904
2905
2906 Roo.bootstrap.NavSimplebar = function(config){
2907     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
2908 };
2909
2910 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
2911     
2912     inverse: false,
2913     
2914     type: false,
2915     arrangement: '',
2916     align : false,
2917     
2918     
2919     
2920     main : false,
2921     
2922     
2923     tag : false,
2924     
2925     
2926     getAutoCreate : function(){
2927         
2928         
2929         var cfg = {
2930             tag : this.tag || 'div',
2931             cls : 'navbar'
2932         };
2933           
2934         
2935         cfg.cn = [
2936             {
2937                 cls: 'nav',
2938                 tag : 'ul'
2939             }
2940         ];
2941         
2942          
2943         this.type = this.type || 'nav';
2944         if (['tabs','pills'].indexOf(this.type)!==-1) {
2945             cfg.cn[0].cls += ' nav-' + this.type
2946         
2947         
2948         } else {
2949             if (this.type!=='nav') {
2950                 Roo.log('nav type must be nav/tabs/pills')
2951             }
2952             cfg.cn[0].cls += ' navbar-nav'
2953         }
2954         
2955         
2956         
2957         
2958         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
2959             cfg.cn[0].cls += ' nav-' + this.arrangement;
2960         }
2961         
2962         
2963         if (this.align === 'right') {
2964             cfg.cn[0].cls += ' navbar-right';
2965         }
2966         
2967         if (this.inverse) {
2968             cfg.cls += ' navbar-inverse';
2969             
2970         }
2971         
2972         
2973         return cfg;
2974     
2975         
2976     }
2977     
2978     
2979     
2980 });
2981
2982
2983
2984  
2985
2986  
2987        /*
2988  * - LGPL
2989  *
2990  * navbar
2991  * 
2992  */
2993
2994 /**
2995  * @class Roo.bootstrap.NavHeaderbar
2996  * @extends Roo.bootstrap.NavSimplebar
2997  * Bootstrap Sidebar class
2998  *
2999  * @cfg {String} brand what is brand
3000  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3001  * @cfg {String} brand_href href of the brand
3002  * @cfg {Boolean} srButton generate the sr-only button (true | false) default true
3003  * 
3004  * @constructor
3005  * Create a new Sidebar
3006  * @param {Object} config The config object
3007  */
3008
3009
3010 Roo.bootstrap.NavHeaderbar = function(config){
3011     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3012 };
3013
3014 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3015     
3016     position: '',
3017     brand: '',
3018     brand_href: false,
3019     srButton : true,
3020     
3021     
3022     getAutoCreate : function(){
3023         
3024         var   cfg = {
3025             tag: this.nav || 'nav',
3026             cls: 'navbar',
3027             role: 'navigation',
3028             cn: []
3029         };
3030         
3031         if(this.srButton){
3032             cfg.cn.push({
3033                 tag: 'div',
3034                 cls: 'navbar-header',
3035                 cn: [
3036                     {
3037                         tag: 'button',
3038                         type: 'button',
3039                         cls: 'navbar-toggle',
3040                         'data-toggle': 'collapse',
3041                         cn: [
3042                             {
3043                                 tag: 'span',
3044                                 cls: 'sr-only',
3045                                 html: 'Toggle navigation'
3046                             },
3047                             {
3048                                 tag: 'span',
3049                                 cls: 'icon-bar'
3050                             },
3051                             {
3052                                 tag: 'span',
3053                                 cls: 'icon-bar'
3054                             },
3055                             {
3056                                 tag: 'span',
3057                                 cls: 'icon-bar'
3058                             }
3059                         ]
3060                     }
3061                 ]
3062             });
3063         }
3064         
3065         cfg.cn.push({
3066             tag: 'div',
3067             cls: 'collapse navbar-collapse',
3068             cn : []
3069         });
3070         
3071         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3072         
3073         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3074             cfg.cls += ' navbar-' + this.position;
3075             
3076             // tag can override this..
3077             
3078             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3079         }
3080         
3081         if (this.brand !== '') {
3082             cfg.cn[0].cn.push({
3083                 tag: 'a',
3084                 href: this.brand_href ? this.brand_href : '#',
3085                 cls: 'navbar-brand',
3086                 cn: [
3087                 this.brand
3088                 ]
3089             });
3090         }
3091         
3092         if(this.main){
3093             cfg.cls += ' main-nav';
3094         }
3095         
3096         
3097         return cfg;
3098
3099         
3100     }
3101     
3102     
3103     
3104 });
3105
3106
3107
3108  
3109
3110  /*
3111  * - LGPL
3112  *
3113  * navbar
3114  * 
3115  */
3116
3117 /**
3118  * @class Roo.bootstrap.NavSidebar
3119  * @extends Roo.bootstrap.Navbar
3120  * Bootstrap Sidebar class
3121  * 
3122  * @constructor
3123  * Create a new Sidebar
3124  * @param {Object} config The config object
3125  */
3126
3127
3128 Roo.bootstrap.NavSidebar = function(config){
3129     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3130 };
3131
3132 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3133     
3134     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3135     
3136     getAutoCreate : function(){
3137         
3138         
3139         return  {
3140             tag: 'div',
3141             cls: 'sidebar sidebar-nav'
3142         };
3143     
3144         
3145     }
3146     
3147     
3148     
3149 });
3150
3151
3152
3153  
3154
3155  /*
3156  * - LGPL
3157  *
3158  * nav group
3159  * 
3160  */
3161
3162 /**
3163  * @class Roo.bootstrap.NavGroup
3164  * @extends Roo.bootstrap.Component
3165  * Bootstrap NavGroup class
3166  * @cfg {String} align left | right
3167  * @cfg {Boolean} inverse false | true
3168  * @cfg {String} type (nav|pills|tab) default nav
3169  * @cfg {String} navId - reference Id for navbar.
3170
3171  * 
3172  * @constructor
3173  * Create a new nav group
3174  * @param {Object} config The config object
3175  */
3176
3177 Roo.bootstrap.NavGroup = function(config){
3178     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3179     this.navItems = [];
3180    
3181     Roo.bootstrap.NavGroup.register(this);
3182      this.addEvents({
3183         /**
3184              * @event changed
3185              * Fires when the active item changes
3186              * @param {Roo.bootstrap.NavGroup} this
3187              * @param {Roo.bootstrap.Navbar.Item} item The item selected
3188              * @param {Roo.bootstrap.Navbar.Item} item The previously selected item 
3189          */
3190         'changed': true
3191      });
3192     
3193 };
3194
3195 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3196     
3197     align: '',
3198     inverse: false,
3199     form: false,
3200     type: 'nav',
3201     navId : '',
3202     // private
3203     
3204     navItems : false, 
3205     
3206     getAutoCreate : function()
3207     {
3208         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3209         
3210         cfg = {
3211             tag : 'ul',
3212             cls: 'nav' 
3213         }
3214         
3215         if (['tabs','pills'].indexOf(this.type)!==-1) {
3216             cfg.cls += ' nav-' + this.type
3217         } else {
3218             if (this.type!=='nav') {
3219                 Roo.log('nav type must be nav/tabs/pills')
3220             }
3221             cfg.cls += ' navbar-nav'
3222         }
3223         
3224         if (this.parent().sidebar) {
3225             cfg = {
3226                 tag: 'ul',
3227                 cls: 'dashboard-menu sidebar-menu'
3228             }
3229             
3230             return cfg;
3231         }
3232         
3233         if (this.form === true) {
3234             cfg = {
3235                 tag: 'form',
3236                 cls: 'navbar-form'
3237             }
3238             
3239             if (this.align === 'right') {
3240                 cfg.cls += ' navbar-right';
3241             } else {
3242                 cfg.cls += ' navbar-left';
3243             }
3244         }
3245         
3246         if (this.align === 'right') {
3247             cfg.cls += ' navbar-right';
3248         }
3249         
3250         if (this.inverse) {
3251             cfg.cls += ' navbar-inverse';
3252             
3253         }
3254         
3255         
3256         return cfg;
3257     },
3258     /**
3259     * sets the active Navigation item
3260     * @param {Roo.bootstrap.NavItem} the new current navitem
3261     */
3262     setActiveItem : function(item)
3263     {
3264         var prev = false;
3265         Roo.each(this.navItems, function(v){
3266             if (v == item) {
3267                 return ;
3268             }
3269             if (v.isActive()) {
3270                 v.setActive(false, true);
3271                 prev = v;
3272                 
3273             }
3274             
3275         });
3276
3277         item.setActive(true, true);
3278         this.fireEvent('changed', this, item, prev);
3279         
3280         
3281     },
3282     /**
3283     * gets the active Navigation item
3284     * @return {Roo.bootstrap.NavItem} the current navitem
3285     */
3286     getActive : function()
3287     {
3288         
3289         var prev = false;
3290         Roo.each(this.navItems, function(v){
3291             
3292             if (v.isActive()) {
3293                 prev = v;
3294                 
3295             }
3296             
3297         });
3298         return prev;
3299     },
3300     
3301     indexOfNav : function()
3302     {
3303         
3304         var prev = false;
3305         Roo.each(this.navItems, function(v,i){
3306             
3307             if (v.isActive()) {
3308                 prev = i;
3309                 
3310             }
3311             
3312         });
3313         return prev;
3314     },
3315     /**
3316     * adds a Navigation item
3317     * @param {Roo.bootstrap.NavItem} the navitem to add
3318     */
3319     addItem : function(cfg)
3320     {
3321         var cn = new Roo.bootstrap.NavItem(cfg);
3322         this.register(cn);
3323         cn.parentId = this.id;
3324         cn.onRender(this.el, null);
3325         return cn;
3326     },
3327     /**
3328     * register a Navigation item
3329     * @param {Roo.bootstrap.NavItem} the navitem to add
3330     */
3331     register : function(item)
3332     {
3333         this.navItems.push( item);
3334         item.navId = this.navId;
3335     
3336     },
3337   
3338     
3339     getNavItem: function(tabId)
3340     {
3341         var ret = false;
3342         Roo.each(this.navItems, function(e) {
3343             if (e.tabId == tabId) {
3344                ret =  e;
3345                return false;
3346             }
3347             return true;
3348             
3349         });
3350         return ret;
3351     },
3352     
3353     setActiveNext : function()
3354     {
3355         var i = this.indexOfNav(this.getActive());
3356         if (i > this.navItems.length) {
3357             return;
3358         }
3359         this.setActiveItem(this.navItems[i+1]);
3360     },
3361     setActivePrev : function()
3362     {
3363         var i = this.indexOfNav(this.getActive());
3364         if (i  < 1) {
3365             return;
3366         }
3367         this.setActiveItem(this.navItems[i-1]);
3368     },
3369     clearWasActive : function(except) {
3370         Roo.each(this.navItems, function(e) {
3371             if (e.tabId != except.tabId && e.was_active) {
3372                e.was_active = false;
3373                return false;
3374             }
3375             return true;
3376             
3377         });
3378     },
3379     getWasActive : function ()
3380     {
3381         var r = false;
3382         Roo.each(this.navItems, function(e) {
3383             if (e.was_active) {
3384                r = e;
3385                return false;
3386             }
3387             return true;
3388             
3389         });
3390         return r;
3391     }
3392     
3393     
3394 });
3395
3396  
3397 Roo.apply(Roo.bootstrap.NavGroup, {
3398     
3399     groups: {},
3400      /**
3401     * register a Navigation Group
3402     * @param {Roo.bootstrap.NavGroup} the navgroup to add
3403     */
3404     register : function(navgrp)
3405     {
3406         this.groups[navgrp.navId] = navgrp;
3407         
3408     },
3409     /**
3410     * fetch a Navigation Group based on the navigation ID
3411     * @param {string} the navgroup to add
3412     * @returns {Roo.bootstrap.NavGroup} the navgroup 
3413     */
3414     get: function(navId) {
3415         if (typeof(this.groups[navId]) == 'undefined') {
3416             return false;
3417             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
3418         }
3419         return this.groups[navId] ;
3420     }
3421     
3422     
3423     
3424 });
3425
3426  /*
3427  * - LGPL
3428  *
3429  * row
3430  * 
3431  */
3432
3433 /**
3434  * @class Roo.bootstrap.NavItem
3435  * @extends Roo.bootstrap.Component
3436  * Bootstrap Navbar.NavItem class
3437  * @cfg {String} href  link to
3438  * @cfg {String} html content of button
3439  * @cfg {String} badge text inside badge
3440  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
3441  * @cfg {String} glyphicon name of glyphicon
3442  * @cfg {String} icon name of font awesome icon
3443  * @cfg {Boolean} active Is item active
3444  * @cfg {Boolean} disabled Is item disabled
3445  
3446  * @cfg {Boolean} preventDefault (true | false) default false
3447  * @cfg {String} tabId the tab that this item activates.
3448  * @cfg {String} tagtype (a|span) render as a href or span?
3449   
3450  * @constructor
3451  * Create a new Navbar Item
3452  * @param {Object} config The config object
3453  */
3454 Roo.bootstrap.NavItem = function(config){
3455     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
3456     this.addEvents({
3457         // raw events
3458         /**
3459          * @event click
3460          * The raw click event for the entire grid.
3461          * @param {Roo.EventObject} e
3462          */
3463         "click" : true,
3464          /**
3465             * @event changed
3466             * Fires when the active item active state changes
3467             * @param {Roo.bootstrap.NavItem} this
3468             * @param {boolean} state the new state
3469              
3470          */
3471         'changed': true
3472     });
3473    
3474 };
3475
3476 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
3477     
3478     href: false,
3479     html: '',
3480     badge: '',
3481     icon: false,
3482     glyphicon: false,
3483     active: false,
3484     preventDefault : false,
3485     tabId : false,
3486     tagtype : 'a',
3487     disabled : false,
3488     
3489     was_active : false,
3490     
3491     getAutoCreate : function(){
3492          
3493         var cfg = {
3494             tag: 'li',
3495             cls: 'nav-item'
3496             
3497         }
3498         if (this.active) {
3499             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
3500         }
3501         if (this.disabled) {
3502             cfg.cls += ' disabled';
3503         }
3504         
3505         if (this.href || this.html || this.glyphicon || this.icon) {
3506             cfg.cn = [
3507                 {
3508                     tag: this.tagtype,
3509                     href : this.href || "#",
3510                     html: this.html || ''
3511                 }
3512             ];
3513             
3514             if (this.icon) {
3515                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
3516             }
3517
3518             if(this.glyphicon) {
3519                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
3520             }
3521             
3522             if (this.menu) {
3523                 
3524                 cfg.cn[0].html += " <span class='caret'></span>";
3525              
3526             }
3527             
3528             if (this.badge !== '') {
3529                  
3530                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
3531             }
3532         }
3533         
3534         
3535         
3536         return cfg;
3537     },
3538     initEvents: function() {
3539        // Roo.log('init events?');
3540        // Roo.log(this.el.dom);
3541         if (typeof (this.menu) != 'undefined') {
3542             this.menu.parentType = this.xtype;
3543             this.menu.triggerEl = this.el;
3544             this.addxtype(Roo.apply({}, this.menu));
3545         }
3546
3547        
3548         this.el.select('a',true).on('click', this.onClick, this);
3549         // at this point parent should be available..
3550         this.parent().register(this);
3551     },
3552     
3553     onClick : function(e)
3554     {
3555          
3556         if(this.preventDefault){
3557             e.preventDefault();
3558         }
3559         if (this.disabled) {
3560             return;
3561         }
3562         
3563         var tg = Roo.bootstrap.TabGroup.get(this.navId);
3564         if (tg && tg.transition) {
3565             Roo.log("waiting for the transitionend");
3566             return;
3567         }
3568         
3569         Roo.log("fire event clicked");
3570         if(this.fireEvent('click', this, e) === false){
3571             return;
3572         };
3573         
3574         if (['tabs','pills'].indexOf(this.parent().type)!==-1) {
3575             if (typeof(this.parent().setActiveItem) !== 'undefined') {
3576                 this.parent().setActiveItem(this);
3577             }
3578         } 
3579     },
3580     
3581     isActive: function () {
3582         return this.active
3583     },
3584     setActive : function(state, fire, is_was_active)
3585     {
3586         if (this.active && !state & this.navId) {
3587             this.was_active = true;
3588             var nv = Roo.bootstrap.NavGroup.get(this.navId);
3589             if (nv) {
3590                 nv.clearWasActive(this);
3591             }
3592             
3593         }
3594         this.active = state;
3595         
3596         if (!state ) {
3597             this.el.removeClass('active');
3598         } else if (!this.el.hasClass('active')) {
3599             this.el.addClass('active');
3600         }
3601         if (fire) {
3602             this.fireEvent('changed', this, state);
3603         }
3604         
3605         // show a panel if it's registered and related..
3606         
3607         if (!this.navId || !this.tabId || !state || is_was_active) {
3608             return;
3609         }
3610         
3611         var tg = Roo.bootstrap.TabGroup.get(this.navId);
3612         if (!tg) {
3613             return;
3614         }
3615         var pan = tg.getPanelByName(this.tabId);
3616         if (!pan) {
3617             return;
3618         }
3619         // if we can not flip to new panel - go back to old nav highlight..
3620         if (false == tg.showPanel(pan)) {
3621             var nv = Roo.bootstrap.NavGroup.get(this.navId);
3622             if (nv) {
3623                 var onav = nv.getWasActive();
3624                 if (onav) {
3625                     onav.setActive(true, false, true);
3626                 }
3627             }
3628             
3629         }
3630         
3631         
3632         
3633     },
3634      // this should not be here...
3635     setDisabled : function(state)
3636     {
3637         this.disabled = state;
3638         if (!state ) {
3639             this.el.removeClass('disabled');
3640         } else if (!this.el.hasClass('disabled')) {
3641             this.el.addClass('disabled');
3642         }
3643         
3644     }
3645 });
3646  
3647
3648  /*
3649  * - LGPL
3650  *
3651  * sidebar item
3652  *
3653  *  li
3654  *    <span> icon </span>
3655  *    <span> text </span>
3656  *    <span>badge </span>
3657  */
3658
3659 /**
3660  * @class Roo.bootstrap.NavSidebarItem
3661  * @extends Roo.bootstrap.NavItem
3662  * Bootstrap Navbar.NavSidebarItem class
3663  * @constructor
3664  * Create a new Navbar Button
3665  * @param {Object} config The config object
3666  */
3667 Roo.bootstrap.NavSidebarItem = function(config){
3668     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
3669     this.addEvents({
3670         // raw events
3671         /**
3672          * @event click
3673          * The raw click event for the entire grid.
3674          * @param {Roo.EventObject} e
3675          */
3676         "click" : true,
3677          /**
3678             * @event changed
3679             * Fires when the active item active state changes
3680             * @param {Roo.bootstrap.NavSidebarItem} this
3681             * @param {boolean} state the new state
3682              
3683          */
3684         'changed': true
3685     });
3686    
3687 };
3688
3689 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
3690     
3691     
3692     getAutoCreate : function(){
3693         
3694         
3695         var a = {
3696                 tag: 'a',
3697                 href : this.href || '#',
3698                 cls: '',
3699                 html : '',
3700                 cn : []
3701         };
3702         var cfg = {
3703             tag: 'li',
3704             cls: '',
3705             cn: [ a ]
3706         }
3707         var span = {
3708             tag: 'span',
3709             html : this.html || ''
3710         }
3711         
3712         
3713         if (this.active) {
3714             cfg.cls += ' active';
3715         }
3716         
3717         // left icon..
3718         if (this.glyphicon || this.icon) {
3719             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
3720             a.cn.push({ tag : 'i', cls : c }) ;
3721         }
3722         // html..
3723         a.cn.push(span);
3724         // then badge..
3725         if (this.badge !== '') {
3726             a.cn.push({ tag: 'span',  cls : 'badge pull-right ' + (this.badgecls || ''), html: this.badge }); 
3727         }
3728         // fi
3729         if (this.menu) {
3730             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
3731             a.cls += 'dropdown-toggle treeview' ;
3732             
3733         }
3734         
3735         
3736         
3737         return cfg;
3738          
3739            
3740     }
3741    
3742      
3743  
3744 });
3745  
3746
3747  /*
3748  * - LGPL
3749  *
3750  * row
3751  * 
3752  */
3753
3754 /**
3755  * @class Roo.bootstrap.Row
3756  * @extends Roo.bootstrap.Component
3757  * Bootstrap Row class (contains columns...)
3758  * 
3759  * @constructor
3760  * Create a new Row
3761  * @param {Object} config The config object
3762  */
3763
3764 Roo.bootstrap.Row = function(config){
3765     Roo.bootstrap.Row.superclass.constructor.call(this, config);
3766 };
3767
3768 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
3769     
3770     getAutoCreate : function(){
3771        return {
3772             cls: 'row clearfix'
3773        };
3774     }
3775     
3776     
3777 });
3778
3779  
3780
3781  /*
3782  * - LGPL
3783  *
3784  * element
3785  * 
3786  */
3787
3788 /**
3789  * @class Roo.bootstrap.Element
3790  * @extends Roo.bootstrap.Component
3791  * Bootstrap Element class
3792  * @cfg {String} html contents of the element
3793  * @cfg {String} tag tag of the element
3794  * @cfg {String} cls class of the element
3795  * 
3796  * @constructor
3797  * Create a new Element
3798  * @param {Object} config The config object
3799  */
3800
3801 Roo.bootstrap.Element = function(config){
3802     Roo.bootstrap.Element.superclass.constructor.call(this, config);
3803 };
3804
3805 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
3806     
3807     tag: 'div',
3808     cls: '',
3809     html: '',
3810      
3811     
3812     getAutoCreate : function(){
3813         
3814         var cfg = {
3815             tag: this.tag,
3816             cls: this.cls,
3817             html: this.html
3818         }
3819         
3820         
3821         
3822         return cfg;
3823     }
3824    
3825 });
3826
3827  
3828
3829  /*
3830  * - LGPL
3831  *
3832  * pagination
3833  * 
3834  */
3835
3836 /**
3837  * @class Roo.bootstrap.Pagination
3838  * @extends Roo.bootstrap.Component
3839  * Bootstrap Pagination class
3840  * @cfg {String} size xs | sm | md | lg
3841  * @cfg {Boolean} inverse false | true
3842  * 
3843  * @constructor
3844  * Create a new Pagination
3845  * @param {Object} config The config object
3846  */
3847
3848 Roo.bootstrap.Pagination = function(config){
3849     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
3850 };
3851
3852 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
3853     
3854     cls: false,
3855     size: false,
3856     inverse: false,
3857     
3858     getAutoCreate : function(){
3859         var cfg = {
3860             tag: 'ul',
3861                 cls: 'pagination'
3862         };
3863         if (this.inverse) {
3864             cfg.cls += ' inverse';
3865         }
3866         if (this.html) {
3867             cfg.html=this.html;
3868         }
3869         if (this.cls) {
3870             cfg.cls += " " + this.cls;
3871         }
3872         return cfg;
3873     }
3874    
3875 });
3876
3877  
3878
3879  /*
3880  * - LGPL
3881  *
3882  * Pagination item
3883  * 
3884  */
3885
3886
3887 /**
3888  * @class Roo.bootstrap.PaginationItem
3889  * @extends Roo.bootstrap.Component
3890  * Bootstrap PaginationItem class
3891  * @cfg {String} html text
3892  * @cfg {String} href the link
3893  * @cfg {Boolean} preventDefault (true | false) default true
3894  * @cfg {Boolean} active (true | false) default false
3895  * 
3896  * 
3897  * @constructor
3898  * Create a new PaginationItem
3899  * @param {Object} config The config object
3900  */
3901
3902
3903 Roo.bootstrap.PaginationItem = function(config){
3904     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
3905     this.addEvents({
3906         // raw events
3907         /**
3908          * @event click
3909          * The raw click event for the entire grid.
3910          * @param {Roo.EventObject} e
3911          */
3912         "click" : true
3913     });
3914 };
3915
3916 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
3917     
3918     href : false,
3919     html : false,
3920     preventDefault: true,
3921     active : false,
3922     cls : false,
3923     
3924     getAutoCreate : function(){
3925         var cfg= {
3926             tag: 'li',
3927             cn: [
3928                 {
3929                     tag : 'a',
3930                     href : this.href ? this.href : '#',
3931                     html : this.html ? this.html : ''
3932                 }
3933             ]
3934         };
3935         
3936         if(this.cls){
3937             cfg.cls = this.cls;
3938         }
3939         
3940         if(this.active){
3941             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
3942         }
3943         
3944         return cfg;
3945     },
3946     
3947     initEvents: function() {
3948         
3949         this.el.on('click', this.onClick, this);
3950         
3951     },
3952     onClick : function(e)
3953     {
3954         Roo.log('PaginationItem on click ');
3955         if(this.preventDefault){
3956             e.preventDefault();
3957         }
3958         
3959         this.fireEvent('click', this, e);
3960     }
3961    
3962 });
3963
3964  
3965
3966  /*
3967  * - LGPL
3968  *
3969  * slider
3970  * 
3971  */
3972
3973
3974 /**
3975  * @class Roo.bootstrap.Slider
3976  * @extends Roo.bootstrap.Component
3977  * Bootstrap Slider class
3978  *    
3979  * @constructor
3980  * Create a new Slider
3981  * @param {Object} config The config object
3982  */
3983
3984 Roo.bootstrap.Slider = function(config){
3985     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
3986 };
3987
3988 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
3989     
3990     getAutoCreate : function(){
3991         
3992         var cfg = {
3993             tag: 'div',
3994             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
3995             cn: [
3996                 {
3997                     tag: 'a',
3998                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
3999                 }
4000             ]
4001         }
4002         
4003         return cfg;
4004     }
4005    
4006 });
4007
4008  /*
4009  * Based on:
4010  * Ext JS Library 1.1.1
4011  * Copyright(c) 2006-2007, Ext JS, LLC.
4012  *
4013  * Originally Released Under LGPL - original licence link has changed is not relivant.
4014  *
4015  * Fork - LGPL
4016  * <script type="text/javascript">
4017  */
4018  
4019
4020 /**
4021  * @class Roo.grid.ColumnModel
4022  * @extends Roo.util.Observable
4023  * This is the default implementation of a ColumnModel used by the Grid. It defines
4024  * the columns in the grid.
4025  * <br>Usage:<br>
4026  <pre><code>
4027  var colModel = new Roo.grid.ColumnModel([
4028         {header: "Ticker", width: 60, sortable: true, locked: true},
4029         {header: "Company Name", width: 150, sortable: true},
4030         {header: "Market Cap.", width: 100, sortable: true},
4031         {header: "$ Sales", width: 100, sortable: true, renderer: money},
4032         {header: "Employees", width: 100, sortable: true, resizable: false}
4033  ]);
4034  </code></pre>
4035  * <p>
4036  
4037  * The config options listed for this class are options which may appear in each
4038  * individual column definition.
4039  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4040  * @constructor
4041  * @param {Object} config An Array of column config objects. See this class's
4042  * config objects for details.
4043 */
4044 Roo.grid.ColumnModel = function(config){
4045         /**
4046      * The config passed into the constructor
4047      */
4048     this.config = config;
4049     this.lookup = {};
4050
4051     // if no id, create one
4052     // if the column does not have a dataIndex mapping,
4053     // map it to the order it is in the config
4054     for(var i = 0, len = config.length; i < len; i++){
4055         var c = config[i];
4056         if(typeof c.dataIndex == "undefined"){
4057             c.dataIndex = i;
4058         }
4059         if(typeof c.renderer == "string"){
4060             c.renderer = Roo.util.Format[c.renderer];
4061         }
4062         if(typeof c.id == "undefined"){
4063             c.id = Roo.id();
4064         }
4065         if(c.editor && c.editor.xtype){
4066             c.editor  = Roo.factory(c.editor, Roo.grid);
4067         }
4068         if(c.editor && c.editor.isFormField){
4069             c.editor = new Roo.grid.GridEditor(c.editor);
4070         }
4071         this.lookup[c.id] = c;
4072     }
4073
4074     /**
4075      * The width of columns which have no width specified (defaults to 100)
4076      * @type Number
4077      */
4078     this.defaultWidth = 100;
4079
4080     /**
4081      * Default sortable of columns which have no sortable specified (defaults to false)
4082      * @type Boolean
4083      */
4084     this.defaultSortable = false;
4085
4086     this.addEvents({
4087         /**
4088              * @event widthchange
4089              * Fires when the width of a column changes.
4090              * @param {ColumnModel} this
4091              * @param {Number} columnIndex The column index
4092              * @param {Number} newWidth The new width
4093              */
4094             "widthchange": true,
4095         /**
4096              * @event headerchange
4097              * Fires when the text of a header changes.
4098              * @param {ColumnModel} this
4099              * @param {Number} columnIndex The column index
4100              * @param {Number} newText The new header text
4101              */
4102             "headerchange": true,
4103         /**
4104              * @event hiddenchange
4105              * Fires when a column is hidden or "unhidden".
4106              * @param {ColumnModel} this
4107              * @param {Number} columnIndex The column index
4108              * @param {Boolean} hidden true if hidden, false otherwise
4109              */
4110             "hiddenchange": true,
4111             /**
4112          * @event columnmoved
4113          * Fires when a column is moved.
4114          * @param {ColumnModel} this
4115          * @param {Number} oldIndex
4116          * @param {Number} newIndex
4117          */
4118         "columnmoved" : true,
4119         /**
4120          * @event columlockchange
4121          * Fires when a column's locked state is changed
4122          * @param {ColumnModel} this
4123          * @param {Number} colIndex
4124          * @param {Boolean} locked true if locked
4125          */
4126         "columnlockchange" : true
4127     });
4128     Roo.grid.ColumnModel.superclass.constructor.call(this);
4129 };
4130 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4131     /**
4132      * @cfg {String} header The header text to display in the Grid view.
4133      */
4134     /**
4135      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4136      * {@link Roo.data.Record} definition from which to draw the column's value. If not
4137      * specified, the column's index is used as an index into the Record's data Array.
4138      */
4139     /**
4140      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4141      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4142      */
4143     /**
4144      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4145      * Defaults to the value of the {@link #defaultSortable} property.
4146      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4147      */
4148     /**
4149      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
4150      */
4151     /**
4152      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
4153      */
4154     /**
4155      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4156      */
4157     /**
4158      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4159      */
4160     /**
4161      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4162      * given the cell's data value. See {@link #setRenderer}. If not specified, the
4163      * default renderer uses the raw data value. If an object is returned (bootstrap only)
4164      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4165      */
4166        /**
4167      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
4168      */
4169     /**
4170      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
4171      */
4172
4173     /**
4174      * Returns the id of the column at the specified index.
4175      * @param {Number} index The column index
4176      * @return {String} the id
4177      */
4178     getColumnId : function(index){
4179         return this.config[index].id;
4180     },
4181
4182     /**
4183      * Returns the column for a specified id.
4184      * @param {String} id The column id
4185      * @return {Object} the column
4186      */
4187     getColumnById : function(id){
4188         return this.lookup[id];
4189     },
4190
4191     
4192     /**
4193      * Returns the column for a specified dataIndex.
4194      * @param {String} dataIndex The column dataIndex
4195      * @return {Object|Boolean} the column or false if not found
4196      */
4197     getColumnByDataIndex: function(dataIndex){
4198         var index = this.findColumnIndex(dataIndex);
4199         return index > -1 ? this.config[index] : false;
4200     },
4201     
4202     /**
4203      * Returns the index for a specified column id.
4204      * @param {String} id The column id
4205      * @return {Number} the index, or -1 if not found
4206      */
4207     getIndexById : function(id){
4208         for(var i = 0, len = this.config.length; i < len; i++){
4209             if(this.config[i].id == id){
4210                 return i;
4211             }
4212         }
4213         return -1;
4214     },
4215     
4216     /**
4217      * Returns the index for a specified column dataIndex.
4218      * @param {String} dataIndex The column dataIndex
4219      * @return {Number} the index, or -1 if not found
4220      */
4221     
4222     findColumnIndex : function(dataIndex){
4223         for(var i = 0, len = this.config.length; i < len; i++){
4224             if(this.config[i].dataIndex == dataIndex){
4225                 return i;
4226             }
4227         }
4228         return -1;
4229     },
4230     
4231     
4232     moveColumn : function(oldIndex, newIndex){
4233         var c = this.config[oldIndex];
4234         this.config.splice(oldIndex, 1);
4235         this.config.splice(newIndex, 0, c);
4236         this.dataMap = null;
4237         this.fireEvent("columnmoved", this, oldIndex, newIndex);
4238     },
4239
4240     isLocked : function(colIndex){
4241         return this.config[colIndex].locked === true;
4242     },
4243
4244     setLocked : function(colIndex, value, suppressEvent){
4245         if(this.isLocked(colIndex) == value){
4246             return;
4247         }
4248         this.config[colIndex].locked = value;
4249         if(!suppressEvent){
4250             this.fireEvent("columnlockchange", this, colIndex, value);
4251         }
4252     },
4253
4254     getTotalLockedWidth : function(){
4255         var totalWidth = 0;
4256         for(var i = 0; i < this.config.length; i++){
4257             if(this.isLocked(i) && !this.isHidden(i)){
4258                 this.totalWidth += this.getColumnWidth(i);
4259             }
4260         }
4261         return totalWidth;
4262     },
4263
4264     getLockedCount : function(){
4265         for(var i = 0, len = this.config.length; i < len; i++){
4266             if(!this.isLocked(i)){
4267                 return i;
4268             }
4269         }
4270     },
4271
4272     /**
4273      * Returns the number of columns.
4274      * @return {Number}
4275      */
4276     getColumnCount : function(visibleOnly){
4277         if(visibleOnly === true){
4278             var c = 0;
4279             for(var i = 0, len = this.config.length; i < len; i++){
4280                 if(!this.isHidden(i)){
4281                     c++;
4282                 }
4283             }
4284             return c;
4285         }
4286         return this.config.length;
4287     },
4288
4289     /**
4290      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
4291      * @param {Function} fn
4292      * @param {Object} scope (optional)
4293      * @return {Array} result
4294      */
4295     getColumnsBy : function(fn, scope){
4296         var r = [];
4297         for(var i = 0, len = this.config.length; i < len; i++){
4298             var c = this.config[i];
4299             if(fn.call(scope||this, c, i) === true){
4300                 r[r.length] = c;
4301             }
4302         }
4303         return r;
4304     },
4305
4306     /**
4307      * Returns true if the specified column is sortable.
4308      * @param {Number} col The column index
4309      * @return {Boolean}
4310      */
4311     isSortable : function(col){
4312         if(typeof this.config[col].sortable == "undefined"){
4313             return this.defaultSortable;
4314         }
4315         return this.config[col].sortable;
4316     },
4317
4318     /**
4319      * Returns the rendering (formatting) function defined for the column.
4320      * @param {Number} col The column index.
4321      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
4322      */
4323     getRenderer : function(col){
4324         if(!this.config[col].renderer){
4325             return Roo.grid.ColumnModel.defaultRenderer;
4326         }
4327         return this.config[col].renderer;
4328     },
4329
4330     /**
4331      * Sets the rendering (formatting) function for a column.
4332      * @param {Number} col The column index
4333      * @param {Function} fn The function to use to process the cell's raw data
4334      * to return HTML markup for the grid view. The render function is called with
4335      * the following parameters:<ul>
4336      * <li>Data value.</li>
4337      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
4338      * <li>css A CSS style string to apply to the table cell.</li>
4339      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
4340      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
4341      * <li>Row index</li>
4342      * <li>Column index</li>
4343      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
4344      */
4345     setRenderer : function(col, fn){
4346         this.config[col].renderer = fn;
4347     },
4348
4349     /**
4350      * Returns the width for the specified column.
4351      * @param {Number} col The column index
4352      * @return {Number}
4353      */
4354     getColumnWidth : function(col){
4355         return this.config[col].width * 1 || this.defaultWidth;
4356     },
4357
4358     /**
4359      * Sets the width for a column.
4360      * @param {Number} col The column index
4361      * @param {Number} width The new width
4362      */
4363     setColumnWidth : function(col, width, suppressEvent){
4364         this.config[col].width = width;
4365         this.totalWidth = null;
4366         if(!suppressEvent){
4367              this.fireEvent("widthchange", this, col, width);
4368         }
4369     },
4370
4371     /**
4372      * Returns the total width of all columns.
4373      * @param {Boolean} includeHidden True to include hidden column widths
4374      * @return {Number}
4375      */
4376     getTotalWidth : function(includeHidden){
4377         if(!this.totalWidth){
4378             this.totalWidth = 0;
4379             for(var i = 0, len = this.config.length; i < len; i++){
4380                 if(includeHidden || !this.isHidden(i)){
4381                     this.totalWidth += this.getColumnWidth(i);
4382                 }
4383             }
4384         }
4385         return this.totalWidth;
4386     },
4387
4388     /**
4389      * Returns the header for the specified column.
4390      * @param {Number} col The column index
4391      * @return {String}
4392      */
4393     getColumnHeader : function(col){
4394         return this.config[col].header;
4395     },
4396
4397     /**
4398      * Sets the header for a column.
4399      * @param {Number} col The column index
4400      * @param {String} header The new header
4401      */
4402     setColumnHeader : function(col, header){
4403         this.config[col].header = header;
4404         this.fireEvent("headerchange", this, col, header);
4405     },
4406
4407     /**
4408      * Returns the tooltip for the specified column.
4409      * @param {Number} col The column index
4410      * @return {String}
4411      */
4412     getColumnTooltip : function(col){
4413             return this.config[col].tooltip;
4414     },
4415     /**
4416      * Sets the tooltip for a column.
4417      * @param {Number} col The column index
4418      * @param {String} tooltip The new tooltip
4419      */
4420     setColumnTooltip : function(col, tooltip){
4421             this.config[col].tooltip = tooltip;
4422     },
4423
4424     /**
4425      * Returns the dataIndex for the specified column.
4426      * @param {Number} col The column index
4427      * @return {Number}
4428      */
4429     getDataIndex : function(col){
4430         return this.config[col].dataIndex;
4431     },
4432
4433     /**
4434      * Sets the dataIndex for a column.
4435      * @param {Number} col The column index
4436      * @param {Number} dataIndex The new dataIndex
4437      */
4438     setDataIndex : function(col, dataIndex){
4439         this.config[col].dataIndex = dataIndex;
4440     },
4441
4442     
4443     
4444     /**
4445      * Returns true if the cell is editable.
4446      * @param {Number} colIndex The column index
4447      * @param {Number} rowIndex The row index
4448      * @return {Boolean}
4449      */
4450     isCellEditable : function(colIndex, rowIndex){
4451         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
4452     },
4453
4454     /**
4455      * Returns the editor defined for the cell/column.
4456      * return false or null to disable editing.
4457      * @param {Number} colIndex The column index
4458      * @param {Number} rowIndex The row index
4459      * @return {Object}
4460      */
4461     getCellEditor : function(colIndex, rowIndex){
4462         return this.config[colIndex].editor;
4463     },
4464
4465     /**
4466      * Sets if a column is editable.
4467      * @param {Number} col The column index
4468      * @param {Boolean} editable True if the column is editable
4469      */
4470     setEditable : function(col, editable){
4471         this.config[col].editable = editable;
4472     },
4473
4474
4475     /**
4476      * Returns true if the column is hidden.
4477      * @param {Number} colIndex The column index
4478      * @return {Boolean}
4479      */
4480     isHidden : function(colIndex){
4481         return this.config[colIndex].hidden;
4482     },
4483
4484
4485     /**
4486      * Returns true if the column width cannot be changed
4487      */
4488     isFixed : function(colIndex){
4489         return this.config[colIndex].fixed;
4490     },
4491
4492     /**
4493      * Returns true if the column can be resized
4494      * @return {Boolean}
4495      */
4496     isResizable : function(colIndex){
4497         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
4498     },
4499     /**
4500      * Sets if a column is hidden.
4501      * @param {Number} colIndex The column index
4502      * @param {Boolean} hidden True if the column is hidden
4503      */
4504     setHidden : function(colIndex, hidden){
4505         this.config[colIndex].hidden = hidden;
4506         this.totalWidth = null;
4507         this.fireEvent("hiddenchange", this, colIndex, hidden);
4508     },
4509
4510     /**
4511      * Sets the editor for a column.
4512      * @param {Number} col The column index
4513      * @param {Object} editor The editor object
4514      */
4515     setEditor : function(col, editor){
4516         this.config[col].editor = editor;
4517     }
4518 });
4519
4520 Roo.grid.ColumnModel.defaultRenderer = function(value){
4521         if(typeof value == "string" && value.length < 1){
4522             return "&#160;";
4523         }
4524         return value;
4525 };
4526
4527 // Alias for backwards compatibility
4528 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
4529 /*
4530  * Based on:
4531  * Ext JS Library 1.1.1
4532  * Copyright(c) 2006-2007, Ext JS, LLC.
4533  *
4534  * Originally Released Under LGPL - original licence link has changed is not relivant.
4535  *
4536  * Fork - LGPL
4537  * <script type="text/javascript">
4538  */
4539  
4540 /**
4541  * @class Roo.LoadMask
4542  * A simple utility class for generically masking elements while loading data.  If the element being masked has
4543  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
4544  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
4545  * element's UpdateManager load indicator and will be destroyed after the initial load.
4546  * @constructor
4547  * Create a new LoadMask
4548  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
4549  * @param {Object} config The config object
4550  */
4551 Roo.LoadMask = function(el, config){
4552     this.el = Roo.get(el);
4553     Roo.apply(this, config);
4554     if(this.store){
4555         this.store.on('beforeload', this.onBeforeLoad, this);
4556         this.store.on('load', this.onLoad, this);
4557         this.store.on('loadexception', this.onLoadException, this);
4558         this.removeMask = false;
4559     }else{
4560         var um = this.el.getUpdateManager();
4561         um.showLoadIndicator = false; // disable the default indicator
4562         um.on('beforeupdate', this.onBeforeLoad, this);
4563         um.on('update', this.onLoad, this);
4564         um.on('failure', this.onLoad, this);
4565         this.removeMask = true;
4566     }
4567 };
4568
4569 Roo.LoadMask.prototype = {
4570     /**
4571      * @cfg {Boolean} removeMask
4572      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
4573      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
4574      */
4575     /**
4576      * @cfg {String} msg
4577      * The text to display in a centered loading message box (defaults to 'Loading...')
4578      */
4579     msg : 'Loading...',
4580     /**
4581      * @cfg {String} msgCls
4582      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
4583      */
4584     msgCls : 'x-mask-loading',
4585
4586     /**
4587      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
4588      * @type Boolean
4589      */
4590     disabled: false,
4591
4592     /**
4593      * Disables the mask to prevent it from being displayed
4594      */
4595     disable : function(){
4596        this.disabled = true;
4597     },
4598
4599     /**
4600      * Enables the mask so that it can be displayed
4601      */
4602     enable : function(){
4603         this.disabled = false;
4604     },
4605     
4606     onLoadException : function()
4607     {
4608         Roo.log(arguments);
4609         
4610         if (typeof(arguments[3]) != 'undefined') {
4611             Roo.MessageBox.alert("Error loading",arguments[3]);
4612         } 
4613         /*
4614         try {
4615             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
4616                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
4617             }   
4618         } catch(e) {
4619             
4620         }
4621         */
4622     
4623         
4624         
4625         this.el.unmask(this.removeMask);
4626     },
4627     // private
4628     onLoad : function()
4629     {
4630         this.el.unmask(this.removeMask);
4631     },
4632
4633     // private
4634     onBeforeLoad : function(){
4635         if(!this.disabled){
4636             this.el.mask(this.msg, this.msgCls);
4637         }
4638     },
4639
4640     // private
4641     destroy : function(){
4642         if(this.store){
4643             this.store.un('beforeload', this.onBeforeLoad, this);
4644             this.store.un('load', this.onLoad, this);
4645             this.store.un('loadexception', this.onLoadException, this);
4646         }else{
4647             var um = this.el.getUpdateManager();
4648             um.un('beforeupdate', this.onBeforeLoad, this);
4649             um.un('update', this.onLoad, this);
4650             um.un('failure', this.onLoad, this);
4651         }
4652     }
4653 };/*
4654  * - LGPL
4655  *
4656  * table
4657  * 
4658  */
4659
4660 /**
4661  * @class Roo.bootstrap.Table
4662  * @extends Roo.bootstrap.Component
4663  * Bootstrap Table class
4664  * @cfg {String} cls table class
4665  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
4666  * @cfg {String} bgcolor Specifies the background color for a table
4667  * @cfg {Number} border Specifies whether the table cells should have borders or not
4668  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
4669  * @cfg {Number} cellspacing Specifies the space between cells
4670  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
4671  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
4672  * @cfg {String} sortable Specifies that the table should be sortable
4673  * @cfg {String} summary Specifies a summary of the content of a table
4674  * @cfg {Number} width Specifies the width of a table
4675  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
4676  * 
4677  * @cfg {boolean} striped Should the rows be alternative striped
4678  * @cfg {boolean} bordered Add borders to the table
4679  * @cfg {boolean} hover Add hover highlighting
4680  * @cfg {boolean} condensed Format condensed
4681  * @cfg {boolean} responsive Format condensed
4682  * @cfg {Boolean} loadMask (true|false) default false
4683  * @cfg {Boolean} tfoot (true|false) generate tfoot, default true
4684  * @cfg {Boolean} thead (true|false) generate thead, default true
4685  * @cfg {Boolean} RowSelection (true|false) default false
4686  * @cfg {Boolean} CellSelection (true|false) default false
4687  *
4688  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
4689  
4690  * 
4691  * @constructor
4692  * Create a new Table
4693  * @param {Object} config The config object
4694  */
4695
4696 Roo.bootstrap.Table = function(config){
4697     Roo.bootstrap.Table.superclass.constructor.call(this, config);
4698     
4699     if (this.sm) {
4700         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
4701         this.sm = this.selModel;
4702         this.sm.xmodule = this.xmodule || false;
4703     }
4704     if (this.cm && typeof(this.cm.config) == 'undefined') {
4705         this.colModel = new Roo.grid.ColumnModel(this.cm);
4706         this.cm = this.colModel;
4707         this.cm.xmodule = this.xmodule || false;
4708     }
4709     if (this.store) {
4710         this.store= Roo.factory(this.store, Roo.data);
4711         this.ds = this.store;
4712         this.ds.xmodule = this.xmodule || false;
4713          
4714     }
4715     if (this.footer && this.store) {
4716         this.footer.dataSource = this.ds;
4717         this.footer = Roo.factory(this.footer);
4718     }
4719     
4720     /** @private */
4721     this.addEvents({
4722         /**
4723          * @event cellclick
4724          * Fires when a cell is clicked
4725          * @param {Roo.bootstrap.Table} this
4726          * @param {Roo.Element} el
4727          * @param {Number} rowIndex
4728          * @param {Number} columnIndex
4729          * @param {Roo.EventObject} e
4730          */
4731         "cellclick" : true,
4732         /**
4733          * @event celldblclick
4734          * Fires when a cell is double clicked
4735          * @param {Roo.bootstrap.Table} this
4736          * @param {Roo.Element} el
4737          * @param {Number} rowIndex
4738          * @param {Number} columnIndex
4739          * @param {Roo.EventObject} e
4740          */
4741         "celldblclick" : true,
4742         /**
4743          * @event rowclick
4744          * Fires when a row is clicked
4745          * @param {Roo.bootstrap.Table} this
4746          * @param {Roo.Element} el
4747          * @param {Number} rowIndex
4748          * @param {Roo.EventObject} e
4749          */
4750         "rowclick" : true,
4751         /**
4752          * @event rowdblclick
4753          * Fires when a row is double clicked
4754          * @param {Roo.bootstrap.Table} this
4755          * @param {Roo.Element} el
4756          * @param {Number} rowIndex
4757          * @param {Roo.EventObject} e
4758          */
4759         "rowdblclick" : true,
4760         /**
4761          * @event mouseover
4762          * Fires when a mouseover occur
4763          * @param {Roo.bootstrap.Table} this
4764          * @param {Roo.Element} el
4765          * @param {Number} rowIndex
4766          * @param {Number} columnIndex
4767          * @param {Roo.EventObject} e
4768          */
4769         "mouseover" : true,
4770         /**
4771          * @event mouseout
4772          * Fires when a mouseout occur
4773          * @param {Roo.bootstrap.Table} this
4774          * @param {Roo.Element} el
4775          * @param {Number} rowIndex
4776          * @param {Number} columnIndex
4777          * @param {Roo.EventObject} e
4778          */
4779         "mouseout" : true,
4780         /**
4781          * @event rowclass
4782          * Fires when a row is rendered, so you can change add a style to it.
4783          * @param {Roo.bootstrap.Table} this
4784          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
4785          */
4786         'rowclass' : true
4787         
4788     });
4789 };
4790
4791 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
4792     
4793     cls: false,
4794     align: false,
4795     bgcolor: false,
4796     border: false,
4797     cellpadding: false,
4798     cellspacing: false,
4799     frame: false,
4800     rules: false,
4801     sortable: false,
4802     summary: false,
4803     width: false,
4804     striped : false,
4805     bordered: false,
4806     hover:  false,
4807     condensed : false,
4808     responsive : false,
4809     sm : false,
4810     cm : false,
4811     store : false,
4812     loadMask : false,
4813     tfoot : true,
4814     thead : true,
4815     RowSelection : false,
4816     CellSelection : false,
4817     layout : false,
4818     
4819     // Roo.Element - the tbody
4820     mainBody: false, 
4821     
4822     getAutoCreate : function(){
4823         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
4824         
4825         cfg = {
4826             tag: 'table',
4827             cls : 'table',
4828             cn : []
4829         }
4830             
4831         if (this.striped) {
4832             cfg.cls += ' table-striped';
4833         }
4834         
4835         if (this.hover) {
4836             cfg.cls += ' table-hover';
4837         }
4838         if (this.bordered) {
4839             cfg.cls += ' table-bordered';
4840         }
4841         if (this.condensed) {
4842             cfg.cls += ' table-condensed';
4843         }
4844         if (this.responsive) {
4845             cfg.cls += ' table-responsive';
4846         }
4847         
4848         if (this.cls) {
4849             cfg.cls+=  ' ' +this.cls;
4850         }
4851         
4852         // this lot should be simplifed...
4853         
4854         if (this.align) {
4855             cfg.align=this.align;
4856         }
4857         if (this.bgcolor) {
4858             cfg.bgcolor=this.bgcolor;
4859         }
4860         if (this.border) {
4861             cfg.border=this.border;
4862         }
4863         if (this.cellpadding) {
4864             cfg.cellpadding=this.cellpadding;
4865         }
4866         if (this.cellspacing) {
4867             cfg.cellspacing=this.cellspacing;
4868         }
4869         if (this.frame) {
4870             cfg.frame=this.frame;
4871         }
4872         if (this.rules) {
4873             cfg.rules=this.rules;
4874         }
4875         if (this.sortable) {
4876             cfg.sortable=this.sortable;
4877         }
4878         if (this.summary) {
4879             cfg.summary=this.summary;
4880         }
4881         if (this.width) {
4882             cfg.width=this.width;
4883         }
4884         if (this.layout) {
4885             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
4886         }
4887         
4888         if(this.store || this.cm){
4889             if(this.thead){
4890                 cfg.cn.push(this.renderHeader());
4891             }
4892             
4893             cfg.cn.push(this.renderBody());
4894             
4895             if(this.tfoot){
4896                 cfg.cn.push(this.renderFooter());
4897             }
4898             
4899             cfg.cls+=  ' TableGrid';
4900         }
4901         
4902         return { cn : [ cfg ] };
4903     },
4904     
4905     initEvents : function()
4906     {   
4907         if(!this.store || !this.cm){
4908             return;
4909         }
4910         
4911         //Roo.log('initEvents with ds!!!!');
4912         
4913         this.mainBody = this.el.select('tbody', true).first();
4914         
4915         
4916         var _this = this;
4917         
4918         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
4919             e.on('click', _this.sort, _this);
4920         });
4921         
4922         this.el.on("click", this.onClick, this);
4923         this.el.on("dblclick", this.onDblClick, this);
4924         
4925         this.parent().el.setStyle('position', 'relative');
4926         if (this.footer) {
4927             this.footer.parentId = this.id;
4928             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
4929         }
4930         
4931         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
4932         
4933         this.store.on('load', this.onLoad, this);
4934         this.store.on('beforeload', this.onBeforeLoad, this);
4935         this.store.on('update', this.onUpdate, this);
4936         
4937     },
4938     
4939     onMouseover : function(e, el)
4940     {
4941         var cell = Roo.get(el);
4942         
4943         if(!cell){
4944             return;
4945         }
4946         
4947         if(e.getTarget().nodeName.toLowerCase() != 'td'){
4948             cell = cell.findParent('td', false, true);
4949         }
4950         
4951         var row = cell.findParent('tr', false, true);
4952         var cellIndex = cell.dom.cellIndex;
4953         var rowIndex = row.dom.rowIndex - 1; // start from 0
4954         
4955         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
4956         
4957     },
4958     
4959     onMouseout : function(e, el)
4960     {
4961         var cell = Roo.get(el);
4962         
4963         if(!cell){
4964             return;
4965         }
4966         
4967         if(e.getTarget().nodeName.toLowerCase() != 'td'){
4968             cell = cell.findParent('td', false, true);
4969         }
4970         
4971         var row = cell.findParent('tr', false, true);
4972         var cellIndex = cell.dom.cellIndex;
4973         var rowIndex = row.dom.rowIndex - 1; // start from 0
4974         
4975         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
4976         
4977     },
4978     
4979     onClick : function(e, el)
4980     {
4981         var cell = Roo.get(el);
4982         
4983         if(!cell || (!this.CellSelection && !this.RowSelection)){
4984             return;
4985         }
4986         
4987         
4988         if(e.getTarget().nodeName.toLowerCase() != 'td'){
4989             cell = cell.findParent('td', false, true);
4990         }
4991         
4992         var row = cell.findParent('tr', false, true);
4993         var cellIndex = cell.dom.cellIndex;
4994         var rowIndex = row.dom.rowIndex - 1;
4995         
4996         if(this.CellSelection){
4997             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
4998         }
4999         
5000         if(this.RowSelection){
5001             this.fireEvent('rowclick', this, row, rowIndex, e);
5002         }
5003         
5004         
5005     },
5006     
5007     onDblClick : function(e,el)
5008     {
5009         var cell = Roo.get(el);
5010         
5011         if(!cell || (!this.CellSelection && !this.RowSelection)){
5012             return;
5013         }
5014         
5015         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5016             cell = cell.findParent('td', false, true);
5017         }
5018         
5019         var row = cell.findParent('tr', false, true);
5020         var cellIndex = cell.dom.cellIndex;
5021         var rowIndex = row.dom.rowIndex - 1;
5022         
5023         if(this.CellSelection){
5024             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5025         }
5026         
5027         if(this.RowSelection){
5028             this.fireEvent('rowdblclick', this, row, rowIndex, e);
5029         }
5030     },
5031     
5032     sort : function(e,el)
5033     {
5034         var col = Roo.get(el)
5035         
5036         if(!col.hasClass('sortable')){
5037             return;
5038         }
5039         
5040         var sort = col.attr('sort');
5041         var dir = 'ASC';
5042         
5043         if(col.hasClass('glyphicon-arrow-up')){
5044             dir = 'DESC';
5045         }
5046         
5047         this.store.sortInfo = {field : sort, direction : dir};
5048         
5049         if (this.footer) {
5050             Roo.log("calling footer first");
5051             this.footer.onClick('first');
5052         } else {
5053         
5054             this.store.load({ params : { start : 0 } });
5055         }
5056     },
5057     
5058     renderHeader : function()
5059     {
5060         var header = {
5061             tag: 'thead',
5062             cn : []
5063         };
5064         
5065         var cm = this.cm;
5066         
5067         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5068             
5069             var config = cm.config[i];
5070                     
5071             var c = {
5072                 tag: 'th',
5073                 style : '',
5074                 html: cm.getColumnHeader(i)
5075             };
5076             
5077             if(typeof(config.hidden) != 'undefined' && config.hidden){
5078                 c.style += ' display:none;';
5079             }
5080             
5081             if(typeof(config.dataIndex) != 'undefined'){
5082                 c.sort = config.dataIndex;
5083             }
5084             
5085             if(typeof(config.sortable) != 'undefined' && config.sortable){
5086                 c.cls = 'sortable';
5087             }
5088             
5089             if(typeof(config.align) != 'undefined' && config.align.length){
5090                 c.style += ' text-align:' + config.align + ';';
5091             }
5092             
5093             if(typeof(config.width) != 'undefined'){
5094                 c.style += ' width:' + config.width + 'px;';
5095             }
5096             
5097             header.cn.push(c)
5098         }
5099         
5100         return header;
5101     },
5102     
5103     renderBody : function()
5104     {
5105         var body = {
5106             tag: 'tbody',
5107             cn : [
5108                 {
5109                     tag: 'tr',
5110                     cn : [
5111                         {
5112                             tag : 'td',
5113                             colspan :  this.cm.getColumnCount()
5114                         }
5115                     ]
5116                 }
5117             ]
5118         };
5119         
5120         return body;
5121     },
5122     
5123     renderFooter : function()
5124     {
5125         var footer = {
5126             tag: 'tfoot',
5127             cn : [
5128                 {
5129                     tag: 'tr',
5130                     cn : [
5131                         {
5132                             tag : 'td',
5133                             colspan :  this.cm.getColumnCount()
5134                         }
5135                     ]
5136                 }
5137             ]
5138         };
5139         
5140         return footer;
5141     },
5142     
5143     
5144     
5145     onLoad : function()
5146     {
5147         Roo.log('ds onload');
5148         this.clear();
5149         
5150         var _this = this;
5151         var cm = this.cm;
5152         var ds = this.store;
5153         
5154         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5155             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
5156             
5157             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
5158                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
5159             }
5160             
5161             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
5162                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
5163             }
5164         });
5165         
5166         var tbody =  this.mainBody;
5167               
5168         if(ds.getCount() > 0){
5169             ds.data.each(function(d,rowIndex){
5170                 var row =  this.renderRow(cm, ds, rowIndex);
5171                 
5172                 tbody.createChild(row);
5173                 
5174                 var _this = this;
5175                 
5176                 if(row.cellObjects.length){
5177                     Roo.each(row.cellObjects, function(r){
5178                         _this.renderCellObject(r);
5179                     })
5180                 }
5181                 
5182             }, this);
5183         }
5184         
5185         Roo.each(this.el.select('tbody td', true).elements, function(e){
5186             e.on('mouseover', _this.onMouseover, _this);
5187         });
5188         
5189         Roo.each(this.el.select('tbody td', true).elements, function(e){
5190             e.on('mouseout', _this.onMouseout, _this);
5191         });
5192
5193         //if(this.loadMask){
5194         //    this.maskEl.hide();
5195         //}
5196     },
5197     
5198     
5199     onUpdate : function(ds,record)
5200     {
5201         this.refreshRow(record);
5202     },
5203     onRemove : function(ds, record, index, isUpdate){
5204         if(isUpdate !== true){
5205             this.fireEvent("beforerowremoved", this, index, record);
5206         }
5207         var bt = this.mainBody.dom;
5208         if(bt.rows[index]){
5209             bt.removeChild(bt.rows[index]);
5210         }
5211         
5212         if(isUpdate !== true){
5213             //this.stripeRows(index);
5214             //this.syncRowHeights(index, index);
5215             //this.layout();
5216             this.fireEvent("rowremoved", this, index, record);
5217         }
5218     },
5219     
5220     
5221     refreshRow : function(record){
5222         var ds = this.store, index;
5223         if(typeof record == 'number'){
5224             index = record;
5225             record = ds.getAt(index);
5226         }else{
5227             index = ds.indexOf(record);
5228         }
5229         this.insertRow(ds, index, true);
5230         this.onRemove(ds, record, index+1, true);
5231         //this.syncRowHeights(index, index);
5232         //this.layout();
5233         this.fireEvent("rowupdated", this, index, record);
5234     },
5235     
5236     insertRow : function(dm, rowIndex, isUpdate){
5237         
5238         if(!isUpdate){
5239             this.fireEvent("beforerowsinserted", this, rowIndex);
5240         }
5241             //var s = this.getScrollState();
5242         var row = this.renderRow(this.cm, this.store, rowIndex);
5243         // insert before rowIndex..
5244         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
5245         
5246         var _this = this;
5247                 
5248         if(row.cellObjects.length){
5249             Roo.each(row.cellObjects, function(r){
5250                 _this.renderCellObject(r);
5251             })
5252         }
5253             
5254         if(!isUpdate){
5255             this.fireEvent("rowsinserted", this, rowIndex);
5256             //this.syncRowHeights(firstRow, lastRow);
5257             //this.stripeRows(firstRow);
5258             //this.layout();
5259         }
5260         
5261     },
5262     
5263     
5264     getRowDom : function(rowIndex)
5265     {
5266         // not sure if I need to check this.. but let's do it anyway..
5267         return (this.mainBody.dom.rows && (rowIndex-1) < this.mainBody.dom.rows.length ) ?
5268                 this.mainBody.dom.rows[rowIndex] : false
5269     },
5270     // returns the object tree for a tr..
5271   
5272     
5273     renderRow : function(cm, ds, rowIndex) {
5274         
5275         var d = ds.getAt(rowIndex);
5276         
5277         var row = {
5278             tag : 'tr',
5279             cn : []
5280         };
5281             
5282         var cellObjects = [];
5283         
5284         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5285             var config = cm.config[i];
5286             
5287             var renderer = cm.getRenderer(i);
5288             var value = '';
5289             var id = false;
5290             
5291             if(typeof(renderer) !== 'undefined'){
5292                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
5293             }
5294             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
5295             // and are rendered into the cells after the row is rendered - using the id for the element.
5296             
5297             if(typeof(value) === 'object'){
5298                 id = Roo.id();
5299                 cellObjects.push({
5300                     container : id,
5301                     cfg : value 
5302                 })
5303             }
5304             
5305             var rowcfg = {
5306                 record: d,
5307                 rowIndex : rowIndex,
5308                 colIndex : i,
5309                 rowClass : ''
5310             }
5311
5312             this.fireEvent('rowclass', this, rowcfg);
5313             
5314             var td = {
5315                 tag: 'td',
5316                 cls : rowcfg.rowClass,
5317                 style: '',
5318                 html: (typeof(value) === 'object') ? '' : value
5319             };
5320             
5321             if (id) {
5322                 td.id = id;
5323             }
5324             
5325             if(typeof(config.hidden) != 'undefined' && config.hidden){
5326                 td.style += ' display:none;';
5327             }
5328             
5329             if(typeof(config.align) != 'undefined' && config.align.length){
5330                 td.style += ' text-align:' + config.align + ';';
5331             }
5332             
5333             if(typeof(config.width) != 'undefined'){
5334                 td.style += ' width:' +  config.width + 'px;';
5335             }
5336              
5337             row.cn.push(td);
5338            
5339         }
5340         
5341         row.cellObjects = cellObjects;
5342         
5343         return row;
5344           
5345     },
5346     
5347     
5348     
5349     onBeforeLoad : function()
5350     {
5351         //Roo.log('ds onBeforeLoad');
5352         
5353         //this.clear();
5354         
5355         //if(this.loadMask){
5356         //    this.maskEl.show();
5357         //}
5358     },
5359     
5360     clear : function()
5361     {
5362         this.el.select('tbody', true).first().dom.innerHTML = '';
5363     },
5364     
5365     getSelectionModel : function(){
5366         if(!this.selModel){
5367             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
5368         }
5369         return this.selModel;
5370     },
5371     /*
5372      * Render the Roo.bootstrap object from renderder
5373      */
5374     renderCellObject : function(r)
5375     {
5376         var _this = this;
5377         
5378         var t = r.cfg.render(r.container);
5379         
5380         if(r.cfg.cn){
5381             Roo.each(r.cfg.cn, function(c){
5382                 var child = {
5383                     container: t.getChildContainer(),
5384                     cfg: c
5385                 }
5386                 _this.renderCellObject(child);
5387             })
5388         }
5389     }
5390    
5391 });
5392
5393  
5394
5395  /*
5396  * - LGPL
5397  *
5398  * table cell
5399  * 
5400  */
5401
5402 /**
5403  * @class Roo.bootstrap.TableCell
5404  * @extends Roo.bootstrap.Component
5405  * Bootstrap TableCell class
5406  * @cfg {String} html cell contain text
5407  * @cfg {String} cls cell class
5408  * @cfg {String} tag cell tag (td|th) default td
5409  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
5410  * @cfg {String} align Aligns the content in a cell
5411  * @cfg {String} axis Categorizes cells
5412  * @cfg {String} bgcolor Specifies the background color of a cell
5413  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5414  * @cfg {Number} colspan Specifies the number of columns a cell should span
5415  * @cfg {String} headers Specifies one or more header cells a cell is related to
5416  * @cfg {Number} height Sets the height of a cell
5417  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
5418  * @cfg {Number} rowspan Sets the number of rows a cell should span
5419  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
5420  * @cfg {String} valign Vertical aligns the content in a cell
5421  * @cfg {Number} width Specifies the width of a cell
5422  * 
5423  * @constructor
5424  * Create a new TableCell
5425  * @param {Object} config The config object
5426  */
5427
5428 Roo.bootstrap.TableCell = function(config){
5429     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
5430 };
5431
5432 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
5433     
5434     html: false,
5435     cls: false,
5436     tag: false,
5437     abbr: false,
5438     align: false,
5439     axis: false,
5440     bgcolor: false,
5441     charoff: false,
5442     colspan: false,
5443     headers: false,
5444     height: false,
5445     nowrap: false,
5446     rowspan: false,
5447     scope: false,
5448     valign: false,
5449     width: false,
5450     
5451     
5452     getAutoCreate : function(){
5453         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
5454         
5455         cfg = {
5456             tag: 'td'
5457         }
5458         
5459         if(this.tag){
5460             cfg.tag = this.tag;
5461         }
5462         
5463         if (this.html) {
5464             cfg.html=this.html
5465         }
5466         if (this.cls) {
5467             cfg.cls=this.cls
5468         }
5469         if (this.abbr) {
5470             cfg.abbr=this.abbr
5471         }
5472         if (this.align) {
5473             cfg.align=this.align
5474         }
5475         if (this.axis) {
5476             cfg.axis=this.axis
5477         }
5478         if (this.bgcolor) {
5479             cfg.bgcolor=this.bgcolor
5480         }
5481         if (this.charoff) {
5482             cfg.charoff=this.charoff
5483         }
5484         if (this.colspan) {
5485             cfg.colspan=this.colspan
5486         }
5487         if (this.headers) {
5488             cfg.headers=this.headers
5489         }
5490         if (this.height) {
5491             cfg.height=this.height
5492         }
5493         if (this.nowrap) {
5494             cfg.nowrap=this.nowrap
5495         }
5496         if (this.rowspan) {
5497             cfg.rowspan=this.rowspan
5498         }
5499         if (this.scope) {
5500             cfg.scope=this.scope
5501         }
5502         if (this.valign) {
5503             cfg.valign=this.valign
5504         }
5505         if (this.width) {
5506             cfg.width=this.width
5507         }
5508         
5509         
5510         return cfg;
5511     }
5512    
5513 });
5514
5515  
5516
5517  /*
5518  * - LGPL
5519  *
5520  * table row
5521  * 
5522  */
5523
5524 /**
5525  * @class Roo.bootstrap.TableRow
5526  * @extends Roo.bootstrap.Component
5527  * Bootstrap TableRow class
5528  * @cfg {String} cls row class
5529  * @cfg {String} align Aligns the content in a table row
5530  * @cfg {String} bgcolor Specifies a background color for a table row
5531  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5532  * @cfg {String} valign Vertical aligns the content in a table row
5533  * 
5534  * @constructor
5535  * Create a new TableRow
5536  * @param {Object} config The config object
5537  */
5538
5539 Roo.bootstrap.TableRow = function(config){
5540     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
5541 };
5542
5543 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
5544     
5545     cls: false,
5546     align: false,
5547     bgcolor: false,
5548     charoff: false,
5549     valign: false,
5550     
5551     getAutoCreate : function(){
5552         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
5553         
5554         cfg = {
5555             tag: 'tr'
5556         }
5557             
5558         if(this.cls){
5559             cfg.cls = this.cls;
5560         }
5561         if(this.align){
5562             cfg.align = this.align;
5563         }
5564         if(this.bgcolor){
5565             cfg.bgcolor = this.bgcolor;
5566         }
5567         if(this.charoff){
5568             cfg.charoff = this.charoff;
5569         }
5570         if(this.valign){
5571             cfg.valign = this.valign;
5572         }
5573         
5574         return cfg;
5575     }
5576    
5577 });
5578
5579  
5580
5581  /*
5582  * - LGPL
5583  *
5584  * table body
5585  * 
5586  */
5587
5588 /**
5589  * @class Roo.bootstrap.TableBody
5590  * @extends Roo.bootstrap.Component
5591  * Bootstrap TableBody class
5592  * @cfg {String} cls element class
5593  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
5594  * @cfg {String} align Aligns the content inside the element
5595  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
5596  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
5597  * 
5598  * @constructor
5599  * Create a new TableBody
5600  * @param {Object} config The config object
5601  */
5602
5603 Roo.bootstrap.TableBody = function(config){
5604     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
5605 };
5606
5607 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
5608     
5609     cls: false,
5610     tag: false,
5611     align: false,
5612     charoff: false,
5613     valign: false,
5614     
5615     getAutoCreate : function(){
5616         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
5617         
5618         cfg = {
5619             tag: 'tbody'
5620         }
5621             
5622         if (this.cls) {
5623             cfg.cls=this.cls
5624         }
5625         if(this.tag){
5626             cfg.tag = this.tag;
5627         }
5628         
5629         if(this.align){
5630             cfg.align = this.align;
5631         }
5632         if(this.charoff){
5633             cfg.charoff = this.charoff;
5634         }
5635         if(this.valign){
5636             cfg.valign = this.valign;
5637         }
5638         
5639         return cfg;
5640     }
5641     
5642     
5643 //    initEvents : function()
5644 //    {
5645 //        
5646 //        if(!this.store){
5647 //            return;
5648 //        }
5649 //        
5650 //        this.store = Roo.factory(this.store, Roo.data);
5651 //        this.store.on('load', this.onLoad, this);
5652 //        
5653 //        this.store.load();
5654 //        
5655 //    },
5656 //    
5657 //    onLoad: function () 
5658 //    {   
5659 //        this.fireEvent('load', this);
5660 //    }
5661 //    
5662 //   
5663 });
5664
5665  
5666
5667  /*
5668  * Based on:
5669  * Ext JS Library 1.1.1
5670  * Copyright(c) 2006-2007, Ext JS, LLC.
5671  *
5672  * Originally Released Under LGPL - original licence link has changed is not relivant.
5673  *
5674  * Fork - LGPL
5675  * <script type="text/javascript">
5676  */
5677
5678 // as we use this in bootstrap.
5679 Roo.namespace('Roo.form');
5680  /**
5681  * @class Roo.form.Action
5682  * Internal Class used to handle form actions
5683  * @constructor
5684  * @param {Roo.form.BasicForm} el The form element or its id
5685  * @param {Object} config Configuration options
5686  */
5687
5688  
5689  
5690 // define the action interface
5691 Roo.form.Action = function(form, options){
5692     this.form = form;
5693     this.options = options || {};
5694 };
5695 /**
5696  * Client Validation Failed
5697  * @const 
5698  */
5699 Roo.form.Action.CLIENT_INVALID = 'client';
5700 /**
5701  * Server Validation Failed
5702  * @const 
5703  */
5704 Roo.form.Action.SERVER_INVALID = 'server';
5705  /**
5706  * Connect to Server Failed
5707  * @const 
5708  */
5709 Roo.form.Action.CONNECT_FAILURE = 'connect';
5710 /**
5711  * Reading Data from Server Failed
5712  * @const 
5713  */
5714 Roo.form.Action.LOAD_FAILURE = 'load';
5715
5716 Roo.form.Action.prototype = {
5717     type : 'default',
5718     failureType : undefined,
5719     response : undefined,
5720     result : undefined,
5721
5722     // interface method
5723     run : function(options){
5724
5725     },
5726
5727     // interface method
5728     success : function(response){
5729
5730     },
5731
5732     // interface method
5733     handleResponse : function(response){
5734
5735     },
5736
5737     // default connection failure
5738     failure : function(response){
5739         
5740         this.response = response;
5741         this.failureType = Roo.form.Action.CONNECT_FAILURE;
5742         this.form.afterAction(this, false);
5743     },
5744
5745     processResponse : function(response){
5746         this.response = response;
5747         if(!response.responseText){
5748             return true;
5749         }
5750         this.result = this.handleResponse(response);
5751         return this.result;
5752     },
5753
5754     // utility functions used internally
5755     getUrl : function(appendParams){
5756         var url = this.options.url || this.form.url || this.form.el.dom.action;
5757         if(appendParams){
5758             var p = this.getParams();
5759             if(p){
5760                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
5761             }
5762         }
5763         return url;
5764     },
5765
5766     getMethod : function(){
5767         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
5768     },
5769
5770     getParams : function(){
5771         var bp = this.form.baseParams;
5772         var p = this.options.params;
5773         if(p){
5774             if(typeof p == "object"){
5775                 p = Roo.urlEncode(Roo.applyIf(p, bp));
5776             }else if(typeof p == 'string' && bp){
5777                 p += '&' + Roo.urlEncode(bp);
5778             }
5779         }else if(bp){
5780             p = Roo.urlEncode(bp);
5781         }
5782         return p;
5783     },
5784
5785     createCallback : function(){
5786         return {
5787             success: this.success,
5788             failure: this.failure,
5789             scope: this,
5790             timeout: (this.form.timeout*1000),
5791             upload: this.form.fileUpload ? this.success : undefined
5792         };
5793     }
5794 };
5795
5796 Roo.form.Action.Submit = function(form, options){
5797     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
5798 };
5799
5800 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
5801     type : 'submit',
5802
5803     haveProgress : false,
5804     uploadComplete : false,
5805     
5806     // uploadProgress indicator.
5807     uploadProgress : function()
5808     {
5809         if (!this.form.progressUrl) {
5810             return;
5811         }
5812         
5813         if (!this.haveProgress) {
5814             Roo.MessageBox.progress("Uploading", "Uploading");
5815         }
5816         if (this.uploadComplete) {
5817            Roo.MessageBox.hide();
5818            return;
5819         }
5820         
5821         this.haveProgress = true;
5822    
5823         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
5824         
5825         var c = new Roo.data.Connection();
5826         c.request({
5827             url : this.form.progressUrl,
5828             params: {
5829                 id : uid
5830             },
5831             method: 'GET',
5832             success : function(req){
5833                //console.log(data);
5834                 var rdata = false;
5835                 var edata;
5836                 try  {
5837                    rdata = Roo.decode(req.responseText)
5838                 } catch (e) {
5839                     Roo.log("Invalid data from server..");
5840                     Roo.log(edata);
5841                     return;
5842                 }
5843                 if (!rdata || !rdata.success) {
5844                     Roo.log(rdata);
5845                     Roo.MessageBox.alert(Roo.encode(rdata));
5846                     return;
5847                 }
5848                 var data = rdata.data;
5849                 
5850                 if (this.uploadComplete) {
5851                    Roo.MessageBox.hide();
5852                    return;
5853                 }
5854                    
5855                 if (data){
5856                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
5857                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
5858                     );
5859                 }
5860                 this.uploadProgress.defer(2000,this);
5861             },
5862        
5863             failure: function(data) {
5864                 Roo.log('progress url failed ');
5865                 Roo.log(data);
5866             },
5867             scope : this
5868         });
5869            
5870     },
5871     
5872     
5873     run : function()
5874     {
5875         // run get Values on the form, so it syncs any secondary forms.
5876         this.form.getValues();
5877         
5878         var o = this.options;
5879         var method = this.getMethod();
5880         var isPost = method == 'POST';
5881         if(o.clientValidation === false || this.form.isValid()){
5882             
5883             if (this.form.progressUrl) {
5884                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
5885                     (new Date() * 1) + '' + Math.random());
5886                     
5887             } 
5888             
5889             
5890             Roo.Ajax.request(Roo.apply(this.createCallback(), {
5891                 form:this.form.el.dom,
5892                 url:this.getUrl(!isPost),
5893                 method: method,
5894                 params:isPost ? this.getParams() : null,
5895                 isUpload: this.form.fileUpload
5896             }));
5897             
5898             this.uploadProgress();
5899
5900         }else if (o.clientValidation !== false){ // client validation failed
5901             this.failureType = Roo.form.Action.CLIENT_INVALID;
5902             this.form.afterAction(this, false);
5903         }
5904     },
5905
5906     success : function(response)
5907     {
5908         this.uploadComplete= true;
5909         if (this.haveProgress) {
5910             Roo.MessageBox.hide();
5911         }
5912         
5913         
5914         var result = this.processResponse(response);
5915         if(result === true || result.success){
5916             this.form.afterAction(this, true);
5917             return;
5918         }
5919         if(result.errors){
5920             this.form.markInvalid(result.errors);
5921             this.failureType = Roo.form.Action.SERVER_INVALID;
5922         }
5923         this.form.afterAction(this, false);
5924     },
5925     failure : function(response)
5926     {
5927         this.uploadComplete= true;
5928         if (this.haveProgress) {
5929             Roo.MessageBox.hide();
5930         }
5931         
5932         this.response = response;
5933         this.failureType = Roo.form.Action.CONNECT_FAILURE;
5934         this.form.afterAction(this, false);
5935     },
5936     
5937     handleResponse : function(response){
5938         if(this.form.errorReader){
5939             var rs = this.form.errorReader.read(response);
5940             var errors = [];
5941             if(rs.records){
5942                 for(var i = 0, len = rs.records.length; i < len; i++) {
5943                     var r = rs.records[i];
5944                     errors[i] = r.data;
5945                 }
5946             }
5947             if(errors.length < 1){
5948                 errors = null;
5949             }
5950             return {
5951                 success : rs.success,
5952                 errors : errors
5953             };
5954         }
5955         var ret = false;
5956         try {
5957             ret = Roo.decode(response.responseText);
5958         } catch (e) {
5959             ret = {
5960                 success: false,
5961                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
5962                 errors : []
5963             };
5964         }
5965         return ret;
5966         
5967     }
5968 });
5969
5970
5971 Roo.form.Action.Load = function(form, options){
5972     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
5973     this.reader = this.form.reader;
5974 };
5975
5976 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
5977     type : 'load',
5978
5979     run : function(){
5980         
5981         Roo.Ajax.request(Roo.apply(
5982                 this.createCallback(), {
5983                     method:this.getMethod(),
5984                     url:this.getUrl(false),
5985                     params:this.getParams()
5986         }));
5987     },
5988
5989     success : function(response){
5990         
5991         var result = this.processResponse(response);
5992         if(result === true || !result.success || !result.data){
5993             this.failureType = Roo.form.Action.LOAD_FAILURE;
5994             this.form.afterAction(this, false);
5995             return;
5996         }
5997         this.form.clearInvalid();
5998         this.form.setValues(result.data);
5999         this.form.afterAction(this, true);
6000     },
6001
6002     handleResponse : function(response){
6003         if(this.form.reader){
6004             var rs = this.form.reader.read(response);
6005             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
6006             return {
6007                 success : rs.success,
6008                 data : data
6009             };
6010         }
6011         return Roo.decode(response.responseText);
6012     }
6013 });
6014
6015 Roo.form.Action.ACTION_TYPES = {
6016     'load' : Roo.form.Action.Load,
6017     'submit' : Roo.form.Action.Submit
6018 };/*
6019  * - LGPL
6020  *
6021  * form
6022  * 
6023  */
6024
6025 /**
6026  * @class Roo.bootstrap.Form
6027  * @extends Roo.bootstrap.Component
6028  * Bootstrap Form class
6029  * @cfg {String} method  GET | POST (default POST)
6030  * @cfg {String} labelAlign top | left (default top)
6031   * @cfg {String} align left  | right - for navbars
6032
6033  * 
6034  * @constructor
6035  * Create a new Form
6036  * @param {Object} config The config object
6037  */
6038
6039
6040 Roo.bootstrap.Form = function(config){
6041     Roo.bootstrap.Form.superclass.constructor.call(this, config);
6042     this.addEvents({
6043         /**
6044          * @event clientvalidation
6045          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
6046          * @param {Form} this
6047          * @param {Boolean} valid true if the form has passed client-side validation
6048          */
6049         clientvalidation: true,
6050         /**
6051          * @event beforeaction
6052          * Fires before any action is performed. Return false to cancel the action.
6053          * @param {Form} this
6054          * @param {Action} action The action to be performed
6055          */
6056         beforeaction: true,
6057         /**
6058          * @event actionfailed
6059          * Fires when an action fails.
6060          * @param {Form} this
6061          * @param {Action} action The action that failed
6062          */
6063         actionfailed : true,
6064         /**
6065          * @event actioncomplete
6066          * Fires when an action is completed.
6067          * @param {Form} this
6068          * @param {Action} action The action that completed
6069          */
6070         actioncomplete : true
6071     });
6072     
6073 };
6074
6075 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
6076       
6077      /**
6078      * @cfg {String} method
6079      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
6080      */
6081     method : 'POST',
6082     /**
6083      * @cfg {String} url
6084      * The URL to use for form actions if one isn't supplied in the action options.
6085      */
6086     /**
6087      * @cfg {Boolean} fileUpload
6088      * Set to true if this form is a file upload.
6089      */
6090      
6091     /**
6092      * @cfg {Object} baseParams
6093      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
6094      */
6095       
6096     /**
6097      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
6098      */
6099     timeout: 30,
6100     /**
6101      * @cfg {Sting} align (left|right) for navbar forms
6102      */
6103     align : 'left',
6104
6105     // private
6106     activeAction : null,
6107  
6108     /**
6109      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
6110      * element by passing it or its id or mask the form itself by passing in true.
6111      * @type Mixed
6112      */
6113     waitMsgTarget : false,
6114     
6115      
6116     
6117     /**
6118      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
6119      * element by passing it or its id or mask the form itself by passing in true.
6120      * @type Mixed
6121      */
6122     
6123     getAutoCreate : function(){
6124         
6125         var cfg = {
6126             tag: 'form',
6127             method : this.method || 'POST',
6128             id : this.id || Roo.id(),
6129             cls : ''
6130         }
6131         if (this.parent().xtype.match(/^Nav/)) {
6132             cfg.cls = 'navbar-form navbar-' + this.align;
6133             
6134         }
6135         
6136         if (this.labelAlign == 'left' ) {
6137             cfg.cls += ' form-horizontal';
6138         }
6139         
6140         
6141         return cfg;
6142     },
6143     initEvents : function()
6144     {
6145         this.el.on('submit', this.onSubmit, this);
6146         // this was added as random key presses on the form where triggering form submit.
6147         this.el.on('keypress', function(e) {
6148             if (e.getCharCode() != 13) {
6149                 return true;
6150             }
6151             // we might need to allow it for textareas.. and some other items.
6152             // check e.getTarget().
6153             
6154             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
6155                 return true;
6156             }
6157         
6158             Roo.log("keypress blocked");
6159             
6160             e.preventDefault();
6161             return false;
6162         });
6163         
6164     },
6165     // private
6166     onSubmit : function(e){
6167         e.stopEvent();
6168     },
6169     
6170      /**
6171      * Returns true if client-side validation on the form is successful.
6172      * @return Boolean
6173      */
6174     isValid : function(){
6175         var items = this.getItems();
6176         var valid = true;
6177         items.each(function(f){
6178            if(!f.validate()){
6179                valid = false;
6180                
6181            }
6182         });
6183         return valid;
6184     },
6185     /**
6186      * Returns true if any fields in this form have changed since their original load.
6187      * @return Boolean
6188      */
6189     isDirty : function(){
6190         var dirty = false;
6191         var items = this.getItems();
6192         items.each(function(f){
6193            if(f.isDirty()){
6194                dirty = true;
6195                return false;
6196            }
6197            return true;
6198         });
6199         return dirty;
6200     },
6201      /**
6202      * Performs a predefined action (submit or load) or custom actions you define on this form.
6203      * @param {String} actionName The name of the action type
6204      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
6205      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
6206      * accept other config options):
6207      * <pre>
6208 Property          Type             Description
6209 ----------------  ---------------  ----------------------------------------------------------------------------------
6210 url               String           The url for the action (defaults to the form's url)
6211 method            String           The form method to use (defaults to the form's method, or POST if not defined)
6212 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
6213 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
6214                                    validate the form on the client (defaults to false)
6215      * </pre>
6216      * @return {BasicForm} this
6217      */
6218     doAction : function(action, options){
6219         if(typeof action == 'string'){
6220             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
6221         }
6222         if(this.fireEvent('beforeaction', this, action) !== false){
6223             this.beforeAction(action);
6224             action.run.defer(100, action);
6225         }
6226         return this;
6227     },
6228     
6229     // private
6230     beforeAction : function(action){
6231         var o = action.options;
6232         
6233         // not really supported yet.. ??
6234         
6235         //if(this.waitMsgTarget === true){
6236             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6237         //}else if(this.waitMsgTarget){
6238         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
6239         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
6240         //}else {
6241         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
6242        // }
6243          
6244     },
6245
6246     // private
6247     afterAction : function(action, success){
6248         this.activeAction = null;
6249         var o = action.options;
6250         
6251         //if(this.waitMsgTarget === true){
6252             this.el.unmask();
6253         //}else if(this.waitMsgTarget){
6254         //    this.waitMsgTarget.unmask();
6255         //}else{
6256         //    Roo.MessageBox.updateProgress(1);
6257         //    Roo.MessageBox.hide();
6258        // }
6259         // 
6260         if(success){
6261             if(o.reset){
6262                 this.reset();
6263             }
6264             Roo.callback(o.success, o.scope, [this, action]);
6265             this.fireEvent('actioncomplete', this, action);
6266             
6267         }else{
6268             
6269             // failure condition..
6270             // we have a scenario where updates need confirming.
6271             // eg. if a locking scenario exists..
6272             // we look for { errors : { needs_confirm : true }} in the response.
6273             if (
6274                 (typeof(action.result) != 'undefined')  &&
6275                 (typeof(action.result.errors) != 'undefined')  &&
6276                 (typeof(action.result.errors.needs_confirm) != 'undefined')
6277            ){
6278                 var _t = this;
6279                 Roo.log("not supported yet");
6280                  /*
6281                 
6282                 Roo.MessageBox.confirm(
6283                     "Change requires confirmation",
6284                     action.result.errorMsg,
6285                     function(r) {
6286                         if (r != 'yes') {
6287                             return;
6288                         }
6289                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
6290                     }
6291                     
6292                 );
6293                 */
6294                 
6295                 
6296                 return;
6297             }
6298             
6299             Roo.callback(o.failure, o.scope, [this, action]);
6300             // show an error message if no failed handler is set..
6301             if (!this.hasListener('actionfailed')) {
6302                 Roo.log("need to add dialog support");
6303                 /*
6304                 Roo.MessageBox.alert("Error",
6305                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
6306                         action.result.errorMsg :
6307                         "Saving Failed, please check your entries or try again"
6308                 );
6309                 */
6310             }
6311             
6312             this.fireEvent('actionfailed', this, action);
6313         }
6314         
6315     },
6316     /**
6317      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
6318      * @param {String} id The value to search for
6319      * @return Field
6320      */
6321     findField : function(id){
6322         var items = this.getItems();
6323         var field = items.get(id);
6324         if(!field){
6325              items.each(function(f){
6326                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
6327                     field = f;
6328                     return false;
6329                 }
6330                 return true;
6331             });
6332         }
6333         return field || null;
6334     },
6335      /**
6336      * Mark fields in this form invalid in bulk.
6337      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
6338      * @return {BasicForm} this
6339      */
6340     markInvalid : function(errors){
6341         if(errors instanceof Array){
6342             for(var i = 0, len = errors.length; i < len; i++){
6343                 var fieldError = errors[i];
6344                 var f = this.findField(fieldError.id);
6345                 if(f){
6346                     f.markInvalid(fieldError.msg);
6347                 }
6348             }
6349         }else{
6350             var field, id;
6351             for(id in errors){
6352                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
6353                     field.markInvalid(errors[id]);
6354                 }
6355             }
6356         }
6357         //Roo.each(this.childForms || [], function (f) {
6358         //    f.markInvalid(errors);
6359         //});
6360         
6361         return this;
6362     },
6363
6364     /**
6365      * Set values for fields in this form in bulk.
6366      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
6367      * @return {BasicForm} this
6368      */
6369     setValues : function(values){
6370         if(values instanceof Array){ // array of objects
6371             for(var i = 0, len = values.length; i < len; i++){
6372                 var v = values[i];
6373                 var f = this.findField(v.id);
6374                 if(f){
6375                     f.setValue(v.value);
6376                     if(this.trackResetOnLoad){
6377                         f.originalValue = f.getValue();
6378                     }
6379                 }
6380             }
6381         }else{ // object hash
6382             var field, id;
6383             for(id in values){
6384                 if(typeof values[id] != 'function' && (field = this.findField(id))){
6385                     
6386                     if (field.setFromData && 
6387                         field.valueField && 
6388                         field.displayField &&
6389                         // combos' with local stores can 
6390                         // be queried via setValue()
6391                         // to set their value..
6392                         (field.store && !field.store.isLocal)
6393                         ) {
6394                         // it's a combo
6395                         var sd = { };
6396                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
6397                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
6398                         field.setFromData(sd);
6399                         
6400                     } else {
6401                         field.setValue(values[id]);
6402                     }
6403                     
6404                     
6405                     if(this.trackResetOnLoad){
6406                         field.originalValue = field.getValue();
6407                     }
6408                 }
6409             }
6410         }
6411          
6412         //Roo.each(this.childForms || [], function (f) {
6413         //    f.setValues(values);
6414         //});
6415                 
6416         return this;
6417     },
6418
6419     /**
6420      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
6421      * they are returned as an array.
6422      * @param {Boolean} asString
6423      * @return {Object}
6424      */
6425     getValues : function(asString){
6426         //if (this.childForms) {
6427             // copy values from the child forms
6428         //    Roo.each(this.childForms, function (f) {
6429         //        this.setValues(f.getValues());
6430         //    }, this);
6431         //}
6432         
6433         
6434         
6435         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
6436         if(asString === true){
6437             return fs;
6438         }
6439         return Roo.urlDecode(fs);
6440     },
6441     
6442     /**
6443      * Returns the fields in this form as an object with key/value pairs. 
6444      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
6445      * @return {Object}
6446      */
6447     getFieldValues : function(with_hidden)
6448     {
6449         var items = this.getItems();
6450         var ret = {};
6451         items.each(function(f){
6452             if (!f.getName()) {
6453                 return;
6454             }
6455             var v = f.getValue();
6456             if (f.inputType =='radio') {
6457                 if (typeof(ret[f.getName()]) == 'undefined') {
6458                     ret[f.getName()] = ''; // empty..
6459                 }
6460                 
6461                 if (!f.el.dom.checked) {
6462                     return;
6463                     
6464                 }
6465                 v = f.el.dom.value;
6466                 
6467             }
6468             
6469             // not sure if this supported any more..
6470             if ((typeof(v) == 'object') && f.getRawValue) {
6471                 v = f.getRawValue() ; // dates..
6472             }
6473             // combo boxes where name != hiddenName...
6474             if (f.name != f.getName()) {
6475                 ret[f.name] = f.getRawValue();
6476             }
6477             ret[f.getName()] = v;
6478         });
6479         
6480         return ret;
6481     },
6482
6483     /**
6484      * Clears all invalid messages in this form.
6485      * @return {BasicForm} this
6486      */
6487     clearInvalid : function(){
6488         var items = this.getItems();
6489         
6490         items.each(function(f){
6491            f.clearInvalid();
6492         });
6493         
6494         
6495         
6496         return this;
6497     },
6498
6499     /**
6500      * Resets this form.
6501      * @return {BasicForm} this
6502      */
6503     reset : function(){
6504         var items = this.getItems();
6505         items.each(function(f){
6506             f.reset();
6507         });
6508         
6509         Roo.each(this.childForms || [], function (f) {
6510             f.reset();
6511         });
6512        
6513         
6514         return this;
6515     },
6516     getItems : function()
6517     {
6518         var r=new Roo.util.MixedCollection(false, function(o){
6519             return o.id || (o.id = Roo.id());
6520         });
6521         var iter = function(el) {
6522             if (el.inputEl) {
6523                 r.add(el);
6524             }
6525             if (!el.items) {
6526                 return;
6527             }
6528             Roo.each(el.items,function(e) {
6529                 iter(e);
6530             });
6531             
6532             
6533         };
6534         iter(this);
6535         return r;
6536         
6537         
6538         
6539         
6540     }
6541     
6542 });
6543
6544  
6545 /*
6546  * Based on:
6547  * Ext JS Library 1.1.1
6548  * Copyright(c) 2006-2007, Ext JS, LLC.
6549  *
6550  * Originally Released Under LGPL - original licence link has changed is not relivant.
6551  *
6552  * Fork - LGPL
6553  * <script type="text/javascript">
6554  */
6555 /**
6556  * @class Roo.form.VTypes
6557  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
6558  * @singleton
6559  */
6560 Roo.form.VTypes = function(){
6561     // closure these in so they are only created once.
6562     var alpha = /^[a-zA-Z_]+$/;
6563     var alphanum = /^[a-zA-Z0-9_]+$/;
6564     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
6565     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
6566
6567     // All these messages and functions are configurable
6568     return {
6569         /**
6570          * The function used to validate email addresses
6571          * @param {String} value The email address
6572          */
6573         'email' : function(v){
6574             return email.test(v);
6575         },
6576         /**
6577          * The error text to display when the email validation function returns false
6578          * @type String
6579          */
6580         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
6581         /**
6582          * The keystroke filter mask to be applied on email input
6583          * @type RegExp
6584          */
6585         'emailMask' : /[a-z0-9_\.\-@]/i,
6586
6587         /**
6588          * The function used to validate URLs
6589          * @param {String} value The URL
6590          */
6591         'url' : function(v){
6592             return url.test(v);
6593         },
6594         /**
6595          * The error text to display when the url validation function returns false
6596          * @type String
6597          */
6598         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
6599         
6600         /**
6601          * The function used to validate alpha values
6602          * @param {String} value The value
6603          */
6604         'alpha' : function(v){
6605             return alpha.test(v);
6606         },
6607         /**
6608          * The error text to display when the alpha validation function returns false
6609          * @type String
6610          */
6611         'alphaText' : 'This field should only contain letters and _',
6612         /**
6613          * The keystroke filter mask to be applied on alpha input
6614          * @type RegExp
6615          */
6616         'alphaMask' : /[a-z_]/i,
6617
6618         /**
6619          * The function used to validate alphanumeric values
6620          * @param {String} value The value
6621          */
6622         'alphanum' : function(v){
6623             return alphanum.test(v);
6624         },
6625         /**
6626          * The error text to display when the alphanumeric validation function returns false
6627          * @type String
6628          */
6629         'alphanumText' : 'This field should only contain letters, numbers and _',
6630         /**
6631          * The keystroke filter mask to be applied on alphanumeric input
6632          * @type RegExp
6633          */
6634         'alphanumMask' : /[a-z0-9_]/i
6635     };
6636 }();/*
6637  * - LGPL
6638  *
6639  * Input
6640  * 
6641  */
6642
6643 /**
6644  * @class Roo.bootstrap.Input
6645  * @extends Roo.bootstrap.Component
6646  * Bootstrap Input class
6647  * @cfg {Boolean} disabled is it disabled
6648  * @cfg {String} fieldLabel - the label associated
6649  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
6650  * @cfg {String} name name of the input
6651  * @cfg {string} fieldLabel - the label associated
6652  * @cfg {string}  inputType - input / file submit ...
6653  * @cfg {string} placeholder - placeholder to put in text.
6654  * @cfg {string}  before - input group add on before
6655  * @cfg {string} after - input group add on after
6656  * @cfg {string} size - (lg|sm) or leave empty..
6657  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
6658  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
6659  * @cfg {Number} md colspan out of 12 for computer-sized screens
6660  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
6661  * @cfg {string} value default value of the input
6662  * @cfg {Number} labelWidth set the width of label (0-12)
6663  * @cfg {String} labelAlign (top|left)
6664  * @cfg {Boolean} readOnly Specifies that the field should be read-only
6665  * @cfg {String} align (left|center|right) Default left
6666  * 
6667  * 
6668  * @constructor
6669  * Create a new Input
6670  * @param {Object} config The config object
6671  */
6672
6673 Roo.bootstrap.Input = function(config){
6674     Roo.bootstrap.Input.superclass.constructor.call(this, config);
6675    
6676         this.addEvents({
6677             /**
6678              * @event focus
6679              * Fires when this field receives input focus.
6680              * @param {Roo.form.Field} this
6681              */
6682             focus : true,
6683             /**
6684              * @event blur
6685              * Fires when this field loses input focus.
6686              * @param {Roo.form.Field} this
6687              */
6688             blur : true,
6689             /**
6690              * @event specialkey
6691              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
6692              * {@link Roo.EventObject#getKey} to determine which key was pressed.
6693              * @param {Roo.form.Field} this
6694              * @param {Roo.EventObject} e The event object
6695              */
6696             specialkey : true,
6697             /**
6698              * @event change
6699              * Fires just before the field blurs if the field value has changed.
6700              * @param {Roo.form.Field} this
6701              * @param {Mixed} newValue The new value
6702              * @param {Mixed} oldValue The original value
6703              */
6704             change : true,
6705             /**
6706              * @event invalid
6707              * Fires after the field has been marked as invalid.
6708              * @param {Roo.form.Field} this
6709              * @param {String} msg The validation message
6710              */
6711             invalid : true,
6712             /**
6713              * @event valid
6714              * Fires after the field has been validated with no errors.
6715              * @param {Roo.form.Field} this
6716              */
6717             valid : true,
6718              /**
6719              * @event keyup
6720              * Fires after the key up
6721              * @param {Roo.form.Field} this
6722              * @param {Roo.EventObject}  e The event Object
6723              */
6724             keyup : true
6725         });
6726 };
6727
6728 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
6729      /**
6730      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
6731       automatic validation (defaults to "keyup").
6732      */
6733     validationEvent : "keyup",
6734      /**
6735      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
6736      */
6737     validateOnBlur : true,
6738     /**
6739      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
6740      */
6741     validationDelay : 250,
6742      /**
6743      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
6744      */
6745     focusClass : "x-form-focus",  // not needed???
6746     
6747        
6748     /**
6749      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
6750      */
6751     invalidClass : "has-error",
6752     
6753     /**
6754      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
6755      */
6756     selectOnFocus : false,
6757     
6758      /**
6759      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
6760      */
6761     maskRe : null,
6762        /**
6763      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
6764      */
6765     vtype : null,
6766     
6767       /**
6768      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
6769      */
6770     disableKeyFilter : false,
6771     
6772        /**
6773      * @cfg {Boolean} disabled True to disable the field (defaults to false).
6774      */
6775     disabled : false,
6776      /**
6777      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
6778      */
6779     allowBlank : true,
6780     /**
6781      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
6782      */
6783     blankText : "This field is required",
6784     
6785      /**
6786      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
6787      */
6788     minLength : 0,
6789     /**
6790      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
6791      */
6792     maxLength : Number.MAX_VALUE,
6793     /**
6794      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
6795      */
6796     minLengthText : "The minimum length for this field is {0}",
6797     /**
6798      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
6799      */
6800     maxLengthText : "The maximum length for this field is {0}",
6801   
6802     
6803     /**
6804      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
6805      * If available, this function will be called only after the basic validators all return true, and will be passed the
6806      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
6807      */
6808     validator : null,
6809     /**
6810      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
6811      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
6812      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
6813      */
6814     regex : null,
6815     /**
6816      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
6817      */
6818     regexText : "",
6819     
6820     
6821     
6822     fieldLabel : '',
6823     inputType : 'text',
6824     
6825     name : false,
6826     placeholder: false,
6827     before : false,
6828     after : false,
6829     size : false,
6830     // private
6831     hasFocus : false,
6832     preventMark: false,
6833     isFormField : true,
6834     value : '',
6835     labelWidth : 2,
6836     labelAlign : false,
6837     readOnly : false,
6838     align : false,
6839     formatedValue : false,
6840     
6841     parentLabelAlign : function()
6842     {
6843         var parent = this;
6844         while (parent.parent()) {
6845             parent = parent.parent();
6846             if (typeof(parent.labelAlign) !='undefined') {
6847                 return parent.labelAlign;
6848             }
6849         }
6850         return 'left';
6851         
6852     },
6853     
6854     getAutoCreate : function(){
6855         
6856         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
6857         
6858         var id = Roo.id();
6859         
6860         var cfg = {};
6861         
6862         if(this.inputType != 'hidden'){
6863             cfg.cls = 'form-group' //input-group
6864         }
6865         
6866         var input =  {
6867             tag: 'input',
6868             id : id,
6869             type : this.inputType,
6870             value : this.value,
6871             cls : 'form-control',
6872             placeholder : this.placeholder || ''
6873             
6874         };
6875         
6876         if(this.align){
6877             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
6878         }
6879         
6880         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
6881             input.maxLength = this.maxLength;
6882         }
6883         
6884         if (this.disabled) {
6885             input.disabled=true;
6886         }
6887         
6888         if (this.readOnly) {
6889             input.readonly=true;
6890         }
6891         
6892         if (this.name) {
6893             input.name = this.name;
6894         }
6895         if (this.size) {
6896             input.cls += ' input-' + this.size;
6897         }
6898         var settings=this;
6899         ['xs','sm','md','lg'].map(function(size){
6900             if (settings[size]) {
6901                 cfg.cls += ' col-' + size + '-' + settings[size];
6902             }
6903         });
6904         
6905         var inputblock = input;
6906         
6907         if (this.before || this.after) {
6908             
6909             inputblock = {
6910                 cls : 'input-group',
6911                 cn :  [] 
6912             };
6913             if (this.before && typeof(this.before) == 'string') {
6914                 
6915                 inputblock.cn.push({
6916                     tag :'span',
6917                     cls : 'roo-input-before input-group-addon',
6918                     html : this.before
6919                 });
6920             }
6921             if (this.before && typeof(this.before) == 'object') {
6922                 this.before = Roo.factory(this.before);
6923                 Roo.log(this.before);
6924                 inputblock.cn.push({
6925                     tag :'span',
6926                     cls : 'roo-input-before input-group-' +
6927                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
6928                 });
6929             }
6930             
6931             inputblock.cn.push(input);
6932             
6933             if (this.after && typeof(this.after) == 'string') {
6934                 inputblock.cn.push({
6935                     tag :'span',
6936                     cls : 'roo-input-after input-group-addon',
6937                     html : this.after
6938                 });
6939             }
6940             if (this.after && typeof(this.after) == 'object') {
6941                 this.after = Roo.factory(this.after);
6942                 Roo.log(this.after);
6943                 inputblock.cn.push({
6944                     tag :'span',
6945                     cls : 'roo-input-after input-group-' +
6946                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
6947                 });
6948             }
6949         };
6950         
6951         if (align ==='left' && this.fieldLabel.length) {
6952                 Roo.log("left and has label");
6953                 cfg.cn = [
6954                     
6955                     {
6956                         tag: 'label',
6957                         'for' :  id,
6958                         cls : 'control-label col-sm-' + this.labelWidth,
6959                         html : this.fieldLabel
6960                         
6961                     },
6962                     {
6963                         cls : "col-sm-" + (12 - this.labelWidth), 
6964                         cn: [
6965                             inputblock
6966                         ]
6967                     }
6968                     
6969                 ];
6970         } else if ( this.fieldLabel.length) {
6971                 Roo.log(" label");
6972                  cfg.cn = [
6973                    
6974                     {
6975                         tag: 'label',
6976                         //cls : 'input-group-addon',
6977                         html : this.fieldLabel
6978                         
6979                     },
6980                     
6981                     inputblock
6982                     
6983                 ];
6984
6985         } else {
6986             
6987                 Roo.log(" no label && no align");
6988                 cfg.cn = [
6989                     
6990                         inputblock
6991                     
6992                 ];
6993                 
6994                 
6995         };
6996         Roo.log('input-parentType: ' + this.parentType);
6997         
6998         if (this.parentType === 'Navbar' &&  this.parent().bar) {
6999            cfg.cls += ' navbar-form';
7000            Roo.log(cfg);
7001         }
7002         
7003         return cfg;
7004         
7005     },
7006     /**
7007      * return the real input element.
7008      */
7009     inputEl: function ()
7010     {
7011         return this.el.select('input.form-control',true).first();
7012     },
7013     setDisabled : function(v)
7014     {
7015         var i  = this.inputEl().dom;
7016         if (!v) {
7017             i.removeAttribute('disabled');
7018             return;
7019             
7020         }
7021         i.setAttribute('disabled','true');
7022     },
7023     initEvents : function()
7024     {
7025         
7026         this.inputEl().on("keydown" , this.fireKey,  this);
7027         this.inputEl().on("focus", this.onFocus,  this);
7028         this.inputEl().on("blur", this.onBlur,  this);
7029         
7030         this.inputEl().relayEvent('keyup', this);
7031
7032         // reference to original value for reset
7033         this.originalValue = this.getValue();
7034         //Roo.form.TextField.superclass.initEvents.call(this);
7035         if(this.validationEvent == 'keyup'){
7036             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
7037             this.inputEl().on('keyup', this.filterValidation, this);
7038         }
7039         else if(this.validationEvent !== false){
7040             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
7041         }
7042         
7043         if(this.selectOnFocus){
7044             this.on("focus", this.preFocus, this);
7045             
7046         }
7047         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
7048             this.inputEl().on("keypress", this.filterKeys, this);
7049         }
7050        /* if(this.grow){
7051             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
7052             this.el.on("click", this.autoSize,  this);
7053         }
7054         */
7055         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
7056             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
7057         }
7058         
7059         if (typeof(this.before) == 'object') {
7060             this.before.render(this.el.select('.roo-input-before',true).first());
7061         }
7062         if (typeof(this.after) == 'object') {
7063             this.after.render(this.el.select('.roo-input-after',true).first());
7064         }
7065         
7066         
7067     },
7068     filterValidation : function(e){
7069         if(!e.isNavKeyPress()){
7070             this.validationTask.delay(this.validationDelay);
7071         }
7072     },
7073      /**
7074      * Validates the field value
7075      * @return {Boolean} True if the value is valid, else false
7076      */
7077     validate : function(){
7078         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
7079         if(this.disabled || this.validateValue(this.getRawValue())){
7080             this.clearInvalid();
7081             return true;
7082         }
7083         return false;
7084     },
7085     
7086     
7087     /**
7088      * Validates a value according to the field's validation rules and marks the field as invalid
7089      * if the validation fails
7090      * @param {Mixed} value The value to validate
7091      * @return {Boolean} True if the value is valid, else false
7092      */
7093     validateValue : function(value){
7094         if(value.length < 1)  { // if it's blank
7095              if(this.allowBlank){
7096                 this.clearInvalid();
7097                 return true;
7098              }else{
7099                 this.markInvalid(this.blankText);
7100                 return false;
7101              }
7102         }
7103         if(value.length < this.minLength){
7104             this.markInvalid(String.format(this.minLengthText, this.minLength));
7105             return false;
7106         }
7107         if(value.length > this.maxLength){
7108             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
7109             return false;
7110         }
7111         if(this.vtype){
7112             var vt = Roo.form.VTypes;
7113             if(!vt[this.vtype](value, this)){
7114                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
7115                 return false;
7116             }
7117         }
7118         if(typeof this.validator == "function"){
7119             var msg = this.validator(value);
7120             if(msg !== true){
7121                 this.markInvalid(msg);
7122                 return false;
7123             }
7124         }
7125         if(this.regex && !this.regex.test(value)){
7126             this.markInvalid(this.regexText);
7127             return false;
7128         }
7129         return true;
7130     },
7131
7132     
7133     
7134      // private
7135     fireKey : function(e){
7136         //Roo.log('field ' + e.getKey());
7137         if(e.isNavKeyPress()){
7138             this.fireEvent("specialkey", this, e);
7139         }
7140     },
7141     focus : function (selectText){
7142         if(this.rendered){
7143             this.inputEl().focus();
7144             if(selectText === true){
7145                 this.inputEl().dom.select();
7146             }
7147         }
7148         return this;
7149     } ,
7150     
7151     onFocus : function(){
7152         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7153            // this.el.addClass(this.focusClass);
7154         }
7155         if(!this.hasFocus){
7156             this.hasFocus = true;
7157             this.startValue = this.getValue();
7158             this.fireEvent("focus", this);
7159         }
7160     },
7161     
7162     beforeBlur : Roo.emptyFn,
7163
7164     
7165     // private
7166     onBlur : function(){
7167         this.beforeBlur();
7168         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7169             //this.el.removeClass(this.focusClass);
7170         }
7171         this.hasFocus = false;
7172         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
7173             this.validate();
7174         }
7175         var v = this.getValue();
7176         if(String(v) !== String(this.startValue)){
7177             this.fireEvent('change', this, v, this.startValue);
7178         }
7179         this.fireEvent("blur", this);
7180     },
7181     
7182     /**
7183      * Resets the current field value to the originally loaded value and clears any validation messages
7184      */
7185     reset : function(){
7186         this.setValue(this.originalValue);
7187         this.clearInvalid();
7188     },
7189      /**
7190      * Returns the name of the field
7191      * @return {Mixed} name The name field
7192      */
7193     getName: function(){
7194         return this.name;
7195     },
7196      /**
7197      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
7198      * @return {Mixed} value The field value
7199      */
7200     getValue : function(){
7201         
7202         var v = this.inputEl().getValue();
7203         
7204         return v;
7205     },
7206     /**
7207      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
7208      * @return {Mixed} value The field value
7209      */
7210     getRawValue : function(){
7211         var v = this.inputEl().getValue();
7212         
7213         return v;
7214     },
7215     
7216     /**
7217      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
7218      * @param {Mixed} value The value to set
7219      */
7220     setRawValue : function(v){
7221         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7222     },
7223     
7224     selectText : function(start, end){
7225         var v = this.getRawValue();
7226         if(v.length > 0){
7227             start = start === undefined ? 0 : start;
7228             end = end === undefined ? v.length : end;
7229             var d = this.inputEl().dom;
7230             if(d.setSelectionRange){
7231                 d.setSelectionRange(start, end);
7232             }else if(d.createTextRange){
7233                 var range = d.createTextRange();
7234                 range.moveStart("character", start);
7235                 range.moveEnd("character", v.length-end);
7236                 range.select();
7237             }
7238         }
7239     },
7240     
7241     /**
7242      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
7243      * @param {Mixed} value The value to set
7244      */
7245     setValue : function(v){
7246         this.value = v;
7247         if(this.rendered){
7248             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7249             this.validate();
7250         }
7251     },
7252     
7253     /*
7254     processValue : function(value){
7255         if(this.stripCharsRe){
7256             var newValue = value.replace(this.stripCharsRe, '');
7257             if(newValue !== value){
7258                 this.setRawValue(newValue);
7259                 return newValue;
7260             }
7261         }
7262         return value;
7263     },
7264   */
7265     preFocus : function(){
7266         
7267         if(this.selectOnFocus){
7268             this.inputEl().dom.select();
7269         }
7270     },
7271     filterKeys : function(e){
7272         var k = e.getKey();
7273         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
7274             return;
7275         }
7276         var c = e.getCharCode(), cc = String.fromCharCode(c);
7277         if(Roo.isIE && (e.isSpecialKey() || !cc)){
7278             return;
7279         }
7280         if(!this.maskRe.test(cc)){
7281             e.stopEvent();
7282         }
7283     },
7284      /**
7285      * Clear any invalid styles/messages for this field
7286      */
7287     clearInvalid : function(){
7288         
7289         if(!this.el || this.preventMark){ // not rendered
7290             return;
7291         }
7292         this.el.removeClass(this.invalidClass);
7293         /*
7294         switch(this.msgTarget){
7295             case 'qtip':
7296                 this.el.dom.qtip = '';
7297                 break;
7298             case 'title':
7299                 this.el.dom.title = '';
7300                 break;
7301             case 'under':
7302                 if(this.errorEl){
7303                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
7304                 }
7305                 break;
7306             case 'side':
7307                 if(this.errorIcon){
7308                     this.errorIcon.dom.qtip = '';
7309                     this.errorIcon.hide();
7310                     this.un('resize', this.alignErrorIcon, this);
7311                 }
7312                 break;
7313             default:
7314                 var t = Roo.getDom(this.msgTarget);
7315                 t.innerHTML = '';
7316                 t.style.display = 'none';
7317                 break;
7318         }
7319         */
7320         this.fireEvent('valid', this);
7321     },
7322      /**
7323      * Mark this field as invalid
7324      * @param {String} msg The validation message
7325      */
7326     markInvalid : function(msg){
7327         if(!this.el  || this.preventMark){ // not rendered
7328             return;
7329         }
7330         this.el.addClass(this.invalidClass);
7331         /*
7332         msg = msg || this.invalidText;
7333         switch(this.msgTarget){
7334             case 'qtip':
7335                 this.el.dom.qtip = msg;
7336                 this.el.dom.qclass = 'x-form-invalid-tip';
7337                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
7338                     Roo.QuickTips.enable();
7339                 }
7340                 break;
7341             case 'title':
7342                 this.el.dom.title = msg;
7343                 break;
7344             case 'under':
7345                 if(!this.errorEl){
7346                     var elp = this.el.findParent('.x-form-element', 5, true);
7347                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
7348                     this.errorEl.setWidth(elp.getWidth(true)-20);
7349                 }
7350                 this.errorEl.update(msg);
7351                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
7352                 break;
7353             case 'side':
7354                 if(!this.errorIcon){
7355                     var elp = this.el.findParent('.x-form-element', 5, true);
7356                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
7357                 }
7358                 this.alignErrorIcon();
7359                 this.errorIcon.dom.qtip = msg;
7360                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
7361                 this.errorIcon.show();
7362                 this.on('resize', this.alignErrorIcon, this);
7363                 break;
7364             default:
7365                 var t = Roo.getDom(this.msgTarget);
7366                 t.innerHTML = msg;
7367                 t.style.display = this.msgDisplay;
7368                 break;
7369         }
7370         */
7371         this.fireEvent('invalid', this, msg);
7372     },
7373     // private
7374     SafariOnKeyDown : function(event)
7375     {
7376         // this is a workaround for a password hang bug on chrome/ webkit.
7377         
7378         var isSelectAll = false;
7379         
7380         if(this.inputEl().dom.selectionEnd > 0){
7381             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
7382         }
7383         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
7384             event.preventDefault();
7385             this.setValue('');
7386             return;
7387         }
7388         
7389         if(isSelectAll){ // backspace and delete key
7390             
7391             event.preventDefault();
7392             // this is very hacky as keydown always get's upper case.
7393             //
7394             var cc = String.fromCharCode(event.getCharCode());
7395             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
7396             
7397         }
7398     },
7399     adjustWidth : function(tag, w){
7400         tag = tag.toLowerCase();
7401         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
7402             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
7403                 if(tag == 'input'){
7404                     return w + 2;
7405                 }
7406                 if(tag == 'textarea'){
7407                     return w-2;
7408                 }
7409             }else if(Roo.isOpera){
7410                 if(tag == 'input'){
7411                     return w + 2;
7412                 }
7413                 if(tag == 'textarea'){
7414                     return w-2;
7415                 }
7416             }
7417         }
7418         return w;
7419     }
7420     
7421 });
7422
7423  
7424 /*
7425  * - LGPL
7426  *
7427  * Input
7428  * 
7429  */
7430
7431 /**
7432  * @class Roo.bootstrap.TextArea
7433  * @extends Roo.bootstrap.Input
7434  * Bootstrap TextArea class
7435  * @cfg {Number} cols Specifies the visible width of a text area
7436  * @cfg {Number} rows Specifies the visible number of lines in a text area
7437  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
7438  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
7439  * @cfg {string} html text
7440  * 
7441  * @constructor
7442  * Create a new TextArea
7443  * @param {Object} config The config object
7444  */
7445
7446 Roo.bootstrap.TextArea = function(config){
7447     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
7448    
7449 };
7450
7451 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
7452      
7453     cols : false,
7454     rows : 5,
7455     readOnly : false,
7456     warp : 'soft',
7457     resize : false,
7458     value: false,
7459     html: false,
7460     
7461     getAutoCreate : function(){
7462         
7463         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7464         
7465         var id = Roo.id();
7466         
7467         var cfg = {};
7468         
7469         var input =  {
7470             tag: 'textarea',
7471             id : id,
7472             warp : this.warp,
7473             rows : this.rows,
7474             value : this.value || '',
7475             html: this.html || '',
7476             cls : 'form-control',
7477             placeholder : this.placeholder || '' 
7478             
7479         };
7480         
7481         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7482             input.maxLength = this.maxLength;
7483         }
7484         
7485         if(this.resize){
7486             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
7487         }
7488         
7489         if(this.cols){
7490             input.cols = this.cols;
7491         }
7492         
7493         if (this.readOnly) {
7494             input.readonly = true;
7495         }
7496         
7497         if (this.name) {
7498             input.name = this.name;
7499         }
7500         
7501         if (this.size) {
7502             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
7503         }
7504         
7505         var settings=this;
7506         ['xs','sm','md','lg'].map(function(size){
7507             if (settings[size]) {
7508                 cfg.cls += ' col-' + size + '-' + settings[size];
7509             }
7510         });
7511         
7512         var inputblock = input;
7513         
7514         if (this.before || this.after) {
7515             
7516             inputblock = {
7517                 cls : 'input-group',
7518                 cn :  [] 
7519             };
7520             if (this.before) {
7521                 inputblock.cn.push({
7522                     tag :'span',
7523                     cls : 'input-group-addon',
7524                     html : this.before
7525                 });
7526             }
7527             inputblock.cn.push(input);
7528             if (this.after) {
7529                 inputblock.cn.push({
7530                     tag :'span',
7531                     cls : 'input-group-addon',
7532                     html : this.after
7533                 });
7534             }
7535             
7536         }
7537         
7538         if (align ==='left' && this.fieldLabel.length) {
7539                 Roo.log("left and has label");
7540                 cfg.cn = [
7541                     
7542                     {
7543                         tag: 'label',
7544                         'for' :  id,
7545                         cls : 'control-label col-sm-' + this.labelWidth,
7546                         html : this.fieldLabel
7547                         
7548                     },
7549                     {
7550                         cls : "col-sm-" + (12 - this.labelWidth), 
7551                         cn: [
7552                             inputblock
7553                         ]
7554                     }
7555                     
7556                 ];
7557         } else if ( this.fieldLabel.length) {
7558                 Roo.log(" label");
7559                  cfg.cn = [
7560                    
7561                     {
7562                         tag: 'label',
7563                         //cls : 'input-group-addon',
7564                         html : this.fieldLabel
7565                         
7566                     },
7567                     
7568                     inputblock
7569                     
7570                 ];
7571
7572         } else {
7573             
7574                    Roo.log(" no label && no align");
7575                 cfg.cn = [
7576                     
7577                         inputblock
7578                     
7579                 ];
7580                 
7581                 
7582         }
7583         
7584         if (this.disabled) {
7585             input.disabled=true;
7586         }
7587         
7588         return cfg;
7589         
7590     },
7591     /**
7592      * return the real textarea element.
7593      */
7594     inputEl: function ()
7595     {
7596         return this.el.select('textarea.form-control',true).first();
7597     }
7598 });
7599
7600  
7601 /*
7602  * - LGPL
7603  *
7604  * trigger field - base class for combo..
7605  * 
7606  */
7607  
7608 /**
7609  * @class Roo.bootstrap.TriggerField
7610  * @extends Roo.bootstrap.Input
7611  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
7612  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
7613  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
7614  * for which you can provide a custom implementation.  For example:
7615  * <pre><code>
7616 var trigger = new Roo.bootstrap.TriggerField();
7617 trigger.onTriggerClick = myTriggerFn;
7618 trigger.applyTo('my-field');
7619 </code></pre>
7620  *
7621  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
7622  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
7623  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
7624  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
7625  * @constructor
7626  * Create a new TriggerField.
7627  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
7628  * to the base TextField)
7629  */
7630 Roo.bootstrap.TriggerField = function(config){
7631     this.mimicing = false;
7632     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
7633 };
7634
7635 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
7636     /**
7637      * @cfg {String} triggerClass A CSS class to apply to the trigger
7638      */
7639      /**
7640      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
7641      */
7642     hideTrigger:false,
7643
7644     /** @cfg {Boolean} grow @hide */
7645     /** @cfg {Number} growMin @hide */
7646     /** @cfg {Number} growMax @hide */
7647
7648     /**
7649      * @hide 
7650      * @method
7651      */
7652     autoSize: Roo.emptyFn,
7653     // private
7654     monitorTab : true,
7655     // private
7656     deferHeight : true,
7657
7658     
7659     actionMode : 'wrap',
7660     
7661     
7662     
7663     getAutoCreate : function(){
7664        
7665         var align = this.labelAlign || this.parentLabelAlign();
7666         
7667         var id = Roo.id();
7668         
7669         var cfg = {
7670             cls: 'form-group' //input-group
7671         };
7672         
7673         
7674         var input =  {
7675             tag: 'input',
7676             id : id,
7677             type : this.inputType,
7678             cls : 'form-control',
7679             autocomplete: 'off',
7680             placeholder : this.placeholder || '' 
7681             
7682         };
7683         if (this.name) {
7684             input.name = this.name;
7685         }
7686         if (this.size) {
7687             input.cls += ' input-' + this.size;
7688         }
7689         
7690         if (this.disabled) {
7691             input.disabled=true;
7692         }
7693         
7694         var inputblock = input;
7695         
7696         if (this.before || this.after) {
7697             
7698             inputblock = {
7699                 cls : 'input-group',
7700                 cn :  [] 
7701             };
7702             if (this.before) {
7703                 inputblock.cn.push({
7704                     tag :'span',
7705                     cls : 'input-group-addon',
7706                     html : this.before
7707                 });
7708             }
7709             inputblock.cn.push(input);
7710             if (this.after) {
7711                 inputblock.cn.push({
7712                     tag :'span',
7713                     cls : 'input-group-addon',
7714                     html : this.after
7715                 });
7716             }
7717             
7718         };
7719         
7720         var box = {
7721             tag: 'div',
7722             cn: [
7723                 {
7724                     tag: 'input',
7725                     type : 'hidden',
7726                     cls: 'form-hidden-field'
7727                 },
7728                 inputblock
7729             ]
7730             
7731         };
7732         
7733         if(this.multiple){
7734             Roo.log('multiple');
7735             
7736             box = {
7737                 tag: 'div',
7738                 cn: [
7739                     {
7740                         tag: 'input',
7741                         type : 'hidden',
7742                         cls: 'form-hidden-field'
7743                     },
7744                     {
7745                         tag: 'ul',
7746                         cls: 'select2-choices',
7747                         cn:[
7748                             {
7749                                 tag: 'li',
7750                                 cls: 'select2-search-field',
7751                                 cn: [
7752
7753                                     inputblock
7754                                 ]
7755                             }
7756                         ]
7757                     }
7758                 ]
7759             }
7760         };
7761         
7762         var combobox = {
7763             cls: 'select2-container input-group',
7764             cn: [
7765                 box
7766 //                {
7767 //                    tag: 'ul',
7768 //                    cls: 'typeahead typeahead-long dropdown-menu',
7769 //                    style: 'display:none'
7770 //                }
7771             ]
7772         };
7773         
7774         if(!this.multiple){
7775             combobox.cn.push({
7776                 tag :'span',
7777                 cls : 'input-group-addon btn dropdown-toggle',
7778                 cn : [
7779                     {
7780                         tag: 'span',
7781                         cls: 'caret'
7782                     },
7783                     {
7784                         tag: 'span',
7785                         cls: 'combobox-clear',
7786                         cn  : [
7787                             {
7788                                 tag : 'i',
7789                                 cls: 'icon-remove'
7790                             }
7791                         ]
7792                     }
7793                 ]
7794
7795             })
7796         }
7797         
7798         if(this.multiple){
7799             combobox.cls += ' select2-container-multi';
7800         }
7801         
7802         if (align ==='left' && this.fieldLabel.length) {
7803             
7804                 Roo.log("left and has label");
7805                 cfg.cn = [
7806                     
7807                     {
7808                         tag: 'label',
7809                         'for' :  id,
7810                         cls : 'control-label col-sm-' + this.labelWidth,
7811                         html : this.fieldLabel
7812                         
7813                     },
7814                     {
7815                         cls : "col-sm-" + (12 - this.labelWidth), 
7816                         cn: [
7817                             combobox
7818                         ]
7819                     }
7820                     
7821                 ];
7822         } else if ( this.fieldLabel.length) {
7823                 Roo.log(" label");
7824                  cfg.cn = [
7825                    
7826                     {
7827                         tag: 'label',
7828                         //cls : 'input-group-addon',
7829                         html : this.fieldLabel
7830                         
7831                     },
7832                     
7833                     combobox
7834                     
7835                 ];
7836
7837         } else {
7838             
7839                 Roo.log(" no label && no align");
7840                 cfg = combobox
7841                      
7842                 
7843         }
7844          
7845         var settings=this;
7846         ['xs','sm','md','lg'].map(function(size){
7847             if (settings[size]) {
7848                 cfg.cls += ' col-' + size + '-' + settings[size];
7849             }
7850         });
7851         
7852         return cfg;
7853         
7854     },
7855     
7856     
7857     
7858     // private
7859     onResize : function(w, h){
7860 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
7861 //        if(typeof w == 'number'){
7862 //            var x = w - this.trigger.getWidth();
7863 //            this.inputEl().setWidth(this.adjustWidth('input', x));
7864 //            this.trigger.setStyle('left', x+'px');
7865 //        }
7866     },
7867
7868     // private
7869     adjustSize : Roo.BoxComponent.prototype.adjustSize,
7870
7871     // private
7872     getResizeEl : function(){
7873         return this.inputEl();
7874     },
7875
7876     // private
7877     getPositionEl : function(){
7878         return this.inputEl();
7879     },
7880
7881     // private
7882     alignErrorIcon : function(){
7883         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
7884     },
7885
7886     // private
7887     initEvents : function(){
7888         
7889         this.createList();
7890         
7891         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
7892         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
7893         if(!this.multiple){
7894             this.trigger = this.el.select('span.dropdown-toggle',true).first();
7895             if(this.hideTrigger){
7896                 this.trigger.setDisplayed(false);
7897             }
7898             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
7899         }
7900         
7901         if(this.multiple){
7902             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
7903         }
7904         
7905         //this.trigger.addClassOnOver('x-form-trigger-over');
7906         //this.trigger.addClassOnClick('x-form-trigger-click');
7907         
7908         //if(!this.width){
7909         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
7910         //}
7911     },
7912     
7913     createList : function()
7914     {
7915         this.list = Roo.get(document.body).createChild({
7916             tag: 'ul',
7917             cls: 'typeahead typeahead-long dropdown-menu',
7918             style: 'display:none'
7919         });
7920     },
7921
7922     // private
7923     initTrigger : function(){
7924        
7925     },
7926
7927     // private
7928     onDestroy : function(){
7929         if(this.trigger){
7930             this.trigger.removeAllListeners();
7931           //  this.trigger.remove();
7932         }
7933         //if(this.wrap){
7934         //    this.wrap.remove();
7935         //}
7936         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
7937     },
7938
7939     // private
7940     onFocus : function(){
7941         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
7942         /*
7943         if(!this.mimicing){
7944             this.wrap.addClass('x-trigger-wrap-focus');
7945             this.mimicing = true;
7946             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
7947             if(this.monitorTab){
7948                 this.el.on("keydown", this.checkTab, this);
7949             }
7950         }
7951         */
7952     },
7953
7954     // private
7955     checkTab : function(e){
7956         if(e.getKey() == e.TAB){
7957             this.triggerBlur();
7958         }
7959     },
7960
7961     // private
7962     onBlur : function(){
7963         // do nothing
7964     },
7965
7966     // private
7967     mimicBlur : function(e, t){
7968         /*
7969         if(!this.wrap.contains(t) && this.validateBlur()){
7970             this.triggerBlur();
7971         }
7972         */
7973     },
7974
7975     // private
7976     triggerBlur : function(){
7977         this.mimicing = false;
7978         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
7979         if(this.monitorTab){
7980             this.el.un("keydown", this.checkTab, this);
7981         }
7982         //this.wrap.removeClass('x-trigger-wrap-focus');
7983         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
7984     },
7985
7986     // private
7987     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
7988     validateBlur : function(e, t){
7989         return true;
7990     },
7991
7992     // private
7993     onDisable : function(){
7994         this.inputEl().dom.disabled = true;
7995         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
7996         //if(this.wrap){
7997         //    this.wrap.addClass('x-item-disabled');
7998         //}
7999     },
8000
8001     // private
8002     onEnable : function(){
8003         this.inputEl().dom.disabled = false;
8004         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
8005         //if(this.wrap){
8006         //    this.el.removeClass('x-item-disabled');
8007         //}
8008     },
8009
8010     // private
8011     onShow : function(){
8012         var ae = this.getActionEl();
8013         
8014         if(ae){
8015             ae.dom.style.display = '';
8016             ae.dom.style.visibility = 'visible';
8017         }
8018     },
8019
8020     // private
8021     
8022     onHide : function(){
8023         var ae = this.getActionEl();
8024         ae.dom.style.display = 'none';
8025     },
8026
8027     /**
8028      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
8029      * by an implementing function.
8030      * @method
8031      * @param {EventObject} e
8032      */
8033     onTriggerClick : Roo.emptyFn
8034 });
8035  /*
8036  * Based on:
8037  * Ext JS Library 1.1.1
8038  * Copyright(c) 2006-2007, Ext JS, LLC.
8039  *
8040  * Originally Released Under LGPL - original licence link has changed is not relivant.
8041  *
8042  * Fork - LGPL
8043  * <script type="text/javascript">
8044  */
8045
8046
8047 /**
8048  * @class Roo.data.SortTypes
8049  * @singleton
8050  * Defines the default sorting (casting?) comparison functions used when sorting data.
8051  */
8052 Roo.data.SortTypes = {
8053     /**
8054      * Default sort that does nothing
8055      * @param {Mixed} s The value being converted
8056      * @return {Mixed} The comparison value
8057      */
8058     none : function(s){
8059         return s;
8060     },
8061     
8062     /**
8063      * The regular expression used to strip tags
8064      * @type {RegExp}
8065      * @property
8066      */
8067     stripTagsRE : /<\/?[^>]+>/gi,
8068     
8069     /**
8070      * Strips all HTML tags to sort on text only
8071      * @param {Mixed} s The value being converted
8072      * @return {String} The comparison value
8073      */
8074     asText : function(s){
8075         return String(s).replace(this.stripTagsRE, "");
8076     },
8077     
8078     /**
8079      * Strips all HTML tags to sort on text only - Case insensitive
8080      * @param {Mixed} s The value being converted
8081      * @return {String} The comparison value
8082      */
8083     asUCText : function(s){
8084         return String(s).toUpperCase().replace(this.stripTagsRE, "");
8085     },
8086     
8087     /**
8088      * Case insensitive string
8089      * @param {Mixed} s The value being converted
8090      * @return {String} The comparison value
8091      */
8092     asUCString : function(s) {
8093         return String(s).toUpperCase();
8094     },
8095     
8096     /**
8097      * Date sorting
8098      * @param {Mixed} s The value being converted
8099      * @return {Number} The comparison value
8100      */
8101     asDate : function(s) {
8102         if(!s){
8103             return 0;
8104         }
8105         if(s instanceof Date){
8106             return s.getTime();
8107         }
8108         return Date.parse(String(s));
8109     },
8110     
8111     /**
8112      * Float sorting
8113      * @param {Mixed} s The value being converted
8114      * @return {Float} The comparison value
8115      */
8116     asFloat : function(s) {
8117         var val = parseFloat(String(s).replace(/,/g, ""));
8118         if(isNaN(val)) val = 0;
8119         return val;
8120     },
8121     
8122     /**
8123      * Integer sorting
8124      * @param {Mixed} s The value being converted
8125      * @return {Number} The comparison value
8126      */
8127     asInt : function(s) {
8128         var val = parseInt(String(s).replace(/,/g, ""));
8129         if(isNaN(val)) val = 0;
8130         return val;
8131     }
8132 };/*
8133  * Based on:
8134  * Ext JS Library 1.1.1
8135  * Copyright(c) 2006-2007, Ext JS, LLC.
8136  *
8137  * Originally Released Under LGPL - original licence link has changed is not relivant.
8138  *
8139  * Fork - LGPL
8140  * <script type="text/javascript">
8141  */
8142
8143 /**
8144 * @class Roo.data.Record
8145  * Instances of this class encapsulate both record <em>definition</em> information, and record
8146  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
8147  * to access Records cached in an {@link Roo.data.Store} object.<br>
8148  * <p>
8149  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
8150  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
8151  * objects.<br>
8152  * <p>
8153  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
8154  * @constructor
8155  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
8156  * {@link #create}. The parameters are the same.
8157  * @param {Array} data An associative Array of data values keyed by the field name.
8158  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
8159  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
8160  * not specified an integer id is generated.
8161  */
8162 Roo.data.Record = function(data, id){
8163     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
8164     this.data = data;
8165 };
8166
8167 /**
8168  * Generate a constructor for a specific record layout.
8169  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
8170  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
8171  * Each field definition object may contain the following properties: <ul>
8172  * <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,
8173  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
8174  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
8175  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
8176  * is being used, then this is a string containing the javascript expression to reference the data relative to 
8177  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
8178  * to the data item relative to the record element. If the mapping expression is the same as the field name,
8179  * this may be omitted.</p></li>
8180  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
8181  * <ul><li>auto (Default, implies no conversion)</li>
8182  * <li>string</li>
8183  * <li>int</li>
8184  * <li>float</li>
8185  * <li>boolean</li>
8186  * <li>date</li></ul></p></li>
8187  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
8188  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
8189  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
8190  * by the Reader into an object that will be stored in the Record. It is passed the
8191  * following parameters:<ul>
8192  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
8193  * </ul></p></li>
8194  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
8195  * </ul>
8196  * <br>usage:<br><pre><code>
8197 var TopicRecord = Roo.data.Record.create(
8198     {name: 'title', mapping: 'topic_title'},
8199     {name: 'author', mapping: 'username'},
8200     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
8201     {name: 'lastPost', mapping: 'post_time', type: 'date'},
8202     {name: 'lastPoster', mapping: 'user2'},
8203     {name: 'excerpt', mapping: 'post_text'}
8204 );
8205
8206 var myNewRecord = new TopicRecord({
8207     title: 'Do my job please',
8208     author: 'noobie',
8209     totalPosts: 1,
8210     lastPost: new Date(),
8211     lastPoster: 'Animal',
8212     excerpt: 'No way dude!'
8213 });
8214 myStore.add(myNewRecord);
8215 </code></pre>
8216  * @method create
8217  * @static
8218  */
8219 Roo.data.Record.create = function(o){
8220     var f = function(){
8221         f.superclass.constructor.apply(this, arguments);
8222     };
8223     Roo.extend(f, Roo.data.Record);
8224     var p = f.prototype;
8225     p.fields = new Roo.util.MixedCollection(false, function(field){
8226         return field.name;
8227     });
8228     for(var i = 0, len = o.length; i < len; i++){
8229         p.fields.add(new Roo.data.Field(o[i]));
8230     }
8231     f.getField = function(name){
8232         return p.fields.get(name);  
8233     };
8234     return f;
8235 };
8236
8237 Roo.data.Record.AUTO_ID = 1000;
8238 Roo.data.Record.EDIT = 'edit';
8239 Roo.data.Record.REJECT = 'reject';
8240 Roo.data.Record.COMMIT = 'commit';
8241
8242 Roo.data.Record.prototype = {
8243     /**
8244      * Readonly flag - true if this record has been modified.
8245      * @type Boolean
8246      */
8247     dirty : false,
8248     editing : false,
8249     error: null,
8250     modified: null,
8251
8252     // private
8253     join : function(store){
8254         this.store = store;
8255     },
8256
8257     /**
8258      * Set the named field to the specified value.
8259      * @param {String} name The name of the field to set.
8260      * @param {Object} value The value to set the field to.
8261      */
8262     set : function(name, value){
8263         if(this.data[name] == value){
8264             return;
8265         }
8266         this.dirty = true;
8267         if(!this.modified){
8268             this.modified = {};
8269         }
8270         if(typeof this.modified[name] == 'undefined'){
8271             this.modified[name] = this.data[name];
8272         }
8273         this.data[name] = value;
8274         if(!this.editing && this.store){
8275             this.store.afterEdit(this);
8276         }       
8277     },
8278
8279     /**
8280      * Get the value of the named field.
8281      * @param {String} name The name of the field to get the value of.
8282      * @return {Object} The value of the field.
8283      */
8284     get : function(name){
8285         return this.data[name]; 
8286     },
8287
8288     // private
8289     beginEdit : function(){
8290         this.editing = true;
8291         this.modified = {}; 
8292     },
8293
8294     // private
8295     cancelEdit : function(){
8296         this.editing = false;
8297         delete this.modified;
8298     },
8299
8300     // private
8301     endEdit : function(){
8302         this.editing = false;
8303         if(this.dirty && this.store){
8304             this.store.afterEdit(this);
8305         }
8306     },
8307
8308     /**
8309      * Usually called by the {@link Roo.data.Store} which owns the Record.
8310      * Rejects all changes made to the Record since either creation, or the last commit operation.
8311      * Modified fields are reverted to their original values.
8312      * <p>
8313      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8314      * of reject operations.
8315      */
8316     reject : function(){
8317         var m = this.modified;
8318         for(var n in m){
8319             if(typeof m[n] != "function"){
8320                 this.data[n] = m[n];
8321             }
8322         }
8323         this.dirty = false;
8324         delete this.modified;
8325         this.editing = false;
8326         if(this.store){
8327             this.store.afterReject(this);
8328         }
8329     },
8330
8331     /**
8332      * Usually called by the {@link Roo.data.Store} which owns the Record.
8333      * Commits all changes made to the Record since either creation, or the last commit operation.
8334      * <p>
8335      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8336      * of commit operations.
8337      */
8338     commit : function(){
8339         this.dirty = false;
8340         delete this.modified;
8341         this.editing = false;
8342         if(this.store){
8343             this.store.afterCommit(this);
8344         }
8345     },
8346
8347     // private
8348     hasError : function(){
8349         return this.error != null;
8350     },
8351
8352     // private
8353     clearError : function(){
8354         this.error = null;
8355     },
8356
8357     /**
8358      * Creates a copy of this record.
8359      * @param {String} id (optional) A new record id if you don't want to use this record's id
8360      * @return {Record}
8361      */
8362     copy : function(newId) {
8363         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
8364     }
8365 };/*
8366  * Based on:
8367  * Ext JS Library 1.1.1
8368  * Copyright(c) 2006-2007, Ext JS, LLC.
8369  *
8370  * Originally Released Under LGPL - original licence link has changed is not relivant.
8371  *
8372  * Fork - LGPL
8373  * <script type="text/javascript">
8374  */
8375
8376
8377
8378 /**
8379  * @class Roo.data.Store
8380  * @extends Roo.util.Observable
8381  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
8382  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
8383  * <p>
8384  * 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
8385  * has no knowledge of the format of the data returned by the Proxy.<br>
8386  * <p>
8387  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
8388  * instances from the data object. These records are cached and made available through accessor functions.
8389  * @constructor
8390  * Creates a new Store.
8391  * @param {Object} config A config object containing the objects needed for the Store to access data,
8392  * and read the data into Records.
8393  */
8394 Roo.data.Store = function(config){
8395     this.data = new Roo.util.MixedCollection(false);
8396     this.data.getKey = function(o){
8397         return o.id;
8398     };
8399     this.baseParams = {};
8400     // private
8401     this.paramNames = {
8402         "start" : "start",
8403         "limit" : "limit",
8404         "sort" : "sort",
8405         "dir" : "dir",
8406         "multisort" : "_multisort"
8407     };
8408
8409     if(config && config.data){
8410         this.inlineData = config.data;
8411         delete config.data;
8412     }
8413
8414     Roo.apply(this, config);
8415     
8416     if(this.reader){ // reader passed
8417         this.reader = Roo.factory(this.reader, Roo.data);
8418         this.reader.xmodule = this.xmodule || false;
8419         if(!this.recordType){
8420             this.recordType = this.reader.recordType;
8421         }
8422         if(this.reader.onMetaChange){
8423             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
8424         }
8425     }
8426
8427     if(this.recordType){
8428         this.fields = this.recordType.prototype.fields;
8429     }
8430     this.modified = [];
8431
8432     this.addEvents({
8433         /**
8434          * @event datachanged
8435          * Fires when the data cache has changed, and a widget which is using this Store
8436          * as a Record cache should refresh its view.
8437          * @param {Store} this
8438          */
8439         datachanged : true,
8440         /**
8441          * @event metachange
8442          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
8443          * @param {Store} this
8444          * @param {Object} meta The JSON metadata
8445          */
8446         metachange : true,
8447         /**
8448          * @event add
8449          * Fires when Records have been added to the Store
8450          * @param {Store} this
8451          * @param {Roo.data.Record[]} records The array of Records added
8452          * @param {Number} index The index at which the record(s) were added
8453          */
8454         add : true,
8455         /**
8456          * @event remove
8457          * Fires when a Record has been removed from the Store
8458          * @param {Store} this
8459          * @param {Roo.data.Record} record The Record that was removed
8460          * @param {Number} index The index at which the record was removed
8461          */
8462         remove : true,
8463         /**
8464          * @event update
8465          * Fires when a Record has been updated
8466          * @param {Store} this
8467          * @param {Roo.data.Record} record The Record that was updated
8468          * @param {String} operation The update operation being performed.  Value may be one of:
8469          * <pre><code>
8470  Roo.data.Record.EDIT
8471  Roo.data.Record.REJECT
8472  Roo.data.Record.COMMIT
8473          * </code></pre>
8474          */
8475         update : true,
8476         /**
8477          * @event clear
8478          * Fires when the data cache has been cleared.
8479          * @param {Store} this
8480          */
8481         clear : true,
8482         /**
8483          * @event beforeload
8484          * Fires before a request is made for a new data object.  If the beforeload handler returns false
8485          * the load action will be canceled.
8486          * @param {Store} this
8487          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8488          */
8489         beforeload : true,
8490         /**
8491          * @event beforeloadadd
8492          * Fires after a new set of Records has been loaded.
8493          * @param {Store} this
8494          * @param {Roo.data.Record[]} records The Records that were loaded
8495          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8496          */
8497         beforeloadadd : true,
8498         /**
8499          * @event load
8500          * Fires after a new set of Records has been loaded, before they are added to the store.
8501          * @param {Store} this
8502          * @param {Roo.data.Record[]} records The Records that were loaded
8503          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8504          * @params {Object} return from reader
8505          */
8506         load : true,
8507         /**
8508          * @event loadexception
8509          * Fires if an exception occurs in the Proxy during loading.
8510          * Called with the signature of the Proxy's "loadexception" event.
8511          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
8512          * 
8513          * @param {Proxy} 
8514          * @param {Object} return from JsonData.reader() - success, totalRecords, records
8515          * @param {Object} load options 
8516          * @param {Object} jsonData from your request (normally this contains the Exception)
8517          */
8518         loadexception : true
8519     });
8520     
8521     if(this.proxy){
8522         this.proxy = Roo.factory(this.proxy, Roo.data);
8523         this.proxy.xmodule = this.xmodule || false;
8524         this.relayEvents(this.proxy,  ["loadexception"]);
8525     }
8526     this.sortToggle = {};
8527     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
8528
8529     Roo.data.Store.superclass.constructor.call(this);
8530
8531     if(this.inlineData){
8532         this.loadData(this.inlineData);
8533         delete this.inlineData;
8534     }
8535 };
8536
8537 Roo.extend(Roo.data.Store, Roo.util.Observable, {
8538      /**
8539     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
8540     * without a remote query - used by combo/forms at present.
8541     */
8542     
8543     /**
8544     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
8545     */
8546     /**
8547     * @cfg {Array} data Inline data to be loaded when the store is initialized.
8548     */
8549     /**
8550     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
8551     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
8552     */
8553     /**
8554     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
8555     * on any HTTP request
8556     */
8557     /**
8558     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
8559     */
8560     /**
8561     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
8562     */
8563     multiSort: false,
8564     /**
8565     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
8566     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
8567     */
8568     remoteSort : false,
8569
8570     /**
8571     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
8572      * loaded or when a record is removed. (defaults to false).
8573     */
8574     pruneModifiedRecords : false,
8575
8576     // private
8577     lastOptions : null,
8578
8579     /**
8580      * Add Records to the Store and fires the add event.
8581      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8582      */
8583     add : function(records){
8584         records = [].concat(records);
8585         for(var i = 0, len = records.length; i < len; i++){
8586             records[i].join(this);
8587         }
8588         var index = this.data.length;
8589         this.data.addAll(records);
8590         this.fireEvent("add", this, records, index);
8591     },
8592
8593     /**
8594      * Remove a Record from the Store and fires the remove event.
8595      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
8596      */
8597     remove : function(record){
8598         var index = this.data.indexOf(record);
8599         this.data.removeAt(index);
8600         if(this.pruneModifiedRecords){
8601             this.modified.remove(record);
8602         }
8603         this.fireEvent("remove", this, record, index);
8604     },
8605
8606     /**
8607      * Remove all Records from the Store and fires the clear event.
8608      */
8609     removeAll : function(){
8610         this.data.clear();
8611         if(this.pruneModifiedRecords){
8612             this.modified = [];
8613         }
8614         this.fireEvent("clear", this);
8615     },
8616
8617     /**
8618      * Inserts Records to the Store at the given index and fires the add event.
8619      * @param {Number} index The start index at which to insert the passed Records.
8620      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8621      */
8622     insert : function(index, records){
8623         records = [].concat(records);
8624         for(var i = 0, len = records.length; i < len; i++){
8625             this.data.insert(index, records[i]);
8626             records[i].join(this);
8627         }
8628         this.fireEvent("add", this, records, index);
8629     },
8630
8631     /**
8632      * Get the index within the cache of the passed Record.
8633      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
8634      * @return {Number} The index of the passed Record. Returns -1 if not found.
8635      */
8636     indexOf : function(record){
8637         return this.data.indexOf(record);
8638     },
8639
8640     /**
8641      * Get the index within the cache of the Record with the passed id.
8642      * @param {String} id The id of the Record to find.
8643      * @return {Number} The index of the Record. Returns -1 if not found.
8644      */
8645     indexOfId : function(id){
8646         return this.data.indexOfKey(id);
8647     },
8648
8649     /**
8650      * Get the Record with the specified id.
8651      * @param {String} id The id of the Record to find.
8652      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
8653      */
8654     getById : function(id){
8655         return this.data.key(id);
8656     },
8657
8658     /**
8659      * Get the Record at the specified index.
8660      * @param {Number} index The index of the Record to find.
8661      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
8662      */
8663     getAt : function(index){
8664         return this.data.itemAt(index);
8665     },
8666
8667     /**
8668      * Returns a range of Records between specified indices.
8669      * @param {Number} startIndex (optional) The starting index (defaults to 0)
8670      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
8671      * @return {Roo.data.Record[]} An array of Records
8672      */
8673     getRange : function(start, end){
8674         return this.data.getRange(start, end);
8675     },
8676
8677     // private
8678     storeOptions : function(o){
8679         o = Roo.apply({}, o);
8680         delete o.callback;
8681         delete o.scope;
8682         this.lastOptions = o;
8683     },
8684
8685     /**
8686      * Loads the Record cache from the configured Proxy using the configured Reader.
8687      * <p>
8688      * If using remote paging, then the first load call must specify the <em>start</em>
8689      * and <em>limit</em> properties in the options.params property to establish the initial
8690      * position within the dataset, and the number of Records to cache on each read from the Proxy.
8691      * <p>
8692      * <strong>It is important to note that for remote data sources, loading is asynchronous,
8693      * and this call will return before the new data has been loaded. Perform any post-processing
8694      * in a callback function, or in a "load" event handler.</strong>
8695      * <p>
8696      * @param {Object} options An object containing properties which control loading options:<ul>
8697      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
8698      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
8699      * passed the following arguments:<ul>
8700      * <li>r : Roo.data.Record[]</li>
8701      * <li>options: Options object from the load call</li>
8702      * <li>success: Boolean success indicator</li></ul></li>
8703      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
8704      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
8705      * </ul>
8706      */
8707     load : function(options){
8708         options = options || {};
8709         if(this.fireEvent("beforeload", this, options) !== false){
8710             this.storeOptions(options);
8711             var p = Roo.apply(options.params || {}, this.baseParams);
8712             // if meta was not loaded from remote source.. try requesting it.
8713             if (!this.reader.metaFromRemote) {
8714                 p._requestMeta = 1;
8715             }
8716             if(this.sortInfo && this.remoteSort){
8717                 var pn = this.paramNames;
8718                 p[pn["sort"]] = this.sortInfo.field;
8719                 p[pn["dir"]] = this.sortInfo.direction;
8720             }
8721             if (this.multiSort) {
8722                 var pn = this.paramNames;
8723                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
8724             }
8725             
8726             this.proxy.load(p, this.reader, this.loadRecords, this, options);
8727         }
8728     },
8729
8730     /**
8731      * Reloads the Record cache from the configured Proxy using the configured Reader and
8732      * the options from the last load operation performed.
8733      * @param {Object} options (optional) An object containing properties which may override the options
8734      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
8735      * the most recently used options are reused).
8736      */
8737     reload : function(options){
8738         this.load(Roo.applyIf(options||{}, this.lastOptions));
8739     },
8740
8741     // private
8742     // Called as a callback by the Reader during a load operation.
8743     loadRecords : function(o, options, success){
8744         if(!o || success === false){
8745             if(success !== false){
8746                 this.fireEvent("load", this, [], options, o);
8747             }
8748             if(options.callback){
8749                 options.callback.call(options.scope || this, [], options, false);
8750             }
8751             return;
8752         }
8753         // if data returned failure - throw an exception.
8754         if (o.success === false) {
8755             // show a message if no listener is registered.
8756             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
8757                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
8758             }
8759             // loadmask wil be hooked into this..
8760             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
8761             return;
8762         }
8763         var r = o.records, t = o.totalRecords || r.length;
8764         
8765         this.fireEvent("beforeloadadd", this, r, options, o);
8766         
8767         if(!options || options.add !== true){
8768             if(this.pruneModifiedRecords){
8769                 this.modified = [];
8770             }
8771             for(var i = 0, len = r.length; i < len; i++){
8772                 r[i].join(this);
8773             }
8774             if(this.snapshot){
8775                 this.data = this.snapshot;
8776                 delete this.snapshot;
8777             }
8778             this.data.clear();
8779             this.data.addAll(r);
8780             this.totalLength = t;
8781             this.applySort();
8782             this.fireEvent("datachanged", this);
8783         }else{
8784             this.totalLength = Math.max(t, this.data.length+r.length);
8785             this.add(r);
8786         }
8787         this.fireEvent("load", this, r, options, o);
8788         if(options.callback){
8789             options.callback.call(options.scope || this, r, options, true);
8790         }
8791     },
8792
8793
8794     /**
8795      * Loads data from a passed data block. A Reader which understands the format of the data
8796      * must have been configured in the constructor.
8797      * @param {Object} data The data block from which to read the Records.  The format of the data expected
8798      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
8799      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
8800      */
8801     loadData : function(o, append){
8802         var r = this.reader.readRecords(o);
8803         this.loadRecords(r, {add: append}, true);
8804     },
8805
8806     /**
8807      * Gets the number of cached records.
8808      * <p>
8809      * <em>If using paging, this may not be the total size of the dataset. If the data object
8810      * used by the Reader contains the dataset size, then the getTotalCount() function returns
8811      * the data set size</em>
8812      */
8813     getCount : function(){
8814         return this.data.length || 0;
8815     },
8816
8817     /**
8818      * Gets the total number of records in the dataset as returned by the server.
8819      * <p>
8820      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
8821      * the dataset size</em>
8822      */
8823     getTotalCount : function(){
8824         return this.totalLength || 0;
8825     },
8826
8827     /**
8828      * Returns the sort state of the Store as an object with two properties:
8829      * <pre><code>
8830  field {String} The name of the field by which the Records are sorted
8831  direction {String} The sort order, "ASC" or "DESC"
8832      * </code></pre>
8833      */
8834     getSortState : function(){
8835         return this.sortInfo;
8836     },
8837
8838     // private
8839     applySort : function(){
8840         if(this.sortInfo && !this.remoteSort){
8841             var s = this.sortInfo, f = s.field;
8842             var st = this.fields.get(f).sortType;
8843             var fn = function(r1, r2){
8844                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
8845                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
8846             };
8847             this.data.sort(s.direction, fn);
8848             if(this.snapshot && this.snapshot != this.data){
8849                 this.snapshot.sort(s.direction, fn);
8850             }
8851         }
8852     },
8853
8854     /**
8855      * Sets the default sort column and order to be used by the next load operation.
8856      * @param {String} fieldName The name of the field to sort by.
8857      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
8858      */
8859     setDefaultSort : function(field, dir){
8860         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
8861     },
8862
8863     /**
8864      * Sort the Records.
8865      * If remote sorting is used, the sort is performed on the server, and the cache is
8866      * reloaded. If local sorting is used, the cache is sorted internally.
8867      * @param {String} fieldName The name of the field to sort by.
8868      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
8869      */
8870     sort : function(fieldName, dir){
8871         var f = this.fields.get(fieldName);
8872         if(!dir){
8873             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
8874             
8875             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
8876                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
8877             }else{
8878                 dir = f.sortDir;
8879             }
8880         }
8881         this.sortToggle[f.name] = dir;
8882         this.sortInfo = {field: f.name, direction: dir};
8883         if(!this.remoteSort){
8884             this.applySort();
8885             this.fireEvent("datachanged", this);
8886         }else{
8887             this.load(this.lastOptions);
8888         }
8889     },
8890
8891     /**
8892      * Calls the specified function for each of the Records in the cache.
8893      * @param {Function} fn The function to call. The Record is passed as the first parameter.
8894      * Returning <em>false</em> aborts and exits the iteration.
8895      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
8896      */
8897     each : function(fn, scope){
8898         this.data.each(fn, scope);
8899     },
8900
8901     /**
8902      * Gets all records modified since the last commit.  Modified records are persisted across load operations
8903      * (e.g., during paging).
8904      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
8905      */
8906     getModifiedRecords : function(){
8907         return this.modified;
8908     },
8909
8910     // private
8911     createFilterFn : function(property, value, anyMatch){
8912         if(!value.exec){ // not a regex
8913             value = String(value);
8914             if(value.length == 0){
8915                 return false;
8916             }
8917             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
8918         }
8919         return function(r){
8920             return value.test(r.data[property]);
8921         };
8922     },
8923
8924     /**
8925      * Sums the value of <i>property</i> for each record between start and end and returns the result.
8926      * @param {String} property A field on your records
8927      * @param {Number} start The record index to start at (defaults to 0)
8928      * @param {Number} end The last record index to include (defaults to length - 1)
8929      * @return {Number} The sum
8930      */
8931     sum : function(property, start, end){
8932         var rs = this.data.items, v = 0;
8933         start = start || 0;
8934         end = (end || end === 0) ? end : rs.length-1;
8935
8936         for(var i = start; i <= end; i++){
8937             v += (rs[i].data[property] || 0);
8938         }
8939         return v;
8940     },
8941
8942     /**
8943      * Filter the records by a specified property.
8944      * @param {String} field A field on your records
8945      * @param {String/RegExp} value Either a string that the field
8946      * should start with or a RegExp to test against the field
8947      * @param {Boolean} anyMatch True to match any part not just the beginning
8948      */
8949     filter : function(property, value, anyMatch){
8950         var fn = this.createFilterFn(property, value, anyMatch);
8951         return fn ? this.filterBy(fn) : this.clearFilter();
8952     },
8953
8954     /**
8955      * Filter by a function. The specified function will be called with each
8956      * record in this data source. If the function returns true the record is included,
8957      * otherwise it is filtered.
8958      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
8959      * @param {Object} scope (optional) The scope of the function (defaults to this)
8960      */
8961     filterBy : function(fn, scope){
8962         this.snapshot = this.snapshot || this.data;
8963         this.data = this.queryBy(fn, scope||this);
8964         this.fireEvent("datachanged", this);
8965     },
8966
8967     /**
8968      * Query the records by a specified property.
8969      * @param {String} field A field on your records
8970      * @param {String/RegExp} value Either a string that the field
8971      * should start with or a RegExp to test against the field
8972      * @param {Boolean} anyMatch True to match any part not just the beginning
8973      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
8974      */
8975     query : function(property, value, anyMatch){
8976         var fn = this.createFilterFn(property, value, anyMatch);
8977         return fn ? this.queryBy(fn) : this.data.clone();
8978     },
8979
8980     /**
8981      * Query by a function. The specified function will be called with each
8982      * record in this data source. If the function returns true the record is included
8983      * in the results.
8984      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
8985      * @param {Object} scope (optional) The scope of the function (defaults to this)
8986       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
8987      **/
8988     queryBy : function(fn, scope){
8989         var data = this.snapshot || this.data;
8990         return data.filterBy(fn, scope||this);
8991     },
8992
8993     /**
8994      * Collects unique values for a particular dataIndex from this store.
8995      * @param {String} dataIndex The property to collect
8996      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
8997      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
8998      * @return {Array} An array of the unique values
8999      **/
9000     collect : function(dataIndex, allowNull, bypassFilter){
9001         var d = (bypassFilter === true && this.snapshot) ?
9002                 this.snapshot.items : this.data.items;
9003         var v, sv, r = [], l = {};
9004         for(var i = 0, len = d.length; i < len; i++){
9005             v = d[i].data[dataIndex];
9006             sv = String(v);
9007             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
9008                 l[sv] = true;
9009                 r[r.length] = v;
9010             }
9011         }
9012         return r;
9013     },
9014
9015     /**
9016      * Revert to a view of the Record cache with no filtering applied.
9017      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
9018      */
9019     clearFilter : function(suppressEvent){
9020         if(this.snapshot && this.snapshot != this.data){
9021             this.data = this.snapshot;
9022             delete this.snapshot;
9023             if(suppressEvent !== true){
9024                 this.fireEvent("datachanged", this);
9025             }
9026         }
9027     },
9028
9029     // private
9030     afterEdit : function(record){
9031         if(this.modified.indexOf(record) == -1){
9032             this.modified.push(record);
9033         }
9034         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
9035     },
9036     
9037     // private
9038     afterReject : function(record){
9039         this.modified.remove(record);
9040         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
9041     },
9042
9043     // private
9044     afterCommit : function(record){
9045         this.modified.remove(record);
9046         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
9047     },
9048
9049     /**
9050      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
9051      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
9052      */
9053     commitChanges : function(){
9054         var m = this.modified.slice(0);
9055         this.modified = [];
9056         for(var i = 0, len = m.length; i < len; i++){
9057             m[i].commit();
9058         }
9059     },
9060
9061     /**
9062      * Cancel outstanding changes on all changed records.
9063      */
9064     rejectChanges : function(){
9065         var m = this.modified.slice(0);
9066         this.modified = [];
9067         for(var i = 0, len = m.length; i < len; i++){
9068             m[i].reject();
9069         }
9070     },
9071
9072     onMetaChange : function(meta, rtype, o){
9073         this.recordType = rtype;
9074         this.fields = rtype.prototype.fields;
9075         delete this.snapshot;
9076         this.sortInfo = meta.sortInfo || this.sortInfo;
9077         this.modified = [];
9078         this.fireEvent('metachange', this, this.reader.meta);
9079     },
9080     
9081     moveIndex : function(data, type)
9082     {
9083         var index = this.indexOf(data);
9084         
9085         var newIndex = index + type;
9086         
9087         this.remove(data);
9088         
9089         this.insert(newIndex, data);
9090         
9091     }
9092 });/*
9093  * Based on:
9094  * Ext JS Library 1.1.1
9095  * Copyright(c) 2006-2007, Ext JS, LLC.
9096  *
9097  * Originally Released Under LGPL - original licence link has changed is not relivant.
9098  *
9099  * Fork - LGPL
9100  * <script type="text/javascript">
9101  */
9102
9103 /**
9104  * @class Roo.data.SimpleStore
9105  * @extends Roo.data.Store
9106  * Small helper class to make creating Stores from Array data easier.
9107  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
9108  * @cfg {Array} fields An array of field definition objects, or field name strings.
9109  * @cfg {Array} data The multi-dimensional array of data
9110  * @constructor
9111  * @param {Object} config
9112  */
9113 Roo.data.SimpleStore = function(config){
9114     Roo.data.SimpleStore.superclass.constructor.call(this, {
9115         isLocal : true,
9116         reader: new Roo.data.ArrayReader({
9117                 id: config.id
9118             },
9119             Roo.data.Record.create(config.fields)
9120         ),
9121         proxy : new Roo.data.MemoryProxy(config.data)
9122     });
9123     this.load();
9124 };
9125 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
9126  * Based on:
9127  * Ext JS Library 1.1.1
9128  * Copyright(c) 2006-2007, Ext JS, LLC.
9129  *
9130  * Originally Released Under LGPL - original licence link has changed is not relivant.
9131  *
9132  * Fork - LGPL
9133  * <script type="text/javascript">
9134  */
9135
9136 /**
9137 /**
9138  * @extends Roo.data.Store
9139  * @class Roo.data.JsonStore
9140  * Small helper class to make creating Stores for JSON data easier. <br/>
9141 <pre><code>
9142 var store = new Roo.data.JsonStore({
9143     url: 'get-images.php',
9144     root: 'images',
9145     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
9146 });
9147 </code></pre>
9148  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
9149  * JsonReader and HttpProxy (unless inline data is provided).</b>
9150  * @cfg {Array} fields An array of field definition objects, or field name strings.
9151  * @constructor
9152  * @param {Object} config
9153  */
9154 Roo.data.JsonStore = function(c){
9155     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
9156         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
9157         reader: new Roo.data.JsonReader(c, c.fields)
9158     }));
9159 };
9160 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
9161  * Based on:
9162  * Ext JS Library 1.1.1
9163  * Copyright(c) 2006-2007, Ext JS, LLC.
9164  *
9165  * Originally Released Under LGPL - original licence link has changed is not relivant.
9166  *
9167  * Fork - LGPL
9168  * <script type="text/javascript">
9169  */
9170
9171  
9172 Roo.data.Field = function(config){
9173     if(typeof config == "string"){
9174         config = {name: config};
9175     }
9176     Roo.apply(this, config);
9177     
9178     if(!this.type){
9179         this.type = "auto";
9180     }
9181     
9182     var st = Roo.data.SortTypes;
9183     // named sortTypes are supported, here we look them up
9184     if(typeof this.sortType == "string"){
9185         this.sortType = st[this.sortType];
9186     }
9187     
9188     // set default sortType for strings and dates
9189     if(!this.sortType){
9190         switch(this.type){
9191             case "string":
9192                 this.sortType = st.asUCString;
9193                 break;
9194             case "date":
9195                 this.sortType = st.asDate;
9196                 break;
9197             default:
9198                 this.sortType = st.none;
9199         }
9200     }
9201
9202     // define once
9203     var stripRe = /[\$,%]/g;
9204
9205     // prebuilt conversion function for this field, instead of
9206     // switching every time we're reading a value
9207     if(!this.convert){
9208         var cv, dateFormat = this.dateFormat;
9209         switch(this.type){
9210             case "":
9211             case "auto":
9212             case undefined:
9213                 cv = function(v){ return v; };
9214                 break;
9215             case "string":
9216                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
9217                 break;
9218             case "int":
9219                 cv = function(v){
9220                     return v !== undefined && v !== null && v !== '' ?
9221                            parseInt(String(v).replace(stripRe, ""), 10) : '';
9222                     };
9223                 break;
9224             case "float":
9225                 cv = function(v){
9226                     return v !== undefined && v !== null && v !== '' ?
9227                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
9228                     };
9229                 break;
9230             case "bool":
9231             case "boolean":
9232                 cv = function(v){ return v === true || v === "true" || v == 1; };
9233                 break;
9234             case "date":
9235                 cv = function(v){
9236                     if(!v){
9237                         return '';
9238                     }
9239                     if(v instanceof Date){
9240                         return v;
9241                     }
9242                     if(dateFormat){
9243                         if(dateFormat == "timestamp"){
9244                             return new Date(v*1000);
9245                         }
9246                         return Date.parseDate(v, dateFormat);
9247                     }
9248                     var parsed = Date.parse(v);
9249                     return parsed ? new Date(parsed) : null;
9250                 };
9251              break;
9252             
9253         }
9254         this.convert = cv;
9255     }
9256 };
9257
9258 Roo.data.Field.prototype = {
9259     dateFormat: null,
9260     defaultValue: "",
9261     mapping: null,
9262     sortType : null,
9263     sortDir : "ASC"
9264 };/*
9265  * Based on:
9266  * Ext JS Library 1.1.1
9267  * Copyright(c) 2006-2007, Ext JS, LLC.
9268  *
9269  * Originally Released Under LGPL - original licence link has changed is not relivant.
9270  *
9271  * Fork - LGPL
9272  * <script type="text/javascript">
9273  */
9274  
9275 // Base class for reading structured data from a data source.  This class is intended to be
9276 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
9277
9278 /**
9279  * @class Roo.data.DataReader
9280  * Base class for reading structured data from a data source.  This class is intended to be
9281  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
9282  */
9283
9284 Roo.data.DataReader = function(meta, recordType){
9285     
9286     this.meta = meta;
9287     
9288     this.recordType = recordType instanceof Array ? 
9289         Roo.data.Record.create(recordType) : recordType;
9290 };
9291
9292 Roo.data.DataReader.prototype = {
9293      /**
9294      * Create an empty record
9295      * @param {Object} data (optional) - overlay some values
9296      * @return {Roo.data.Record} record created.
9297      */
9298     newRow :  function(d) {
9299         var da =  {};
9300         this.recordType.prototype.fields.each(function(c) {
9301             switch( c.type) {
9302                 case 'int' : da[c.name] = 0; break;
9303                 case 'date' : da[c.name] = new Date(); break;
9304                 case 'float' : da[c.name] = 0.0; break;
9305                 case 'boolean' : da[c.name] = false; break;
9306                 default : da[c.name] = ""; break;
9307             }
9308             
9309         });
9310         return new this.recordType(Roo.apply(da, d));
9311     }
9312     
9313 };/*
9314  * Based on:
9315  * Ext JS Library 1.1.1
9316  * Copyright(c) 2006-2007, Ext JS, LLC.
9317  *
9318  * Originally Released Under LGPL - original licence link has changed is not relivant.
9319  *
9320  * Fork - LGPL
9321  * <script type="text/javascript">
9322  */
9323
9324 /**
9325  * @class Roo.data.DataProxy
9326  * @extends Roo.data.Observable
9327  * This class is an abstract base class for implementations which provide retrieval of
9328  * unformatted data objects.<br>
9329  * <p>
9330  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
9331  * (of the appropriate type which knows how to parse the data object) to provide a block of
9332  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
9333  * <p>
9334  * Custom implementations must implement the load method as described in
9335  * {@link Roo.data.HttpProxy#load}.
9336  */
9337 Roo.data.DataProxy = function(){
9338     this.addEvents({
9339         /**
9340          * @event beforeload
9341          * Fires before a network request is made to retrieve a data object.
9342          * @param {Object} This DataProxy object.
9343          * @param {Object} params The params parameter to the load function.
9344          */
9345         beforeload : true,
9346         /**
9347          * @event load
9348          * Fires before the load method's callback is called.
9349          * @param {Object} This DataProxy object.
9350          * @param {Object} o The data object.
9351          * @param {Object} arg The callback argument object passed to the load function.
9352          */
9353         load : true,
9354         /**
9355          * @event loadexception
9356          * Fires if an Exception occurs during data retrieval.
9357          * @param {Object} This DataProxy object.
9358          * @param {Object} o The data object.
9359          * @param {Object} arg The callback argument object passed to the load function.
9360          * @param {Object} e The Exception.
9361          */
9362         loadexception : true
9363     });
9364     Roo.data.DataProxy.superclass.constructor.call(this);
9365 };
9366
9367 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
9368
9369     /**
9370      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
9371      */
9372 /*
9373  * Based on:
9374  * Ext JS Library 1.1.1
9375  * Copyright(c) 2006-2007, Ext JS, LLC.
9376  *
9377  * Originally Released Under LGPL - original licence link has changed is not relivant.
9378  *
9379  * Fork - LGPL
9380  * <script type="text/javascript">
9381  */
9382 /**
9383  * @class Roo.data.MemoryProxy
9384  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
9385  * to the Reader when its load method is called.
9386  * @constructor
9387  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
9388  */
9389 Roo.data.MemoryProxy = function(data){
9390     if (data.data) {
9391         data = data.data;
9392     }
9393     Roo.data.MemoryProxy.superclass.constructor.call(this);
9394     this.data = data;
9395 };
9396
9397 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
9398     /**
9399      * Load data from the requested source (in this case an in-memory
9400      * data object passed to the constructor), read the data object into
9401      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9402      * process that block using the passed callback.
9403      * @param {Object} params This parameter is not used by the MemoryProxy class.
9404      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9405      * object into a block of Roo.data.Records.
9406      * @param {Function} callback The function into which to pass the block of Roo.data.records.
9407      * The function must be passed <ul>
9408      * <li>The Record block object</li>
9409      * <li>The "arg" argument from the load function</li>
9410      * <li>A boolean success indicator</li>
9411      * </ul>
9412      * @param {Object} scope The scope in which to call the callback
9413      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9414      */
9415     load : function(params, reader, callback, scope, arg){
9416         params = params || {};
9417         var result;
9418         try {
9419             result = reader.readRecords(this.data);
9420         }catch(e){
9421             this.fireEvent("loadexception", this, arg, null, e);
9422             callback.call(scope, null, arg, false);
9423             return;
9424         }
9425         callback.call(scope, result, arg, true);
9426     },
9427     
9428     // private
9429     update : function(params, records){
9430         
9431     }
9432 });/*
9433  * Based on:
9434  * Ext JS Library 1.1.1
9435  * Copyright(c) 2006-2007, Ext JS, LLC.
9436  *
9437  * Originally Released Under LGPL - original licence link has changed is not relivant.
9438  *
9439  * Fork - LGPL
9440  * <script type="text/javascript">
9441  */
9442 /**
9443  * @class Roo.data.HttpProxy
9444  * @extends Roo.data.DataProxy
9445  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
9446  * configured to reference a certain URL.<br><br>
9447  * <p>
9448  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
9449  * from which the running page was served.<br><br>
9450  * <p>
9451  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
9452  * <p>
9453  * Be aware that to enable the browser to parse an XML document, the server must set
9454  * the Content-Type header in the HTTP response to "text/xml".
9455  * @constructor
9456  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
9457  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
9458  * will be used to make the request.
9459  */
9460 Roo.data.HttpProxy = function(conn){
9461     Roo.data.HttpProxy.superclass.constructor.call(this);
9462     // is conn a conn config or a real conn?
9463     this.conn = conn;
9464     this.useAjax = !conn || !conn.events;
9465   
9466 };
9467
9468 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
9469     // thse are take from connection...
9470     
9471     /**
9472      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
9473      */
9474     /**
9475      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
9476      * extra parameters to each request made by this object. (defaults to undefined)
9477      */
9478     /**
9479      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
9480      *  to each request made by this object. (defaults to undefined)
9481      */
9482     /**
9483      * @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)
9484      */
9485     /**
9486      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
9487      */
9488      /**
9489      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
9490      * @type Boolean
9491      */
9492   
9493
9494     /**
9495      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
9496      * @type Boolean
9497      */
9498     /**
9499      * Return the {@link Roo.data.Connection} object being used by this Proxy.
9500      * @return {Connection} The Connection object. This object may be used to subscribe to events on
9501      * a finer-grained basis than the DataProxy events.
9502      */
9503     getConnection : function(){
9504         return this.useAjax ? Roo.Ajax : this.conn;
9505     },
9506
9507     /**
9508      * Load data from the configured {@link Roo.data.Connection}, read the data object into
9509      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
9510      * process that block using the passed callback.
9511      * @param {Object} params An object containing properties which are to be used as HTTP parameters
9512      * for the request to the remote server.
9513      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9514      * object into a block of Roo.data.Records.
9515      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9516      * The function must be passed <ul>
9517      * <li>The Record block object</li>
9518      * <li>The "arg" argument from the load function</li>
9519      * <li>A boolean success indicator</li>
9520      * </ul>
9521      * @param {Object} scope The scope in which to call the callback
9522      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9523      */
9524     load : function(params, reader, callback, scope, arg){
9525         if(this.fireEvent("beforeload", this, params) !== false){
9526             var  o = {
9527                 params : params || {},
9528                 request: {
9529                     callback : callback,
9530                     scope : scope,
9531                     arg : arg
9532                 },
9533                 reader: reader,
9534                 callback : this.loadResponse,
9535                 scope: this
9536             };
9537             if(this.useAjax){
9538                 Roo.applyIf(o, this.conn);
9539                 if(this.activeRequest){
9540                     Roo.Ajax.abort(this.activeRequest);
9541                 }
9542                 this.activeRequest = Roo.Ajax.request(o);
9543             }else{
9544                 this.conn.request(o);
9545             }
9546         }else{
9547             callback.call(scope||this, null, arg, false);
9548         }
9549     },
9550
9551     // private
9552     loadResponse : function(o, success, response){
9553         delete this.activeRequest;
9554         if(!success){
9555             this.fireEvent("loadexception", this, o, response);
9556             o.request.callback.call(o.request.scope, null, o.request.arg, false);
9557             return;
9558         }
9559         var result;
9560         try {
9561             result = o.reader.read(response);
9562         }catch(e){
9563             this.fireEvent("loadexception", this, o, response, e);
9564             o.request.callback.call(o.request.scope, null, o.request.arg, false);
9565             return;
9566         }
9567         
9568         this.fireEvent("load", this, o, o.request.arg);
9569         o.request.callback.call(o.request.scope, result, o.request.arg, true);
9570     },
9571
9572     // private
9573     update : function(dataSet){
9574
9575     },
9576
9577     // private
9578     updateResponse : function(dataSet){
9579
9580     }
9581 });/*
9582  * Based on:
9583  * Ext JS Library 1.1.1
9584  * Copyright(c) 2006-2007, Ext JS, LLC.
9585  *
9586  * Originally Released Under LGPL - original licence link has changed is not relivant.
9587  *
9588  * Fork - LGPL
9589  * <script type="text/javascript">
9590  */
9591
9592 /**
9593  * @class Roo.data.ScriptTagProxy
9594  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
9595  * other than the originating domain of the running page.<br><br>
9596  * <p>
9597  * <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
9598  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
9599  * <p>
9600  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
9601  * source code that is used as the source inside a &lt;script> tag.<br><br>
9602  * <p>
9603  * In order for the browser to process the returned data, the server must wrap the data object
9604  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
9605  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
9606  * depending on whether the callback name was passed:
9607  * <p>
9608  * <pre><code>
9609 boolean scriptTag = false;
9610 String cb = request.getParameter("callback");
9611 if (cb != null) {
9612     scriptTag = true;
9613     response.setContentType("text/javascript");
9614 } else {
9615     response.setContentType("application/x-json");
9616 }
9617 Writer out = response.getWriter();
9618 if (scriptTag) {
9619     out.write(cb + "(");
9620 }
9621 out.print(dataBlock.toJsonString());
9622 if (scriptTag) {
9623     out.write(");");
9624 }
9625 </pre></code>
9626  *
9627  * @constructor
9628  * @param {Object} config A configuration object.
9629  */
9630 Roo.data.ScriptTagProxy = function(config){
9631     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
9632     Roo.apply(this, config);
9633     this.head = document.getElementsByTagName("head")[0];
9634 };
9635
9636 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
9637
9638 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
9639     /**
9640      * @cfg {String} url The URL from which to request the data object.
9641      */
9642     /**
9643      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
9644      */
9645     timeout : 30000,
9646     /**
9647      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
9648      * the server the name of the callback function set up by the load call to process the returned data object.
9649      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
9650      * javascript output which calls this named function passing the data object as its only parameter.
9651      */
9652     callbackParam : "callback",
9653     /**
9654      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
9655      * name to the request.
9656      */
9657     nocache : true,
9658
9659     /**
9660      * Load data from the configured URL, read the data object into
9661      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9662      * process that block using the passed callback.
9663      * @param {Object} params An object containing properties which are to be used as HTTP parameters
9664      * for the request to the remote server.
9665      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9666      * object into a block of Roo.data.Records.
9667      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9668      * The function must be passed <ul>
9669      * <li>The Record block object</li>
9670      * <li>The "arg" argument from the load function</li>
9671      * <li>A boolean success indicator</li>
9672      * </ul>
9673      * @param {Object} scope The scope in which to call the callback
9674      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9675      */
9676     load : function(params, reader, callback, scope, arg){
9677         if(this.fireEvent("beforeload", this, params) !== false){
9678
9679             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
9680
9681             var url = this.url;
9682             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
9683             if(this.nocache){
9684                 url += "&_dc=" + (new Date().getTime());
9685             }
9686             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
9687             var trans = {
9688                 id : transId,
9689                 cb : "stcCallback"+transId,
9690                 scriptId : "stcScript"+transId,
9691                 params : params,
9692                 arg : arg,
9693                 url : url,
9694                 callback : callback,
9695                 scope : scope,
9696                 reader : reader
9697             };
9698             var conn = this;
9699
9700             window[trans.cb] = function(o){
9701                 conn.handleResponse(o, trans);
9702             };
9703
9704             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
9705
9706             if(this.autoAbort !== false){
9707                 this.abort();
9708             }
9709
9710             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
9711
9712             var script = document.createElement("script");
9713             script.setAttribute("src", url);
9714             script.setAttribute("type", "text/javascript");
9715             script.setAttribute("id", trans.scriptId);
9716             this.head.appendChild(script);
9717
9718             this.trans = trans;
9719         }else{
9720             callback.call(scope||this, null, arg, false);
9721         }
9722     },
9723
9724     // private
9725     isLoading : function(){
9726         return this.trans ? true : false;
9727     },
9728
9729     /**
9730      * Abort the current server request.
9731      */
9732     abort : function(){
9733         if(this.isLoading()){
9734             this.destroyTrans(this.trans);
9735         }
9736     },
9737
9738     // private
9739     destroyTrans : function(trans, isLoaded){
9740         this.head.removeChild(document.getElementById(trans.scriptId));
9741         clearTimeout(trans.timeoutId);
9742         if(isLoaded){
9743             window[trans.cb] = undefined;
9744             try{
9745                 delete window[trans.cb];
9746             }catch(e){}
9747         }else{
9748             // if hasn't been loaded, wait for load to remove it to prevent script error
9749             window[trans.cb] = function(){
9750                 window[trans.cb] = undefined;
9751                 try{
9752                     delete window[trans.cb];
9753                 }catch(e){}
9754             };
9755         }
9756     },
9757
9758     // private
9759     handleResponse : function(o, trans){
9760         this.trans = false;
9761         this.destroyTrans(trans, true);
9762         var result;
9763         try {
9764             result = trans.reader.readRecords(o);
9765         }catch(e){
9766             this.fireEvent("loadexception", this, o, trans.arg, e);
9767             trans.callback.call(trans.scope||window, null, trans.arg, false);
9768             return;
9769         }
9770         this.fireEvent("load", this, o, trans.arg);
9771         trans.callback.call(trans.scope||window, result, trans.arg, true);
9772     },
9773
9774     // private
9775     handleFailure : function(trans){
9776         this.trans = false;
9777         this.destroyTrans(trans, false);
9778         this.fireEvent("loadexception", this, null, trans.arg);
9779         trans.callback.call(trans.scope||window, null, trans.arg, false);
9780     }
9781 });/*
9782  * Based on:
9783  * Ext JS Library 1.1.1
9784  * Copyright(c) 2006-2007, Ext JS, LLC.
9785  *
9786  * Originally Released Under LGPL - original licence link has changed is not relivant.
9787  *
9788  * Fork - LGPL
9789  * <script type="text/javascript">
9790  */
9791
9792 /**
9793  * @class Roo.data.JsonReader
9794  * @extends Roo.data.DataReader
9795  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
9796  * based on mappings in a provided Roo.data.Record constructor.
9797  * 
9798  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
9799  * in the reply previously. 
9800  * 
9801  * <p>
9802  * Example code:
9803  * <pre><code>
9804 var RecordDef = Roo.data.Record.create([
9805     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
9806     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
9807 ]);
9808 var myReader = new Roo.data.JsonReader({
9809     totalProperty: "results",    // The property which contains the total dataset size (optional)
9810     root: "rows",                // The property which contains an Array of row objects
9811     id: "id"                     // The property within each row object that provides an ID for the record (optional)
9812 }, RecordDef);
9813 </code></pre>
9814  * <p>
9815  * This would consume a JSON file like this:
9816  * <pre><code>
9817 { 'results': 2, 'rows': [
9818     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
9819     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
9820 }
9821 </code></pre>
9822  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
9823  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
9824  * paged from the remote server.
9825  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
9826  * @cfg {String} root name of the property which contains the Array of row objects.
9827  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
9828  * @constructor
9829  * Create a new JsonReader
9830  * @param {Object} meta Metadata configuration options
9831  * @param {Object} recordType Either an Array of field definition objects,
9832  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
9833  */
9834 Roo.data.JsonReader = function(meta, recordType){
9835     
9836     meta = meta || {};
9837     // set some defaults:
9838     Roo.applyIf(meta, {
9839         totalProperty: 'total',
9840         successProperty : 'success',
9841         root : 'data',
9842         id : 'id'
9843     });
9844     
9845     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
9846 };
9847 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
9848     
9849     /**
9850      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
9851      * Used by Store query builder to append _requestMeta to params.
9852      * 
9853      */
9854     metaFromRemote : false,
9855     /**
9856      * This method is only used by a DataProxy which has retrieved data from a remote server.
9857      * @param {Object} response The XHR object which contains the JSON data in its responseText.
9858      * @return {Object} data A data block which is used by an Roo.data.Store object as
9859      * a cache of Roo.data.Records.
9860      */
9861     read : function(response){
9862         var json = response.responseText;
9863        
9864         var o = /* eval:var:o */ eval("("+json+")");
9865         if(!o) {
9866             throw {message: "JsonReader.read: Json object not found"};
9867         }
9868         
9869         if(o.metaData){
9870             
9871             delete this.ef;
9872             this.metaFromRemote = true;
9873             this.meta = o.metaData;
9874             this.recordType = Roo.data.Record.create(o.metaData.fields);
9875             this.onMetaChange(this.meta, this.recordType, o);
9876         }
9877         return this.readRecords(o);
9878     },
9879
9880     // private function a store will implement
9881     onMetaChange : function(meta, recordType, o){
9882
9883     },
9884
9885     /**
9886          * @ignore
9887          */
9888     simpleAccess: function(obj, subsc) {
9889         return obj[subsc];
9890     },
9891
9892         /**
9893          * @ignore
9894          */
9895     getJsonAccessor: function(){
9896         var re = /[\[\.]/;
9897         return function(expr) {
9898             try {
9899                 return(re.test(expr))
9900                     ? new Function("obj", "return obj." + expr)
9901                     : function(obj){
9902                         return obj[expr];
9903                     };
9904             } catch(e){}
9905             return Roo.emptyFn;
9906         };
9907     }(),
9908
9909     /**
9910      * Create a data block containing Roo.data.Records from an XML document.
9911      * @param {Object} o An object which contains an Array of row objects in the property specified
9912      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
9913      * which contains the total size of the dataset.
9914      * @return {Object} data A data block which is used by an Roo.data.Store object as
9915      * a cache of Roo.data.Records.
9916      */
9917     readRecords : function(o){
9918         /**
9919          * After any data loads, the raw JSON data is available for further custom processing.
9920          * @type Object
9921          */
9922         this.o = o;
9923         var s = this.meta, Record = this.recordType,
9924             f = Record.prototype.fields, fi = f.items, fl = f.length;
9925
9926 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
9927         if (!this.ef) {
9928             if(s.totalProperty) {
9929                     this.getTotal = this.getJsonAccessor(s.totalProperty);
9930                 }
9931                 if(s.successProperty) {
9932                     this.getSuccess = this.getJsonAccessor(s.successProperty);
9933                 }
9934                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
9935                 if (s.id) {
9936                         var g = this.getJsonAccessor(s.id);
9937                         this.getId = function(rec) {
9938                                 var r = g(rec);
9939                                 return (r === undefined || r === "") ? null : r;
9940                         };
9941                 } else {
9942                         this.getId = function(){return null;};
9943                 }
9944             this.ef = [];
9945             for(var jj = 0; jj < fl; jj++){
9946                 f = fi[jj];
9947                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
9948                 this.ef[jj] = this.getJsonAccessor(map);
9949             }
9950         }
9951
9952         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
9953         if(s.totalProperty){
9954             var vt = parseInt(this.getTotal(o), 10);
9955             if(!isNaN(vt)){
9956                 totalRecords = vt;
9957             }
9958         }
9959         if(s.successProperty){
9960             var vs = this.getSuccess(o);
9961             if(vs === false || vs === 'false'){
9962                 success = false;
9963             }
9964         }
9965         var records = [];
9966             for(var i = 0; i < c; i++){
9967                     var n = root[i];
9968                 var values = {};
9969                 var id = this.getId(n);
9970                 for(var j = 0; j < fl; j++){
9971                     f = fi[j];
9972                 var v = this.ef[j](n);
9973                 if (!f.convert) {
9974                     Roo.log('missing convert for ' + f.name);
9975                     Roo.log(f);
9976                     continue;
9977                 }
9978                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
9979                 }
9980                 var record = new Record(values, id);
9981                 record.json = n;
9982                 records[i] = record;
9983             }
9984             return {
9985             raw : o,
9986                 success : success,
9987                 records : records,
9988                 totalRecords : totalRecords
9989             };
9990     }
9991 });/*
9992  * Based on:
9993  * Ext JS Library 1.1.1
9994  * Copyright(c) 2006-2007, Ext JS, LLC.
9995  *
9996  * Originally Released Under LGPL - original licence link has changed is not relivant.
9997  *
9998  * Fork - LGPL
9999  * <script type="text/javascript">
10000  */
10001
10002 /**
10003  * @class Roo.data.ArrayReader
10004  * @extends Roo.data.DataReader
10005  * Data reader class to create an Array of Roo.data.Record objects from an Array.
10006  * Each element of that Array represents a row of data fields. The
10007  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
10008  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
10009  * <p>
10010  * Example code:.
10011  * <pre><code>
10012 var RecordDef = Roo.data.Record.create([
10013     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
10014     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
10015 ]);
10016 var myReader = new Roo.data.ArrayReader({
10017     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
10018 }, RecordDef);
10019 </code></pre>
10020  * <p>
10021  * This would consume an Array like this:
10022  * <pre><code>
10023 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
10024   </code></pre>
10025  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
10026  * @constructor
10027  * Create a new JsonReader
10028  * @param {Object} meta Metadata configuration options.
10029  * @param {Object} recordType Either an Array of field definition objects
10030  * as specified to {@link Roo.data.Record#create},
10031  * or an {@link Roo.data.Record} object
10032  * created using {@link Roo.data.Record#create}.
10033  */
10034 Roo.data.ArrayReader = function(meta, recordType){
10035     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
10036 };
10037
10038 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
10039     /**
10040      * Create a data block containing Roo.data.Records from an XML document.
10041      * @param {Object} o An Array of row objects which represents the dataset.
10042      * @return {Object} data A data block which is used by an Roo.data.Store object as
10043      * a cache of Roo.data.Records.
10044      */
10045     readRecords : function(o){
10046         var sid = this.meta ? this.meta.id : null;
10047         var recordType = this.recordType, fields = recordType.prototype.fields;
10048         var records = [];
10049         var root = o;
10050             for(var i = 0; i < root.length; i++){
10051                     var n = root[i];
10052                 var values = {};
10053                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
10054                 for(var j = 0, jlen = fields.length; j < jlen; j++){
10055                 var f = fields.items[j];
10056                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
10057                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
10058                 v = f.convert(v);
10059                 values[f.name] = v;
10060             }
10061                 var record = new recordType(values, id);
10062                 record.json = n;
10063                 records[records.length] = record;
10064             }
10065             return {
10066                 records : records,
10067                 totalRecords : records.length
10068             };
10069     }
10070 });/*
10071  * - LGPL
10072  * * 
10073  */
10074
10075 /**
10076  * @class Roo.bootstrap.ComboBox
10077  * @extends Roo.bootstrap.TriggerField
10078  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
10079  * @cfg {Boolean} append (true|false) default false
10080  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
10081  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
10082  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
10083  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
10084  * @constructor
10085  * Create a new ComboBox.
10086  * @param {Object} config Configuration options
10087  */
10088 Roo.bootstrap.ComboBox = function(config){
10089     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
10090     this.addEvents({
10091         /**
10092          * @event expand
10093          * Fires when the dropdown list is expanded
10094              * @param {Roo.bootstrap.ComboBox} combo This combo box
10095              */
10096         'expand' : true,
10097         /**
10098          * @event collapse
10099          * Fires when the dropdown list is collapsed
10100              * @param {Roo.bootstrap.ComboBox} combo This combo box
10101              */
10102         'collapse' : true,
10103         /**
10104          * @event beforeselect
10105          * Fires before a list item is selected. Return false to cancel the selection.
10106              * @param {Roo.bootstrap.ComboBox} combo This combo box
10107              * @param {Roo.data.Record} record The data record returned from the underlying store
10108              * @param {Number} index The index of the selected item in the dropdown list
10109              */
10110         'beforeselect' : true,
10111         /**
10112          * @event select
10113          * Fires when a list item is selected
10114              * @param {Roo.bootstrap.ComboBox} combo This combo box
10115              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
10116              * @param {Number} index The index of the selected item in the dropdown list
10117              */
10118         'select' : true,
10119         /**
10120          * @event beforequery
10121          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
10122          * The event object passed has these properties:
10123              * @param {Roo.bootstrap.ComboBox} combo This combo box
10124              * @param {String} query The query
10125              * @param {Boolean} forceAll true to force "all" query
10126              * @param {Boolean} cancel true to cancel the query
10127              * @param {Object} e The query event object
10128              */
10129         'beforequery': true,
10130          /**
10131          * @event add
10132          * Fires when the 'add' icon is pressed (add a listener to enable add button)
10133              * @param {Roo.bootstrap.ComboBox} combo This combo box
10134              */
10135         'add' : true,
10136         /**
10137          * @event edit
10138          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
10139              * @param {Roo.bootstrap.ComboBox} combo This combo box
10140              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
10141              */
10142         'edit' : true,
10143         /**
10144          * @event remove
10145          * Fires when the remove value from the combobox array
10146              * @param {Roo.bootstrap.ComboBox} combo This combo box
10147              */
10148         'remove' : true
10149         
10150     });
10151     
10152     this.item = [];
10153     this.tickItems = [];
10154     
10155     this.selectedIndex = -1;
10156     if(this.mode == 'local'){
10157         if(config.queryDelay === undefined){
10158             this.queryDelay = 10;
10159         }
10160         if(config.minChars === undefined){
10161             this.minChars = 0;
10162         }
10163     }
10164 };
10165
10166 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
10167      
10168     /**
10169      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
10170      * rendering into an Roo.Editor, defaults to false)
10171      */
10172     /**
10173      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
10174      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
10175      */
10176     /**
10177      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
10178      */
10179     /**
10180      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
10181      * the dropdown list (defaults to undefined, with no header element)
10182      */
10183
10184      /**
10185      * @cfg {String/Roo.Template} tpl The template to use to render the output
10186      */
10187      
10188      /**
10189      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
10190      */
10191     listWidth: undefined,
10192     /**
10193      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
10194      * mode = 'remote' or 'text' if mode = 'local')
10195      */
10196     displayField: undefined,
10197     /**
10198      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
10199      * mode = 'remote' or 'value' if mode = 'local'). 
10200      * Note: use of a valueField requires the user make a selection
10201      * in order for a value to be mapped.
10202      */
10203     valueField: undefined,
10204     
10205     
10206     /**
10207      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
10208      * field's data value (defaults to the underlying DOM element's name)
10209      */
10210     hiddenName: undefined,
10211     /**
10212      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
10213      */
10214     listClass: '',
10215     /**
10216      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
10217      */
10218     selectedClass: 'active',
10219     
10220     /**
10221      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
10222      */
10223     shadow:'sides',
10224     /**
10225      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
10226      * anchor positions (defaults to 'tl-bl')
10227      */
10228     listAlign: 'tl-bl?',
10229     /**
10230      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
10231      */
10232     maxHeight: 300,
10233     /**
10234      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
10235      * query specified by the allQuery config option (defaults to 'query')
10236      */
10237     triggerAction: 'query',
10238     /**
10239      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
10240      * (defaults to 4, does not apply if editable = false)
10241      */
10242     minChars : 4,
10243     /**
10244      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
10245      * delay (typeAheadDelay) if it matches a known value (defaults to false)
10246      */
10247     typeAhead: false,
10248     /**
10249      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
10250      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
10251      */
10252     queryDelay: 500,
10253     /**
10254      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
10255      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
10256      */
10257     pageSize: 0,
10258     /**
10259      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
10260      * when editable = true (defaults to false)
10261      */
10262     selectOnFocus:false,
10263     /**
10264      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
10265      */
10266     queryParam: 'query',
10267     /**
10268      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
10269      * when mode = 'remote' (defaults to 'Loading...')
10270      */
10271     loadingText: 'Loading...',
10272     /**
10273      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
10274      */
10275     resizable: false,
10276     /**
10277      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
10278      */
10279     handleHeight : 8,
10280     /**
10281      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
10282      * traditional select (defaults to true)
10283      */
10284     editable: true,
10285     /**
10286      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
10287      */
10288     allQuery: '',
10289     /**
10290      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
10291      */
10292     mode: 'remote',
10293     /**
10294      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
10295      * listWidth has a higher value)
10296      */
10297     minListWidth : 70,
10298     /**
10299      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
10300      * allow the user to set arbitrary text into the field (defaults to false)
10301      */
10302     forceSelection:false,
10303     /**
10304      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
10305      * if typeAhead = true (defaults to 250)
10306      */
10307     typeAheadDelay : 250,
10308     /**
10309      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
10310      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
10311      */
10312     valueNotFoundText : undefined,
10313     /**
10314      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
10315      */
10316     blockFocus : false,
10317     
10318     /**
10319      * @cfg {Boolean} disableClear Disable showing of clear button.
10320      */
10321     disableClear : false,
10322     /**
10323      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
10324      */
10325     alwaysQuery : false,
10326     
10327     /**
10328      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
10329      */
10330     multiple : false,
10331     
10332     //private
10333     addicon : false,
10334     editicon: false,
10335     
10336     page: 0,
10337     hasQuery: false,
10338     append: false,
10339     loadNext: false,
10340     autoFocus : true,
10341     tickable : false,
10342     btnPosition : 'right',
10343     triggerList : true,
10344     // element that contains real text value.. (when hidden is used..)
10345     
10346     getAutoCreate : function()
10347     {
10348         var cfg = false;
10349         
10350         /*
10351          *  Normal ComboBox
10352          */
10353         if(!this.tickable){
10354             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
10355             return cfg;
10356         }
10357         
10358         /*
10359          *  ComboBox with tickable selections
10360          */
10361              
10362         var align = this.labelAlign || this.parentLabelAlign();
10363         
10364         cfg = {
10365             cls : 'form-group roo-combobox-tickable' //input-group
10366         };
10367         
10368         
10369         var buttons = {
10370             tag : 'div',
10371             cls : 'tickable-buttons',
10372             cn : [
10373                 {
10374                     tag : 'button',
10375                     type : 'button',
10376                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
10377                     html : 'Edit'
10378                 },
10379                 {
10380                     tag : 'button',
10381                     type : 'button',
10382                     name : 'ok',
10383                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
10384                     html : 'Done'
10385                 },
10386                 {
10387                     tag : 'button',
10388                     type : 'button',
10389                     name : 'cancel',
10390                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
10391                     html : 'Cancel'
10392                 }
10393             ]
10394         };
10395         
10396         var _this = this;
10397         Roo.each(buttons.cn, function(c){
10398             if (_this.size) {
10399                 c.cls += ' btn-' + _this.size;
10400             }
10401
10402             if (_this.disabled) {
10403                 c.disabled = true;
10404             }
10405         });
10406         
10407         var box = {
10408             tag: 'div',
10409             cn: [
10410                 {
10411                     tag: 'input',
10412                     type : 'hidden',
10413                     cls: 'form-hidden-field'
10414                 },
10415                 {
10416                     tag: 'ul',
10417                     cls: 'select2-choices',
10418                     cn:[
10419                         {
10420                             tag: 'li',
10421                             cls: 'select2-search-field',
10422                             cn: [
10423
10424                                 buttons
10425                             ]
10426                         }
10427                     ]
10428                 }
10429             ]
10430         }
10431         
10432         var combobox = {
10433             cls: 'select2-container input-group select2-container-multi',
10434             cn: [
10435                 box
10436 //                {
10437 //                    tag: 'ul',
10438 //                    cls: 'typeahead typeahead-long dropdown-menu',
10439 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
10440 //                }
10441             ]
10442         };
10443         
10444         if (align ==='left' && this.fieldLabel.length) {
10445             
10446                 Roo.log("left and has label");
10447                 cfg.cn = [
10448                     
10449                     {
10450                         tag: 'label',
10451                         'for' :  id,
10452                         cls : 'control-label col-sm-' + this.labelWidth,
10453                         html : this.fieldLabel
10454                         
10455                     },
10456                     {
10457                         cls : "col-sm-" + (12 - this.labelWidth), 
10458                         cn: [
10459                             combobox
10460                         ]
10461                     }
10462                     
10463                 ];
10464         } else if ( this.fieldLabel.length) {
10465                 Roo.log(" label");
10466                  cfg.cn = [
10467                    
10468                     {
10469                         tag: 'label',
10470                         //cls : 'input-group-addon',
10471                         html : this.fieldLabel
10472                         
10473                     },
10474                     
10475                     combobox
10476                     
10477                 ];
10478
10479         } else {
10480             
10481                 Roo.log(" no label && no align");
10482                 cfg = combobox
10483                      
10484                 
10485         }
10486          
10487         var settings=this;
10488         ['xs','sm','md','lg'].map(function(size){
10489             if (settings[size]) {
10490                 cfg.cls += ' col-' + size + '-' + settings[size];
10491             }
10492         });
10493         
10494         return cfg;
10495         
10496     },
10497     
10498     // private
10499     initEvents: function()
10500     {
10501         
10502         if (!this.store) {
10503             throw "can not find store for combo";
10504         }
10505         this.store = Roo.factory(this.store, Roo.data);
10506         
10507         if(this.tickable){
10508             this.initTickableEvnets();
10509             return;
10510         }
10511         
10512         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
10513         
10514         if(this.hiddenName){
10515             
10516             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10517             
10518             this.hiddenField.dom.value =
10519                 this.hiddenValue !== undefined ? this.hiddenValue :
10520                 this.value !== undefined ? this.value : '';
10521
10522             // prevent input submission
10523             this.el.dom.removeAttribute('name');
10524             this.hiddenField.dom.setAttribute('name', this.hiddenName);
10525              
10526              
10527         }
10528         //if(Roo.isGecko){
10529         //    this.el.dom.setAttribute('autocomplete', 'off');
10530         //}
10531         
10532         var cls = 'x-combo-list';
10533         
10534         //this.list = new Roo.Layer({
10535         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
10536         //});
10537         
10538         var _this = this;
10539         
10540         (function(){
10541             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
10542             _this.list.setWidth(lw);
10543         }).defer(100);
10544         
10545         this.list.on('mouseover', this.onViewOver, this);
10546         this.list.on('mousemove', this.onViewMove, this);
10547         
10548         this.list.on('scroll', this.onViewScroll, this);
10549         
10550         /*
10551         this.list.swallowEvent('mousewheel');
10552         this.assetHeight = 0;
10553
10554         if(this.title){
10555             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
10556             this.assetHeight += this.header.getHeight();
10557         }
10558
10559         this.innerList = this.list.createChild({cls:cls+'-inner'});
10560         this.innerList.on('mouseover', this.onViewOver, this);
10561         this.innerList.on('mousemove', this.onViewMove, this);
10562         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
10563         
10564         if(this.allowBlank && !this.pageSize && !this.disableClear){
10565             this.footer = this.list.createChild({cls:cls+'-ft'});
10566             this.pageTb = new Roo.Toolbar(this.footer);
10567            
10568         }
10569         if(this.pageSize){
10570             this.footer = this.list.createChild({cls:cls+'-ft'});
10571             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
10572                     {pageSize: this.pageSize});
10573             
10574         }
10575         
10576         if (this.pageTb && this.allowBlank && !this.disableClear) {
10577             var _this = this;
10578             this.pageTb.add(new Roo.Toolbar.Fill(), {
10579                 cls: 'x-btn-icon x-btn-clear',
10580                 text: '&#160;',
10581                 handler: function()
10582                 {
10583                     _this.collapse();
10584                     _this.clearValue();
10585                     _this.onSelect(false, -1);
10586                 }
10587             });
10588         }
10589         if (this.footer) {
10590             this.assetHeight += this.footer.getHeight();
10591         }
10592         */
10593             
10594         if(!this.tpl){
10595             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
10596         }
10597
10598         this.view = new Roo.View(this.list, this.tpl, {
10599             singleSelect:true, store: this.store, selectedClass: this.selectedClass
10600         });
10601         //this.view.wrapEl.setDisplayed(false);
10602         this.view.on('click', this.onViewClick, this);
10603         
10604         
10605         
10606         this.store.on('beforeload', this.onBeforeLoad, this);
10607         this.store.on('load', this.onLoad, this);
10608         this.store.on('loadexception', this.onLoadException, this);
10609         /*
10610         if(this.resizable){
10611             this.resizer = new Roo.Resizable(this.list,  {
10612                pinned:true, handles:'se'
10613             });
10614             this.resizer.on('resize', function(r, w, h){
10615                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
10616                 this.listWidth = w;
10617                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
10618                 this.restrictHeight();
10619             }, this);
10620             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
10621         }
10622         */
10623         if(!this.editable){
10624             this.editable = true;
10625             this.setEditable(false);
10626         }
10627         
10628         /*
10629         
10630         if (typeof(this.events.add.listeners) != 'undefined') {
10631             
10632             this.addicon = this.wrap.createChild(
10633                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
10634        
10635             this.addicon.on('click', function(e) {
10636                 this.fireEvent('add', this);
10637             }, this);
10638         }
10639         if (typeof(this.events.edit.listeners) != 'undefined') {
10640             
10641             this.editicon = this.wrap.createChild(
10642                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
10643             if (this.addicon) {
10644                 this.editicon.setStyle('margin-left', '40px');
10645             }
10646             this.editicon.on('click', function(e) {
10647                 
10648                 // we fire even  if inothing is selected..
10649                 this.fireEvent('edit', this, this.lastData );
10650                 
10651             }, this);
10652         }
10653         */
10654         
10655         this.keyNav = new Roo.KeyNav(this.inputEl(), {
10656             "up" : function(e){
10657                 this.inKeyMode = true;
10658                 this.selectPrev();
10659             },
10660
10661             "down" : function(e){
10662                 if(!this.isExpanded()){
10663                     this.onTriggerClick();
10664                 }else{
10665                     this.inKeyMode = true;
10666                     this.selectNext();
10667                 }
10668             },
10669
10670             "enter" : function(e){
10671 //                this.onViewClick();
10672                 //return true;
10673                 this.collapse();
10674                 
10675                 if(this.fireEvent("specialkey", this, e)){
10676                     this.onViewClick(false);
10677                 }
10678                 
10679                 return true;
10680             },
10681
10682             "esc" : function(e){
10683                 this.collapse();
10684             },
10685
10686             "tab" : function(e){
10687                 this.collapse();
10688                 
10689                 if(this.fireEvent("specialkey", this, e)){
10690                     this.onViewClick(false);
10691                 }
10692                 
10693                 return true;
10694             },
10695
10696             scope : this,
10697
10698             doRelay : function(foo, bar, hname){
10699                 if(hname == 'down' || this.scope.isExpanded()){
10700                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
10701                 }
10702                 return true;
10703             },
10704
10705             forceKeyDown: true
10706         });
10707         
10708         
10709         this.queryDelay = Math.max(this.queryDelay || 10,
10710                 this.mode == 'local' ? 10 : 250);
10711         
10712         
10713         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
10714         
10715         if(this.typeAhead){
10716             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
10717         }
10718         if(this.editable !== false){
10719             this.inputEl().on("keyup", this.onKeyUp, this);
10720         }
10721         if(this.forceSelection){
10722             this.inputEl().on('blur', this.doForce, this);
10723         }
10724         
10725         if(this.multiple){
10726             this.choices = this.el.select('ul.select2-choices', true).first();
10727             this.searchField = this.el.select('ul li.select2-search-field', true).first();
10728         }
10729     },
10730     
10731     initTickableEvnets: function()
10732     {   
10733         this.createList();
10734         
10735         if(this.hiddenName){
10736             
10737             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10738             
10739             this.hiddenField.dom.value =
10740                 this.hiddenValue !== undefined ? this.hiddenValue :
10741                 this.value !== undefined ? this.value : '';
10742
10743             // prevent input submission
10744             this.el.dom.removeAttribute('name');
10745             this.hiddenField.dom.setAttribute('name', this.hiddenName);
10746              
10747              
10748         }
10749         
10750 //        this.list = this.el.select('ul.dropdown-menu',true).first();
10751         
10752         this.choices = this.el.select('ul.select2-choices', true).first();
10753         this.searchField = this.el.select('ul li.select2-search-field', true).first();
10754         if(this.triggerList){
10755             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
10756         }
10757          
10758         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
10759         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
10760         
10761         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
10762         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
10763         
10764         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
10765         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
10766         
10767         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
10768         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
10769         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
10770         
10771         this.okBtn.hide();
10772         this.cancelBtn.hide();
10773         
10774         var _this = this;
10775         
10776         (function(){
10777             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
10778             _this.list.setWidth(lw);
10779         }).defer(100);
10780         
10781         this.list.on('mouseover', this.onViewOver, this);
10782         this.list.on('mousemove', this.onViewMove, this);
10783         
10784         this.list.on('scroll', this.onViewScroll, this);
10785         
10786         if(!this.tpl){
10787             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>';
10788         }
10789
10790         this.view = new Roo.View(this.list, this.tpl, {
10791             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
10792         });
10793         
10794         //this.view.wrapEl.setDisplayed(false);
10795         this.view.on('click', this.onViewClick, this);
10796         
10797         
10798         
10799         this.store.on('beforeload', this.onBeforeLoad, this);
10800         this.store.on('load', this.onLoad, this);
10801         this.store.on('loadexception', this.onLoadException, this);
10802         
10803 //        this.keyNav = new Roo.KeyNav(this.inputEl(), {
10804 //            "up" : function(e){
10805 //                this.inKeyMode = true;
10806 //                this.selectPrev();
10807 //            },
10808 //
10809 //            "down" : function(e){
10810 //                if(!this.isExpanded()){
10811 //                    this.onTriggerClick();
10812 //                }else{
10813 //                    this.inKeyMode = true;
10814 //                    this.selectNext();
10815 //                }
10816 //            },
10817 //
10818 //            "enter" : function(e){
10819 ////                this.onViewClick();
10820 //                //return true;
10821 //                this.collapse();
10822 //                
10823 //                if(this.fireEvent("specialkey", this, e)){
10824 //                    this.onViewClick(false);
10825 //                }
10826 //                
10827 //                return true;
10828 //            },
10829 //
10830 //            "esc" : function(e){
10831 //                this.collapse();
10832 //            },
10833 //
10834 //            "tab" : function(e){
10835 //                this.collapse();
10836 //                
10837 //                if(this.fireEvent("specialkey", this, e)){
10838 //                    this.onViewClick(false);
10839 //                }
10840 //                
10841 //                return true;
10842 //            },
10843 //
10844 //            scope : this,
10845 //
10846 //            doRelay : function(foo, bar, hname){
10847 //                if(hname == 'down' || this.scope.isExpanded()){
10848 //                   return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
10849 //                }
10850 //                return true;
10851 //            },
10852 //
10853 //            forceKeyDown: true
10854 //        });
10855         
10856         
10857         this.queryDelay = Math.max(this.queryDelay || 10,
10858                 this.mode == 'local' ? 10 : 250);
10859         
10860         
10861         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
10862         
10863         if(this.typeAhead){
10864             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
10865         }
10866     },
10867
10868     onDestroy : function(){
10869         if(this.view){
10870             this.view.setStore(null);
10871             this.view.el.removeAllListeners();
10872             this.view.el.remove();
10873             this.view.purgeListeners();
10874         }
10875         if(this.list){
10876             this.list.dom.innerHTML  = '';
10877         }
10878         
10879         if(this.store){
10880             this.store.un('beforeload', this.onBeforeLoad, this);
10881             this.store.un('load', this.onLoad, this);
10882             this.store.un('loadexception', this.onLoadException, this);
10883         }
10884         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
10885     },
10886
10887     // private
10888     fireKey : function(e){
10889         if(e.isNavKeyPress() && !this.list.isVisible()){
10890             this.fireEvent("specialkey", this, e);
10891         }
10892     },
10893
10894     // private
10895     onResize: function(w, h){
10896 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
10897 //        
10898 //        if(typeof w != 'number'){
10899 //            // we do not handle it!?!?
10900 //            return;
10901 //        }
10902 //        var tw = this.trigger.getWidth();
10903 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
10904 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
10905 //        var x = w - tw;
10906 //        this.inputEl().setWidth( this.adjustWidth('input', x));
10907 //            
10908 //        //this.trigger.setStyle('left', x+'px');
10909 //        
10910 //        if(this.list && this.listWidth === undefined){
10911 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
10912 //            this.list.setWidth(lw);
10913 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
10914 //        }
10915         
10916     
10917         
10918     },
10919
10920     /**
10921      * Allow or prevent the user from directly editing the field text.  If false is passed,
10922      * the user will only be able to select from the items defined in the dropdown list.  This method
10923      * is the runtime equivalent of setting the 'editable' config option at config time.
10924      * @param {Boolean} value True to allow the user to directly edit the field text
10925      */
10926     setEditable : function(value){
10927         if(value == this.editable){
10928             return;
10929         }
10930         this.editable = value;
10931         if(!value){
10932             this.inputEl().dom.setAttribute('readOnly', true);
10933             this.inputEl().on('mousedown', this.onTriggerClick,  this);
10934             this.inputEl().addClass('x-combo-noedit');
10935         }else{
10936             this.inputEl().dom.setAttribute('readOnly', false);
10937             this.inputEl().un('mousedown', this.onTriggerClick,  this);
10938             this.inputEl().removeClass('x-combo-noedit');
10939         }
10940     },
10941
10942     // private
10943     
10944     onBeforeLoad : function(combo,opts){
10945         if(!this.hasFocus){
10946             return;
10947         }
10948          if (!opts.add) {
10949             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
10950          }
10951 //        this.restrictHeight();
10952         this.selectedIndex = -1;
10953     },
10954
10955     // private
10956     onLoad : function(){
10957         
10958         this.hasQuery = false;
10959         
10960         if(!this.hasFocus){
10961             return;
10962         }
10963         
10964         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
10965             this.loading.hide();
10966         }
10967         
10968         if(this.store.getCount() > 0){
10969             this.expand();
10970 //            this.restrictHeight();
10971             if(this.lastQuery == this.allQuery){
10972                 if(this.editable && !this.tickable){
10973                     this.inputEl().dom.select();
10974                 }
10975                 if(!this.selectByValue(this.value, true) && this.autoFocus){
10976                     this.select(0, true);
10977                 }
10978             }else{
10979                 if(this.autoFocus){
10980                     this.selectNext();
10981                 }
10982                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
10983                     this.taTask.delay(this.typeAheadDelay);
10984                 }
10985             }
10986         }else{
10987             this.onEmptyResults();
10988         }
10989         
10990         //this.el.focus();
10991     },
10992     // private
10993     onLoadException : function()
10994     {
10995         this.hasQuery = false;
10996         
10997         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
10998             this.loading.hide();
10999         }
11000         
11001         this.collapse();
11002         Roo.log(this.store.reader.jsonData);
11003         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
11004             // fixme
11005             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
11006         }
11007         
11008         
11009     },
11010     // private
11011     onTypeAhead : function(){
11012         if(this.store.getCount() > 0){
11013             var r = this.store.getAt(0);
11014             var newValue = r.data[this.displayField];
11015             var len = newValue.length;
11016             var selStart = this.getRawValue().length;
11017             
11018             if(selStart != len){
11019                 this.setRawValue(newValue);
11020                 this.selectText(selStart, newValue.length);
11021             }
11022         }
11023     },
11024
11025     // private
11026     onSelect : function(record, index){
11027         
11028         if(this.fireEvent('beforeselect', this, record, index) !== false){
11029         
11030             this.setFromData(index > -1 ? record.data : false);
11031             
11032             this.collapse();
11033             this.fireEvent('select', this, record, index);
11034         }
11035     },
11036
11037     /**
11038      * Returns the currently selected field value or empty string if no value is set.
11039      * @return {String} value The selected value
11040      */
11041     getValue : function(){
11042         
11043         if(this.multiple){
11044             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
11045         }
11046         
11047         if(this.valueField){
11048             return typeof this.value != 'undefined' ? this.value : '';
11049         }else{
11050             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
11051         }
11052     },
11053
11054     /**
11055      * Clears any text/value currently set in the field
11056      */
11057     clearValue : function(){
11058         if(this.hiddenField){
11059             this.hiddenField.dom.value = '';
11060         }
11061         this.value = '';
11062         this.setRawValue('');
11063         this.lastSelectionText = '';
11064         
11065     },
11066
11067     /**
11068      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
11069      * will be displayed in the field.  If the value does not match the data value of an existing item,
11070      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
11071      * Otherwise the field will be blank (although the value will still be set).
11072      * @param {String} value The value to match
11073      */
11074     setValue : function(v){
11075         if(this.multiple){
11076             this.syncValue();
11077             return;
11078         }
11079         
11080         var text = v;
11081         if(this.valueField){
11082             var r = this.findRecord(this.valueField, v);
11083             if(r){
11084                 text = r.data[this.displayField];
11085             }else if(this.valueNotFoundText !== undefined){
11086                 text = this.valueNotFoundText;
11087             }
11088         }
11089         this.lastSelectionText = text;
11090         if(this.hiddenField){
11091             this.hiddenField.dom.value = v;
11092         }
11093         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
11094         this.value = v;
11095     },
11096     /**
11097      * @property {Object} the last set data for the element
11098      */
11099     
11100     lastData : false,
11101     /**
11102      * Sets the value of the field based on a object which is related to the record format for the store.
11103      * @param {Object} value the value to set as. or false on reset?
11104      */
11105     setFromData : function(o){
11106         
11107         if(this.multiple){
11108             // if(typeof o.display_name !== 'string'){
11109             //     for(var i=0;i<o.display_name.length;i++){
11110             //         this.addItem({'id':o.id[i],'display_name':o.display_name[i]});
11111             //     }
11112             // }
11113             this.addItem(o);
11114             return;
11115         }
11116             
11117         var dv = ''; // display value
11118         var vv = ''; // value value..
11119         this.lastData = o;
11120         if (this.displayField) {
11121             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11122         } else {
11123             // this is an error condition!!!
11124             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
11125         }
11126         
11127         if(this.valueField){
11128             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
11129         }
11130         
11131         if(this.hiddenField){
11132             this.hiddenField.dom.value = vv;
11133             
11134             this.lastSelectionText = dv;
11135             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11136             this.value = vv;
11137             return;
11138         }
11139         // no hidden field.. - we store the value in 'value', but still display
11140         // display field!!!!
11141         this.lastSelectionText = dv;
11142         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11143         this.value = vv;
11144         
11145         
11146     },
11147     // private
11148     reset : function(){
11149         // overridden so that last data is reset..
11150         this.setValue(this.originalValue);
11151         this.clearInvalid();
11152         this.lastData = false;
11153         if (this.view) {
11154             this.view.clearSelections();
11155         }
11156     },
11157     // private
11158     findRecord : function(prop, value){
11159         var record;
11160         if(this.store.getCount() > 0){
11161             this.store.each(function(r){
11162                 if(r.data[prop] == value){
11163                     record = r;
11164                     return false;
11165                 }
11166                 return true;
11167             });
11168         }
11169         return record;
11170     },
11171     
11172     getName: function()
11173     {
11174         // returns hidden if it's set..
11175         if (!this.rendered) {return ''};
11176         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
11177         
11178     },
11179     // private
11180     onViewMove : function(e, t){
11181         this.inKeyMode = false;
11182     },
11183
11184     // private
11185     onViewOver : function(e, t){
11186         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
11187             return;
11188         }
11189         var item = this.view.findItemFromChild(t);
11190         
11191         if(item){
11192             var index = this.view.indexOf(item);
11193             this.select(index, false);
11194         }
11195     },
11196
11197     // private
11198     onViewClick : function(view, doFocus, el, e)
11199     {
11200         var index = this.view.getSelectedIndexes()[0];
11201         
11202         var r = this.store.getAt(index);
11203         
11204         if(this.tickable){
11205             
11206             if(e.getTarget().nodeName.toLowerCase() != 'input'){
11207                 return;
11208             }
11209             
11210             var rm = false;
11211             var _this = this;
11212             
11213             Roo.each(this.tickItems, function(v,k){
11214                 
11215                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
11216                     _this.tickItems.splice(k, 1);
11217                     rm = true;
11218                     return;
11219                 }
11220             })
11221             
11222             if(rm){
11223                 return;
11224             }
11225             
11226             this.tickItems.push(r.data);
11227             return;
11228         }
11229         
11230         if(r){
11231             this.onSelect(r, index);
11232         }
11233         if(doFocus !== false && !this.blockFocus){
11234             this.inputEl().focus();
11235         }
11236     },
11237
11238     // private
11239     restrictHeight : function(){
11240         //this.innerList.dom.style.height = '';
11241         //var inner = this.innerList.dom;
11242         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
11243         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
11244         //this.list.beginUpdate();
11245         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
11246         this.list.alignTo(this.inputEl(), this.listAlign);
11247         this.list.alignTo(this.inputEl(), this.listAlign);
11248         //this.list.endUpdate();
11249     },
11250
11251     // private
11252     onEmptyResults : function(){
11253         this.collapse();
11254     },
11255
11256     /**
11257      * Returns true if the dropdown list is expanded, else false.
11258      */
11259     isExpanded : function(){
11260         return this.list.isVisible();
11261     },
11262
11263     /**
11264      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
11265      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11266      * @param {String} value The data value of the item to select
11267      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11268      * selected item if it is not currently in view (defaults to true)
11269      * @return {Boolean} True if the value matched an item in the list, else false
11270      */
11271     selectByValue : function(v, scrollIntoView){
11272         if(v !== undefined && v !== null){
11273             var r = this.findRecord(this.valueField || this.displayField, v);
11274             if(r){
11275                 this.select(this.store.indexOf(r), scrollIntoView);
11276                 return true;
11277             }
11278         }
11279         return false;
11280     },
11281
11282     /**
11283      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
11284      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11285      * @param {Number} index The zero-based index of the list item to select
11286      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11287      * selected item if it is not currently in view (defaults to true)
11288      */
11289     select : function(index, scrollIntoView){
11290         this.selectedIndex = index;
11291         this.view.select(index);
11292         if(scrollIntoView !== false){
11293             var el = this.view.getNode(index);
11294             if(el && !this.multiple && !this.tickable){
11295                 this.list.scrollChildIntoView(el, false);
11296             }
11297         }
11298     },
11299
11300     // private
11301     selectNext : function(){
11302         var ct = this.store.getCount();
11303         if(ct > 0){
11304             if(this.selectedIndex == -1){
11305                 this.select(0);
11306             }else if(this.selectedIndex < ct-1){
11307                 this.select(this.selectedIndex+1);
11308             }
11309         }
11310     },
11311
11312     // private
11313     selectPrev : function(){
11314         var ct = this.store.getCount();
11315         if(ct > 0){
11316             if(this.selectedIndex == -1){
11317                 this.select(0);
11318             }else if(this.selectedIndex != 0){
11319                 this.select(this.selectedIndex-1);
11320             }
11321         }
11322     },
11323
11324     // private
11325     onKeyUp : function(e){
11326         if(this.editable !== false && !e.isSpecialKey()){
11327             this.lastKey = e.getKey();
11328             this.dqTask.delay(this.queryDelay);
11329         }
11330     },
11331
11332     // private
11333     validateBlur : function(){
11334         return !this.list || !this.list.isVisible();   
11335     },
11336
11337     // private
11338     initQuery : function(){
11339         this.doQuery(this.getRawValue());
11340     },
11341
11342     // private
11343     doForce : function(){
11344         if(this.inputEl().dom.value.length > 0){
11345             this.inputEl().dom.value =
11346                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
11347              
11348         }
11349     },
11350
11351     /**
11352      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
11353      * query allowing the query action to be canceled if needed.
11354      * @param {String} query The SQL query to execute
11355      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
11356      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
11357      * saved in the current store (defaults to false)
11358      */
11359     doQuery : function(q, forceAll){
11360         
11361         if(q === undefined || q === null){
11362             q = '';
11363         }
11364         var qe = {
11365             query: q,
11366             forceAll: forceAll,
11367             combo: this,
11368             cancel:false
11369         };
11370         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
11371             return false;
11372         }
11373         q = qe.query;
11374         
11375         forceAll = qe.forceAll;
11376         if(forceAll === true || (q.length >= this.minChars)){
11377             
11378             this.hasQuery = true;
11379             
11380             if(this.lastQuery != q || this.alwaysQuery){
11381                 this.lastQuery = q;
11382                 if(this.mode == 'local'){
11383                     this.selectedIndex = -1;
11384                     if(forceAll){
11385                         this.store.clearFilter();
11386                     }else{
11387                         this.store.filter(this.displayField, q);
11388                     }
11389                     this.onLoad();
11390                 }else{
11391                     this.store.baseParams[this.queryParam] = q;
11392                     
11393                     var options = {params : this.getParams(q)};
11394                     
11395                     if(this.loadNext){
11396                         options.add = true;
11397                         options.params.start = this.page * this.pageSize;
11398                     }
11399                     
11400                     this.store.load(options);
11401                     /*
11402                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
11403                      *  we should expand the list on onLoad
11404                      *  so command out it
11405                      */
11406 //                    this.expand();
11407                 }
11408             }else{
11409                 this.selectedIndex = -1;
11410                 this.onLoad();   
11411             }
11412         }
11413         
11414         this.loadNext = false;
11415     },
11416
11417     // private
11418     getParams : function(q){
11419         var p = {};
11420         //p[this.queryParam] = q;
11421         
11422         if(this.pageSize){
11423             p.start = 0;
11424             p.limit = this.pageSize;
11425         }
11426         return p;
11427     },
11428
11429     /**
11430      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
11431      */
11432     collapse : function(){
11433         if(!this.isExpanded()){
11434             return;
11435         }
11436         
11437         this.hasFocus = false;
11438         
11439         this.list.hide();
11440         
11441         if(this.tickable){
11442             this.okBtn.hide();
11443             this.cancelBtn.hide();
11444             this.trigger.show();
11445         }
11446         
11447         Roo.get(document).un('mousedown', this.collapseIf, this);
11448         Roo.get(document).un('mousewheel', this.collapseIf, this);
11449         if (!this.editable) {
11450             Roo.get(document).un('keydown', this.listKeyPress, this);
11451         }
11452         this.fireEvent('collapse', this);
11453     },
11454
11455     // private
11456     collapseIf : function(e){
11457         var in_combo  = e.within(this.el);
11458         var in_list =  e.within(this.list);
11459         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
11460         
11461         if (in_combo || in_list || is_list) {
11462             //e.stopPropagation();
11463             return;
11464         }
11465         
11466         if(this.tickable){
11467             this.onTickableFooterButtonClick(e, false, false);
11468         }
11469
11470         this.collapse();
11471         
11472     },
11473
11474     /**
11475      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
11476      */
11477     expand : function(){
11478        
11479         if(this.isExpanded() || !this.hasFocus){
11480             return;
11481         }
11482          Roo.log('expand');
11483         
11484         this.list.show();
11485         
11486         this.restrictHeight();
11487         
11488         if(this.tickable){
11489             
11490             this.tickItems = Roo.apply([], this.item);
11491             
11492             this.okBtn.show();
11493             this.cancelBtn.show();
11494             this.trigger.hide();
11495             
11496         }
11497         
11498         Roo.get(document).on('mousedown', this.collapseIf, this);
11499         Roo.get(document).on('mousewheel', this.collapseIf, this);
11500         if (!this.editable) {
11501             Roo.get(document).on('keydown', this.listKeyPress, this);
11502         }
11503         
11504         this.fireEvent('expand', this);
11505     },
11506
11507     // private
11508     // Implements the default empty TriggerField.onTriggerClick function
11509     onTriggerClick : function(e)
11510     {
11511         Roo.log('trigger click');
11512         
11513         if(this.disabled || !this.triggerList){
11514             return;
11515         }
11516         
11517         this.page = 0;
11518         this.loadNext = false;
11519         
11520         if(this.isExpanded()){
11521             this.collapse();
11522             if (!this.blockFocus) {
11523                 this.inputEl().focus();
11524             }
11525             
11526         }else {
11527             this.hasFocus = true;
11528             if(this.triggerAction == 'all') {
11529                 this.doQuery(this.allQuery, true);
11530             } else {
11531                 this.doQuery(this.getRawValue());
11532             }
11533             if (!this.blockFocus) {
11534                 this.inputEl().focus();
11535             }
11536         }
11537     },
11538     
11539     onTickableTriggerClick : function(e)
11540     {
11541         if(this.disabled){
11542             return;
11543         }
11544         
11545         this.page = 0;
11546         this.loadNext = false;
11547         this.hasFocus = true;
11548         
11549         if(this.triggerAction == 'all') {
11550             this.doQuery(this.allQuery, true);
11551         } else {
11552             this.doQuery(this.getRawValue());
11553         }
11554     },
11555     
11556     onSearchFieldClick : function(e)
11557     {
11558         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
11559             return;
11560         }
11561         
11562         this.page = 0;
11563         this.loadNext = false;
11564         this.hasFocus = true;
11565         
11566         if(this.triggerAction == 'all') {
11567             this.doQuery(this.allQuery, true);
11568         } else {
11569             this.doQuery(this.getRawValue());
11570         }
11571     },
11572     
11573     listKeyPress : function(e)
11574     {
11575         //Roo.log('listkeypress');
11576         // scroll to first matching element based on key pres..
11577         if (e.isSpecialKey()) {
11578             return false;
11579         }
11580         var k = String.fromCharCode(e.getKey()).toUpperCase();
11581         //Roo.log(k);
11582         var match  = false;
11583         var csel = this.view.getSelectedNodes();
11584         var cselitem = false;
11585         if (csel.length) {
11586             var ix = this.view.indexOf(csel[0]);
11587             cselitem  = this.store.getAt(ix);
11588             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
11589                 cselitem = false;
11590             }
11591             
11592         }
11593         
11594         this.store.each(function(v) { 
11595             if (cselitem) {
11596                 // start at existing selection.
11597                 if (cselitem.id == v.id) {
11598                     cselitem = false;
11599                 }
11600                 return true;
11601             }
11602                 
11603             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
11604                 match = this.store.indexOf(v);
11605                 return false;
11606             }
11607             return true;
11608         }, this);
11609         
11610         if (match === false) {
11611             return true; // no more action?
11612         }
11613         // scroll to?
11614         this.view.select(match);
11615         var sn = Roo.get(this.view.getSelectedNodes()[0])
11616         //sn.scrollIntoView(sn.dom.parentNode, false);
11617     },
11618     
11619     onViewScroll : function(e, t){
11620         
11621         if(this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
11622             return;
11623         }
11624         
11625         this.hasQuery = true;
11626         
11627         this.loading = this.list.select('.loading', true).first();
11628         
11629         if(this.loading === null){
11630             this.list.createChild({
11631                 tag: 'div',
11632                 cls: 'loading select2-more-results select2-active',
11633                 html: 'Loading more results...'
11634             })
11635             
11636             this.loading = this.list.select('.loading', true).first();
11637             
11638             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
11639             
11640             this.loading.hide();
11641         }
11642         
11643         this.loading.show();
11644         
11645         var _combo = this;
11646         
11647         this.page++;
11648         this.loadNext = true;
11649         
11650         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
11651         
11652         return;
11653     },
11654     
11655     addItem : function(o)
11656     {   
11657         var dv = ''; // display value
11658         
11659         if (this.displayField) {
11660             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11661         } else {
11662             // this is an error condition!!!
11663             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
11664         }
11665         
11666         if(!dv.length){
11667             return;
11668         }
11669         
11670         var choice = this.choices.createChild({
11671             tag: 'li',
11672             cls: 'select2-search-choice',
11673             cn: [
11674                 {
11675                     tag: 'div',
11676                     html: dv
11677                 },
11678                 {
11679                     tag: 'a',
11680                     href: '#',
11681                     cls: 'select2-search-choice-close',
11682                     tabindex: '-1'
11683                 }
11684             ]
11685             
11686         }, this.searchField);
11687         
11688         var close = choice.select('a.select2-search-choice-close', true).first()
11689         
11690         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
11691         
11692         this.item.push(o);
11693         
11694         this.lastData = o;
11695         
11696         this.syncValue();
11697         
11698         this.inputEl().dom.value = '';
11699         
11700     },
11701     
11702     onRemoveItem : function(e, _self, o)
11703     {
11704         e.preventDefault();
11705         var index = this.item.indexOf(o.data) * 1;
11706         
11707         if( index < 0){
11708             Roo.log('not this item?!');
11709             return;
11710         }
11711         
11712         this.item.splice(index, 1);
11713         o.item.remove();
11714         
11715         this.syncValue();
11716         
11717         this.fireEvent('remove', this, e);
11718         
11719     },
11720     
11721     syncValue : function()
11722     {
11723         if(!this.item.length){
11724             this.clearValue();
11725             return;
11726         }
11727             
11728         var value = [];
11729         var _this = this;
11730         Roo.each(this.item, function(i){
11731             if(_this.valueField){
11732                 value.push(i[_this.valueField]);
11733                 return;
11734             }
11735
11736             value.push(i);
11737         });
11738
11739         this.value = value.join(',');
11740
11741         if(this.hiddenField){
11742             this.hiddenField.dom.value = this.value;
11743         }
11744     },
11745     
11746     clearItem : function()
11747     {
11748         if(!this.multiple){
11749             return;
11750         }
11751         
11752         this.item = [];
11753         
11754         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
11755            c.remove();
11756         });
11757         
11758         this.syncValue();
11759     },
11760     
11761     inputEl: function ()
11762     {
11763         if(this.tickable){
11764             return this.searchField;
11765         }
11766         return this.el.select('input.form-control',true).first();
11767     },
11768     
11769     
11770     onTickableFooterButtonClick : function(e, btn, el)
11771     {
11772         e.preventDefault();
11773         
11774         if(btn && btn.name == 'cancel'){
11775             this.tickItems = Roo.apply([], this.item);
11776             this.collapse();
11777             return;
11778         }
11779         
11780         this.clearItem();
11781         
11782         var _this = this;
11783         
11784         Roo.each(this.tickItems, function(o){
11785             _this.addItem(o);
11786         });
11787         
11788         this.collapse();
11789         
11790     }
11791     
11792     
11793
11794     /** 
11795     * @cfg {Boolean} grow 
11796     * @hide 
11797     */
11798     /** 
11799     * @cfg {Number} growMin 
11800     * @hide 
11801     */
11802     /** 
11803     * @cfg {Number} growMax 
11804     * @hide 
11805     */
11806     /**
11807      * @hide
11808      * @method autoSize
11809      */
11810 });
11811 /*
11812  * Based on:
11813  * Ext JS Library 1.1.1
11814  * Copyright(c) 2006-2007, Ext JS, LLC.
11815  *
11816  * Originally Released Under LGPL - original licence link has changed is not relivant.
11817  *
11818  * Fork - LGPL
11819  * <script type="text/javascript">
11820  */
11821
11822 /**
11823  * @class Roo.View
11824  * @extends Roo.util.Observable
11825  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
11826  * This class also supports single and multi selection modes. <br>
11827  * Create a data model bound view:
11828  <pre><code>
11829  var store = new Roo.data.Store(...);
11830
11831  var view = new Roo.View({
11832     el : "my-element",
11833     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
11834  
11835     singleSelect: true,
11836     selectedClass: "ydataview-selected",
11837     store: store
11838  });
11839
11840  // listen for node click?
11841  view.on("click", function(vw, index, node, e){
11842  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
11843  });
11844
11845  // load XML data
11846  dataModel.load("foobar.xml");
11847  </code></pre>
11848  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
11849  * <br><br>
11850  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
11851  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
11852  * 
11853  * Note: old style constructor is still suported (container, template, config)
11854  * 
11855  * @constructor
11856  * Create a new View
11857  * @param {Object} config The config object
11858  * 
11859  */
11860 Roo.View = function(config, depreciated_tpl, depreciated_config){
11861     
11862     this.parent = false;
11863     
11864     if (typeof(depreciated_tpl) == 'undefined') {
11865         // new way.. - universal constructor.
11866         Roo.apply(this, config);
11867         this.el  = Roo.get(this.el);
11868     } else {
11869         // old format..
11870         this.el  = Roo.get(config);
11871         this.tpl = depreciated_tpl;
11872         Roo.apply(this, depreciated_config);
11873     }
11874     this.wrapEl  = this.el.wrap().wrap();
11875     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
11876     
11877     
11878     if(typeof(this.tpl) == "string"){
11879         this.tpl = new Roo.Template(this.tpl);
11880     } else {
11881         // support xtype ctors..
11882         this.tpl = new Roo.factory(this.tpl, Roo);
11883     }
11884     
11885     
11886     this.tpl.compile();
11887     
11888     /** @private */
11889     this.addEvents({
11890         /**
11891          * @event beforeclick
11892          * Fires before a click is processed. Returns false to cancel the default action.
11893          * @param {Roo.View} this
11894          * @param {Number} index The index of the target node
11895          * @param {HTMLElement} node The target node
11896          * @param {Roo.EventObject} e The raw event object
11897          */
11898             "beforeclick" : true,
11899         /**
11900          * @event click
11901          * Fires when a template node is clicked.
11902          * @param {Roo.View} this
11903          * @param {Number} index The index of the target node
11904          * @param {HTMLElement} node The target node
11905          * @param {Roo.EventObject} e The raw event object
11906          */
11907             "click" : true,
11908         /**
11909          * @event dblclick
11910          * Fires when a template node is double clicked.
11911          * @param {Roo.View} this
11912          * @param {Number} index The index of the target node
11913          * @param {HTMLElement} node The target node
11914          * @param {Roo.EventObject} e The raw event object
11915          */
11916             "dblclick" : true,
11917         /**
11918          * @event contextmenu
11919          * Fires when a template node is right clicked.
11920          * @param {Roo.View} this
11921          * @param {Number} index The index of the target node
11922          * @param {HTMLElement} node The target node
11923          * @param {Roo.EventObject} e The raw event object
11924          */
11925             "contextmenu" : true,
11926         /**
11927          * @event selectionchange
11928          * Fires when the selected nodes change.
11929          * @param {Roo.View} this
11930          * @param {Array} selections Array of the selected nodes
11931          */
11932             "selectionchange" : true,
11933     
11934         /**
11935          * @event beforeselect
11936          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
11937          * @param {Roo.View} this
11938          * @param {HTMLElement} node The node to be selected
11939          * @param {Array} selections Array of currently selected nodes
11940          */
11941             "beforeselect" : true,
11942         /**
11943          * @event preparedata
11944          * Fires on every row to render, to allow you to change the data.
11945          * @param {Roo.View} this
11946          * @param {Object} data to be rendered (change this)
11947          */
11948           "preparedata" : true
11949           
11950           
11951         });
11952
11953
11954
11955     this.el.on({
11956         "click": this.onClick,
11957         "dblclick": this.onDblClick,
11958         "contextmenu": this.onContextMenu,
11959         scope:this
11960     });
11961
11962     this.selections = [];
11963     this.nodes = [];
11964     this.cmp = new Roo.CompositeElementLite([]);
11965     if(this.store){
11966         this.store = Roo.factory(this.store, Roo.data);
11967         this.setStore(this.store, true);
11968     }
11969     
11970     if ( this.footer && this.footer.xtype) {
11971            
11972          var fctr = this.wrapEl.appendChild(document.createElement("div"));
11973         
11974         this.footer.dataSource = this.store
11975         this.footer.container = fctr;
11976         this.footer = Roo.factory(this.footer, Roo);
11977         fctr.insertFirst(this.el);
11978         
11979         // this is a bit insane - as the paging toolbar seems to detach the el..
11980 //        dom.parentNode.parentNode.parentNode
11981          // they get detached?
11982     }
11983     
11984     
11985     Roo.View.superclass.constructor.call(this);
11986     
11987     
11988 };
11989
11990 Roo.extend(Roo.View, Roo.util.Observable, {
11991     
11992      /**
11993      * @cfg {Roo.data.Store} store Data store to load data from.
11994      */
11995     store : false,
11996     
11997     /**
11998      * @cfg {String|Roo.Element} el The container element.
11999      */
12000     el : '',
12001     
12002     /**
12003      * @cfg {String|Roo.Template} tpl The template used by this View 
12004      */
12005     tpl : false,
12006     /**
12007      * @cfg {String} dataName the named area of the template to use as the data area
12008      *                          Works with domtemplates roo-name="name"
12009      */
12010     dataName: false,
12011     /**
12012      * @cfg {String} selectedClass The css class to add to selected nodes
12013      */
12014     selectedClass : "x-view-selected",
12015      /**
12016      * @cfg {String} emptyText The empty text to show when nothing is loaded.
12017      */
12018     emptyText : "",
12019     
12020     /**
12021      * @cfg {String} text to display on mask (default Loading)
12022      */
12023     mask : false,
12024     /**
12025      * @cfg {Boolean} multiSelect Allow multiple selection
12026      */
12027     multiSelect : false,
12028     /**
12029      * @cfg {Boolean} singleSelect Allow single selection
12030      */
12031     singleSelect:  false,
12032     
12033     /**
12034      * @cfg {Boolean} toggleSelect - selecting 
12035      */
12036     toggleSelect : false,
12037     
12038     /**
12039      * @cfg {Boolean} tickable - selecting 
12040      */
12041     tickable : false,
12042     
12043     /**
12044      * Returns the element this view is bound to.
12045      * @return {Roo.Element}
12046      */
12047     getEl : function(){
12048         return this.wrapEl;
12049     },
12050     
12051     
12052
12053     /**
12054      * Refreshes the view. - called by datachanged on the store. - do not call directly.
12055      */
12056     refresh : function(){
12057         Roo.log('refresh');
12058         var t = this.tpl;
12059         
12060         // if we are using something like 'domtemplate', then
12061         // the what gets used is:
12062         // t.applySubtemplate(NAME, data, wrapping data..)
12063         // the outer template then get' applied with
12064         //     the store 'extra data'
12065         // and the body get's added to the
12066         //      roo-name="data" node?
12067         //      <span class='roo-tpl-{name}'></span> ?????
12068         
12069         
12070         
12071         this.clearSelections();
12072         this.el.update("");
12073         var html = [];
12074         var records = this.store.getRange();
12075         if(records.length < 1) {
12076             
12077             // is this valid??  = should it render a template??
12078             
12079             this.el.update(this.emptyText);
12080             return;
12081         }
12082         var el = this.el;
12083         if (this.dataName) {
12084             this.el.update(t.apply(this.store.meta)); //????
12085             el = this.el.child('.roo-tpl-' + this.dataName);
12086         }
12087         
12088         for(var i = 0, len = records.length; i < len; i++){
12089             var data = this.prepareData(records[i].data, i, records[i]);
12090             this.fireEvent("preparedata", this, data, i, records[i]);
12091             
12092             var d = Roo.apply({}, data);
12093             
12094             if(this.tickable){
12095                 Roo.apply(d, {'roo-id' : Roo.id()});
12096                 
12097                 var _this = this;
12098             
12099                 Roo.each(this.parent.item, function(item){
12100                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
12101                         return;
12102                     }
12103                     Roo.apply(d, {'roo-data-checked' : 'checked'});
12104                 });
12105             }
12106             
12107             html[html.length] = Roo.util.Format.trim(
12108                 this.dataName ?
12109                     t.applySubtemplate(this.dataName, d, this.store.meta) :
12110                     t.apply(d)
12111             );
12112         }
12113         
12114         
12115         
12116         el.update(html.join(""));
12117         this.nodes = el.dom.childNodes;
12118         this.updateIndexes(0);
12119     },
12120     
12121
12122     /**
12123      * Function to override to reformat the data that is sent to
12124      * the template for each node.
12125      * DEPRICATED - use the preparedata event handler.
12126      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
12127      * a JSON object for an UpdateManager bound view).
12128      */
12129     prepareData : function(data, index, record)
12130     {
12131         this.fireEvent("preparedata", this, data, index, record);
12132         return data;
12133     },
12134
12135     onUpdate : function(ds, record){
12136          Roo.log('on update');   
12137         this.clearSelections();
12138         var index = this.store.indexOf(record);
12139         var n = this.nodes[index];
12140         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
12141         n.parentNode.removeChild(n);
12142         this.updateIndexes(index, index);
12143     },
12144
12145     
12146     
12147 // --------- FIXME     
12148     onAdd : function(ds, records, index)
12149     {
12150         Roo.log(['on Add', ds, records, index] );        
12151         this.clearSelections();
12152         if(this.nodes.length == 0){
12153             this.refresh();
12154             return;
12155         }
12156         var n = this.nodes[index];
12157         for(var i = 0, len = records.length; i < len; i++){
12158             var d = this.prepareData(records[i].data, i, records[i]);
12159             if(n){
12160                 this.tpl.insertBefore(n, d);
12161             }else{
12162                 
12163                 this.tpl.append(this.el, d);
12164             }
12165         }
12166         this.updateIndexes(index);
12167     },
12168
12169     onRemove : function(ds, record, index){
12170         Roo.log('onRemove');
12171         this.clearSelections();
12172         var el = this.dataName  ?
12173             this.el.child('.roo-tpl-' + this.dataName) :
12174             this.el; 
12175         
12176         el.dom.removeChild(this.nodes[index]);
12177         this.updateIndexes(index);
12178     },
12179
12180     /**
12181      * Refresh an individual node.
12182      * @param {Number} index
12183      */
12184     refreshNode : function(index){
12185         this.onUpdate(this.store, this.store.getAt(index));
12186     },
12187
12188     updateIndexes : function(startIndex, endIndex){
12189         var ns = this.nodes;
12190         startIndex = startIndex || 0;
12191         endIndex = endIndex || ns.length - 1;
12192         for(var i = startIndex; i <= endIndex; i++){
12193             ns[i].nodeIndex = i;
12194         }
12195     },
12196
12197     /**
12198      * Changes the data store this view uses and refresh the view.
12199      * @param {Store} store
12200      */
12201     setStore : function(store, initial){
12202         if(!initial && this.store){
12203             this.store.un("datachanged", this.refresh);
12204             this.store.un("add", this.onAdd);
12205             this.store.un("remove", this.onRemove);
12206             this.store.un("update", this.onUpdate);
12207             this.store.un("clear", this.refresh);
12208             this.store.un("beforeload", this.onBeforeLoad);
12209             this.store.un("load", this.onLoad);
12210             this.store.un("loadexception", this.onLoad);
12211         }
12212         if(store){
12213           
12214             store.on("datachanged", this.refresh, this);
12215             store.on("add", this.onAdd, this);
12216             store.on("remove", this.onRemove, this);
12217             store.on("update", this.onUpdate, this);
12218             store.on("clear", this.refresh, this);
12219             store.on("beforeload", this.onBeforeLoad, this);
12220             store.on("load", this.onLoad, this);
12221             store.on("loadexception", this.onLoad, this);
12222         }
12223         
12224         if(store){
12225             this.refresh();
12226         }
12227     },
12228     /**
12229      * onbeforeLoad - masks the loading area.
12230      *
12231      */
12232     onBeforeLoad : function(store,opts)
12233     {
12234          Roo.log('onBeforeLoad');   
12235         if (!opts.add) {
12236             this.el.update("");
12237         }
12238         this.el.mask(this.mask ? this.mask : "Loading" ); 
12239     },
12240     onLoad : function ()
12241     {
12242         this.el.unmask();
12243     },
12244     
12245
12246     /**
12247      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
12248      * @param {HTMLElement} node
12249      * @return {HTMLElement} The template node
12250      */
12251     findItemFromChild : function(node){
12252         var el = this.dataName  ?
12253             this.el.child('.roo-tpl-' + this.dataName,true) :
12254             this.el.dom; 
12255         
12256         if(!node || node.parentNode == el){
12257                     return node;
12258             }
12259             var p = node.parentNode;
12260             while(p && p != el){
12261             if(p.parentNode == el){
12262                 return p;
12263             }
12264             p = p.parentNode;
12265         }
12266             return null;
12267     },
12268
12269     /** @ignore */
12270     onClick : function(e){
12271         var item = this.findItemFromChild(e.getTarget());
12272         if(item){
12273             var index = this.indexOf(item);
12274             if(this.onItemClick(item, index, e) !== false){
12275                 this.fireEvent("click", this, index, item, e);
12276             }
12277         }else{
12278             this.clearSelections();
12279         }
12280     },
12281
12282     /** @ignore */
12283     onContextMenu : function(e){
12284         var item = this.findItemFromChild(e.getTarget());
12285         if(item){
12286             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
12287         }
12288     },
12289
12290     /** @ignore */
12291     onDblClick : function(e){
12292         var item = this.findItemFromChild(e.getTarget());
12293         if(item){
12294             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
12295         }
12296     },
12297
12298     onItemClick : function(item, index, e)
12299     {
12300         if(this.fireEvent("beforeclick", this, index, item, e) === false){
12301             return false;
12302         }
12303         if (this.toggleSelect) {
12304             var m = this.isSelected(item) ? 'unselect' : 'select';
12305             Roo.log(m);
12306             var _t = this;
12307             _t[m](item, true, false);
12308             return true;
12309         }
12310         if(this.multiSelect || this.singleSelect){
12311             if(this.multiSelect && e.shiftKey && this.lastSelection){
12312                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
12313             }else{
12314                 this.select(item, this.multiSelect && e.ctrlKey);
12315                 this.lastSelection = item;
12316             }
12317             
12318             if(!this.tickable){
12319                 e.preventDefault();
12320             }
12321             
12322         }
12323         return true;
12324     },
12325
12326     /**
12327      * Get the number of selected nodes.
12328      * @return {Number}
12329      */
12330     getSelectionCount : function(){
12331         return this.selections.length;
12332     },
12333
12334     /**
12335      * Get the currently selected nodes.
12336      * @return {Array} An array of HTMLElements
12337      */
12338     getSelectedNodes : function(){
12339         return this.selections;
12340     },
12341
12342     /**
12343      * Get the indexes of the selected nodes.
12344      * @return {Array}
12345      */
12346     getSelectedIndexes : function(){
12347         var indexes = [], s = this.selections;
12348         for(var i = 0, len = s.length; i < len; i++){
12349             indexes.push(s[i].nodeIndex);
12350         }
12351         return indexes;
12352     },
12353
12354     /**
12355      * Clear all selections
12356      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
12357      */
12358     clearSelections : function(suppressEvent){
12359         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
12360             this.cmp.elements = this.selections;
12361             this.cmp.removeClass(this.selectedClass);
12362             this.selections = [];
12363             if(!suppressEvent){
12364                 this.fireEvent("selectionchange", this, this.selections);
12365             }
12366         }
12367     },
12368
12369     /**
12370      * Returns true if the passed node is selected
12371      * @param {HTMLElement/Number} node The node or node index
12372      * @return {Boolean}
12373      */
12374     isSelected : function(node){
12375         var s = this.selections;
12376         if(s.length < 1){
12377             return false;
12378         }
12379         node = this.getNode(node);
12380         return s.indexOf(node) !== -1;
12381     },
12382
12383     /**
12384      * Selects nodes.
12385      * @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
12386      * @param {Boolean} keepExisting (optional) true to keep existing selections
12387      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12388      */
12389     select : function(nodeInfo, keepExisting, suppressEvent){
12390         if(nodeInfo instanceof Array){
12391             if(!keepExisting){
12392                 this.clearSelections(true);
12393             }
12394             for(var i = 0, len = nodeInfo.length; i < len; i++){
12395                 this.select(nodeInfo[i], true, true);
12396             }
12397             return;
12398         } 
12399         var node = this.getNode(nodeInfo);
12400         if(!node || this.isSelected(node)){
12401             return; // already selected.
12402         }
12403         if(!keepExisting){
12404             this.clearSelections(true);
12405         }
12406         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
12407             Roo.fly(node).addClass(this.selectedClass);
12408             this.selections.push(node);
12409             if(!suppressEvent){
12410                 this.fireEvent("selectionchange", this, this.selections);
12411             }
12412         }
12413         
12414         
12415     },
12416       /**
12417      * Unselects nodes.
12418      * @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
12419      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
12420      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12421      */
12422     unselect : function(nodeInfo, keepExisting, suppressEvent)
12423     {
12424         if(nodeInfo instanceof Array){
12425             Roo.each(this.selections, function(s) {
12426                 this.unselect(s, nodeInfo);
12427             }, this);
12428             return;
12429         }
12430         var node = this.getNode(nodeInfo);
12431         if(!node || !this.isSelected(node)){
12432             Roo.log("not selected");
12433             return; // not selected.
12434         }
12435         // fireevent???
12436         var ns = [];
12437         Roo.each(this.selections, function(s) {
12438             if (s == node ) {
12439                 Roo.fly(node).removeClass(this.selectedClass);
12440
12441                 return;
12442             }
12443             ns.push(s);
12444         },this);
12445         
12446         this.selections= ns;
12447         this.fireEvent("selectionchange", this, this.selections);
12448     },
12449
12450     /**
12451      * Gets a template node.
12452      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12453      * @return {HTMLElement} The node or null if it wasn't found
12454      */
12455     getNode : function(nodeInfo){
12456         if(typeof nodeInfo == "string"){
12457             return document.getElementById(nodeInfo);
12458         }else if(typeof nodeInfo == "number"){
12459             return this.nodes[nodeInfo];
12460         }
12461         return nodeInfo;
12462     },
12463
12464     /**
12465      * Gets a range template nodes.
12466      * @param {Number} startIndex
12467      * @param {Number} endIndex
12468      * @return {Array} An array of nodes
12469      */
12470     getNodes : function(start, end){
12471         var ns = this.nodes;
12472         start = start || 0;
12473         end = typeof end == "undefined" ? ns.length - 1 : end;
12474         var nodes = [];
12475         if(start <= end){
12476             for(var i = start; i <= end; i++){
12477                 nodes.push(ns[i]);
12478             }
12479         } else{
12480             for(var i = start; i >= end; i--){
12481                 nodes.push(ns[i]);
12482             }
12483         }
12484         return nodes;
12485     },
12486
12487     /**
12488      * Finds the index of the passed node
12489      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12490      * @return {Number} The index of the node or -1
12491      */
12492     indexOf : function(node){
12493         node = this.getNode(node);
12494         if(typeof node.nodeIndex == "number"){
12495             return node.nodeIndex;
12496         }
12497         var ns = this.nodes;
12498         for(var i = 0, len = ns.length; i < len; i++){
12499             if(ns[i] == node){
12500                 return i;
12501             }
12502         }
12503         return -1;
12504     }
12505 });
12506 /*
12507  * - LGPL
12508  *
12509  * based on jquery fullcalendar
12510  * 
12511  */
12512
12513 Roo.bootstrap = Roo.bootstrap || {};
12514 /**
12515  * @class Roo.bootstrap.Calendar
12516  * @extends Roo.bootstrap.Component
12517  * Bootstrap Calendar class
12518  * @cfg {Boolean} loadMask (true|false) default false
12519  * @cfg {Object} header generate the user specific header of the calendar, default false
12520
12521  * @constructor
12522  * Create a new Container
12523  * @param {Object} config The config object
12524  */
12525
12526
12527
12528 Roo.bootstrap.Calendar = function(config){
12529     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
12530      this.addEvents({
12531         /**
12532              * @event select
12533              * Fires when a date is selected
12534              * @param {DatePicker} this
12535              * @param {Date} date The selected date
12536              */
12537         'select': true,
12538         /**
12539              * @event monthchange
12540              * Fires when the displayed month changes 
12541              * @param {DatePicker} this
12542              * @param {Date} date The selected month
12543              */
12544         'monthchange': true,
12545         /**
12546              * @event evententer
12547              * Fires when mouse over an event
12548              * @param {Calendar} this
12549              * @param {event} Event
12550              */
12551         'evententer': true,
12552         /**
12553              * @event eventleave
12554              * Fires when the mouse leaves an
12555              * @param {Calendar} this
12556              * @param {event}
12557              */
12558         'eventleave': true,
12559         /**
12560              * @event eventclick
12561              * Fires when the mouse click an
12562              * @param {Calendar} this
12563              * @param {event}
12564              */
12565         'eventclick': true
12566         
12567     });
12568
12569 };
12570
12571 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
12572     
12573      /**
12574      * @cfg {Number} startDay
12575      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
12576      */
12577     startDay : 0,
12578     
12579     loadMask : false,
12580     
12581     header : false,
12582       
12583     getAutoCreate : function(){
12584         
12585         
12586         var fc_button = function(name, corner, style, content ) {
12587             return Roo.apply({},{
12588                 tag : 'span',
12589                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
12590                          (corner.length ?
12591                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
12592                             ''
12593                         ),
12594                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
12595                 unselectable: 'on'
12596             });
12597         };
12598         
12599         var header = {};
12600         
12601         if(!this.header){
12602             header = {
12603                 tag : 'table',
12604                 cls : 'fc-header',
12605                 style : 'width:100%',
12606                 cn : [
12607                     {
12608                         tag: 'tr',
12609                         cn : [
12610                             {
12611                                 tag : 'td',
12612                                 cls : 'fc-header-left',
12613                                 cn : [
12614                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
12615                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
12616                                     { tag: 'span', cls: 'fc-header-space' },
12617                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
12618
12619
12620                                 ]
12621                             },
12622
12623                             {
12624                                 tag : 'td',
12625                                 cls : 'fc-header-center',
12626                                 cn : [
12627                                     {
12628                                         tag: 'span',
12629                                         cls: 'fc-header-title',
12630                                         cn : {
12631                                             tag: 'H2',
12632                                             html : 'month / year'
12633                                         }
12634                                     }
12635
12636                                 ]
12637                             },
12638                             {
12639                                 tag : 'td',
12640                                 cls : 'fc-header-right',
12641                                 cn : [
12642                               /*      fc_button('month', 'left', '', 'month' ),
12643                                     fc_button('week', '', '', 'week' ),
12644                                     fc_button('day', 'right', '', 'day' )
12645                                 */    
12646
12647                                 ]
12648                             }
12649
12650                         ]
12651                     }
12652                 ]
12653             };
12654         }
12655         
12656         header = this.header;
12657         
12658        
12659         var cal_heads = function() {
12660             var ret = [];
12661             // fixme - handle this.
12662             
12663             for (var i =0; i < Date.dayNames.length; i++) {
12664                 var d = Date.dayNames[i];
12665                 ret.push({
12666                     tag: 'th',
12667                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
12668                     html : d.substring(0,3)
12669                 });
12670                 
12671             }
12672             ret[0].cls += ' fc-first';
12673             ret[6].cls += ' fc-last';
12674             return ret;
12675         };
12676         var cal_cell = function(n) {
12677             return  {
12678                 tag: 'td',
12679                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
12680                 cn : [
12681                     {
12682                         cn : [
12683                             {
12684                                 cls: 'fc-day-number',
12685                                 html: 'D'
12686                             },
12687                             {
12688                                 cls: 'fc-day-content',
12689                              
12690                                 cn : [
12691                                      {
12692                                         style: 'position: relative;' // height: 17px;
12693                                     }
12694                                 ]
12695                             }
12696                             
12697                             
12698                         ]
12699                     }
12700                 ]
12701                 
12702             }
12703         };
12704         var cal_rows = function() {
12705             
12706             var ret = []
12707             for (var r = 0; r < 6; r++) {
12708                 var row= {
12709                     tag : 'tr',
12710                     cls : 'fc-week',
12711                     cn : []
12712                 };
12713                 
12714                 for (var i =0; i < Date.dayNames.length; i++) {
12715                     var d = Date.dayNames[i];
12716                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
12717
12718                 }
12719                 row.cn[0].cls+=' fc-first';
12720                 row.cn[0].cn[0].style = 'min-height:90px';
12721                 row.cn[6].cls+=' fc-last';
12722                 ret.push(row);
12723                 
12724             }
12725             ret[0].cls += ' fc-first';
12726             ret[4].cls += ' fc-prev-last';
12727             ret[5].cls += ' fc-last';
12728             return ret;
12729             
12730         };
12731         
12732         var cal_table = {
12733             tag: 'table',
12734             cls: 'fc-border-separate',
12735             style : 'width:100%',
12736             cellspacing  : 0,
12737             cn : [
12738                 { 
12739                     tag: 'thead',
12740                     cn : [
12741                         { 
12742                             tag: 'tr',
12743                             cls : 'fc-first fc-last',
12744                             cn : cal_heads()
12745                         }
12746                     ]
12747                 },
12748                 { 
12749                     tag: 'tbody',
12750                     cn : cal_rows()
12751                 }
12752                   
12753             ]
12754         };
12755          
12756          var cfg = {
12757             cls : 'fc fc-ltr',
12758             cn : [
12759                 header,
12760                 {
12761                     cls : 'fc-content',
12762                     style : "position: relative;",
12763                     cn : [
12764                         {
12765                             cls : 'fc-view fc-view-month fc-grid',
12766                             style : 'position: relative',
12767                             unselectable : 'on',
12768                             cn : [
12769                                 {
12770                                     cls : 'fc-event-container',
12771                                     style : 'position:absolute;z-index:8;top:0;left:0;'
12772                                 },
12773                                 cal_table
12774                             ]
12775                         }
12776                     ]
12777     
12778                 }
12779            ] 
12780             
12781         };
12782         
12783          
12784         
12785         return cfg;
12786     },
12787     
12788     
12789     initEvents : function()
12790     {
12791         if(!this.store){
12792             throw "can not find store for calendar";
12793         }
12794         
12795         var mark = {
12796             tag: "div",
12797             cls:"x-dlg-mask",
12798             style: "text-align:center",
12799             cn: [
12800                 {
12801                     tag: "div",
12802                     style: "background-color:white;width:50%;margin:250 auto",
12803                     cn: [
12804                         {
12805                             tag: "img",
12806                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
12807                         },
12808                         {
12809                             tag: "span",
12810                             html: "Loading"
12811                         }
12812                         
12813                     ]
12814                 }
12815             ]
12816         }
12817         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
12818         
12819         var size = this.el.select('.fc-content', true).first().getSize();
12820         this.maskEl.setSize(size.width, size.height);
12821         this.maskEl.enableDisplayMode("block");
12822         if(!this.loadMask){
12823             this.maskEl.hide();
12824         }
12825         
12826         this.store = Roo.factory(this.store, Roo.data);
12827         this.store.on('load', this.onLoad, this);
12828         this.store.on('beforeload', this.onBeforeLoad, this);
12829         
12830         this.resize();
12831         
12832         this.cells = this.el.select('.fc-day',true);
12833         //Roo.log(this.cells);
12834         this.textNodes = this.el.query('.fc-day-number');
12835         this.cells.addClassOnOver('fc-state-hover');
12836         
12837         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
12838         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
12839         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
12840         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
12841         
12842         this.on('monthchange', this.onMonthChange, this);
12843         
12844         this.update(new Date().clearTime());
12845     },
12846     
12847     resize : function() {
12848         var sz  = this.el.getSize();
12849         
12850         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
12851         this.el.select('.fc-day-content div',true).setHeight(34);
12852     },
12853     
12854     
12855     // private
12856     showPrevMonth : function(e){
12857         this.update(this.activeDate.add("mo", -1));
12858     },
12859     showToday : function(e){
12860         this.update(new Date().clearTime());
12861     },
12862     // private
12863     showNextMonth : function(e){
12864         this.update(this.activeDate.add("mo", 1));
12865     },
12866
12867     // private
12868     showPrevYear : function(){
12869         this.update(this.activeDate.add("y", -1));
12870     },
12871
12872     // private
12873     showNextYear : function(){
12874         this.update(this.activeDate.add("y", 1));
12875     },
12876
12877     
12878    // private
12879     update : function(date)
12880     {
12881         var vd = this.activeDate;
12882         this.activeDate = date;
12883 //        if(vd && this.el){
12884 //            var t = date.getTime();
12885 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
12886 //                Roo.log('using add remove');
12887 //                
12888 //                this.fireEvent('monthchange', this, date);
12889 //                
12890 //                this.cells.removeClass("fc-state-highlight");
12891 //                this.cells.each(function(c){
12892 //                   if(c.dateValue == t){
12893 //                       c.addClass("fc-state-highlight");
12894 //                       setTimeout(function(){
12895 //                            try{c.dom.firstChild.focus();}catch(e){}
12896 //                       }, 50);
12897 //                       return false;
12898 //                   }
12899 //                   return true;
12900 //                });
12901 //                return;
12902 //            }
12903 //        }
12904         
12905         var days = date.getDaysInMonth();
12906         
12907         var firstOfMonth = date.getFirstDateOfMonth();
12908         var startingPos = firstOfMonth.getDay()-this.startDay;
12909         
12910         if(startingPos < this.startDay){
12911             startingPos += 7;
12912         }
12913         
12914         var pm = date.add(Date.MONTH, -1);
12915         var prevStart = pm.getDaysInMonth()-startingPos;
12916 //        
12917         this.cells = this.el.select('.fc-day',true);
12918         this.textNodes = this.el.query('.fc-day-number');
12919         this.cells.addClassOnOver('fc-state-hover');
12920         
12921         var cells = this.cells.elements;
12922         var textEls = this.textNodes;
12923         
12924         Roo.each(cells, function(cell){
12925             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
12926         });
12927         
12928         days += startingPos;
12929
12930         // convert everything to numbers so it's fast
12931         var day = 86400000;
12932         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
12933         //Roo.log(d);
12934         //Roo.log(pm);
12935         //Roo.log(prevStart);
12936         
12937         var today = new Date().clearTime().getTime();
12938         var sel = date.clearTime().getTime();
12939         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
12940         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
12941         var ddMatch = this.disabledDatesRE;
12942         var ddText = this.disabledDatesText;
12943         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
12944         var ddaysText = this.disabledDaysText;
12945         var format = this.format;
12946         
12947         var setCellClass = function(cal, cell){
12948             cell.row = 0;
12949             cell.events = [];
12950             cell.more = [];
12951             //Roo.log('set Cell Class');
12952             cell.title = "";
12953             var t = d.getTime();
12954             
12955             //Roo.log(d);
12956             
12957             cell.dateValue = t;
12958             if(t == today){
12959                 cell.className += " fc-today";
12960                 cell.className += " fc-state-highlight";
12961                 cell.title = cal.todayText;
12962             }
12963             if(t == sel){
12964                 // disable highlight in other month..
12965                 //cell.className += " fc-state-highlight";
12966                 
12967             }
12968             // disabling
12969             if(t < min) {
12970                 cell.className = " fc-state-disabled";
12971                 cell.title = cal.minText;
12972                 return;
12973             }
12974             if(t > max) {
12975                 cell.className = " fc-state-disabled";
12976                 cell.title = cal.maxText;
12977                 return;
12978             }
12979             if(ddays){
12980                 if(ddays.indexOf(d.getDay()) != -1){
12981                     cell.title = ddaysText;
12982                     cell.className = " fc-state-disabled";
12983                 }
12984             }
12985             if(ddMatch && format){
12986                 var fvalue = d.dateFormat(format);
12987                 if(ddMatch.test(fvalue)){
12988                     cell.title = ddText.replace("%0", fvalue);
12989                     cell.className = " fc-state-disabled";
12990                 }
12991             }
12992             
12993             if (!cell.initialClassName) {
12994                 cell.initialClassName = cell.dom.className;
12995             }
12996             
12997             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
12998         };
12999
13000         var i = 0;
13001         
13002         for(; i < startingPos; i++) {
13003             textEls[i].innerHTML = (++prevStart);
13004             d.setDate(d.getDate()+1);
13005             
13006             cells[i].className = "fc-past fc-other-month";
13007             setCellClass(this, cells[i]);
13008         }
13009         
13010         var intDay = 0;
13011         
13012         for(; i < days; i++){
13013             intDay = i - startingPos + 1;
13014             textEls[i].innerHTML = (intDay);
13015             d.setDate(d.getDate()+1);
13016             
13017             cells[i].className = ''; // "x-date-active";
13018             setCellClass(this, cells[i]);
13019         }
13020         var extraDays = 0;
13021         
13022         for(; i < 42; i++) {
13023             textEls[i].innerHTML = (++extraDays);
13024             d.setDate(d.getDate()+1);
13025             
13026             cells[i].className = "fc-future fc-other-month";
13027             setCellClass(this, cells[i]);
13028         }
13029         
13030         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
13031         
13032         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
13033         
13034         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
13035         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
13036         
13037         if(totalRows != 6){
13038             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
13039             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
13040         }
13041         
13042         this.fireEvent('monthchange', this, date);
13043         
13044         
13045         /*
13046         if(!this.internalRender){
13047             var main = this.el.dom.firstChild;
13048             var w = main.offsetWidth;
13049             this.el.setWidth(w + this.el.getBorderWidth("lr"));
13050             Roo.fly(main).setWidth(w);
13051             this.internalRender = true;
13052             // opera does not respect the auto grow header center column
13053             // then, after it gets a width opera refuses to recalculate
13054             // without a second pass
13055             if(Roo.isOpera && !this.secondPass){
13056                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
13057                 this.secondPass = true;
13058                 this.update.defer(10, this, [date]);
13059             }
13060         }
13061         */
13062         
13063     },
13064     
13065     findCell : function(dt) {
13066         dt = dt.clearTime().getTime();
13067         var ret = false;
13068         this.cells.each(function(c){
13069             //Roo.log("check " +c.dateValue + '?=' + dt);
13070             if(c.dateValue == dt){
13071                 ret = c;
13072                 return false;
13073             }
13074             return true;
13075         });
13076         
13077         return ret;
13078     },
13079     
13080     findCells : function(ev) {
13081         var s = ev.start.clone().clearTime().getTime();
13082        // Roo.log(s);
13083         var e= ev.end.clone().clearTime().getTime();
13084        // Roo.log(e);
13085         var ret = [];
13086         this.cells.each(function(c){
13087              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
13088             
13089             if(c.dateValue > e){
13090                 return ;
13091             }
13092             if(c.dateValue < s){
13093                 return ;
13094             }
13095             ret.push(c);
13096         });
13097         
13098         return ret;    
13099     },
13100     
13101 //    findBestRow: function(cells)
13102 //    {
13103 //        var ret = 0;
13104 //        
13105 //        for (var i =0 ; i < cells.length;i++) {
13106 //            ret  = Math.max(cells[i].rows || 0,ret);
13107 //        }
13108 //        return ret;
13109 //        
13110 //    },
13111     
13112     
13113     addItem : function(ev)
13114     {
13115         // look for vertical location slot in
13116         var cells = this.findCells(ev);
13117         
13118 //        ev.row = this.findBestRow(cells);
13119         
13120         // work out the location.
13121         
13122         var crow = false;
13123         var rows = [];
13124         for(var i =0; i < cells.length; i++) {
13125             
13126             cells[i].row = cells[0].row;
13127             
13128             if(i == 0){
13129                 cells[i].row = cells[i].row + 1;
13130             }
13131             
13132             if (!crow) {
13133                 crow = {
13134                     start : cells[i],
13135                     end :  cells[i]
13136                 };
13137                 continue;
13138             }
13139             if (crow.start.getY() == cells[i].getY()) {
13140                 // on same row.
13141                 crow.end = cells[i];
13142                 continue;
13143             }
13144             // different row.
13145             rows.push(crow);
13146             crow = {
13147                 start: cells[i],
13148                 end : cells[i]
13149             };
13150             
13151         }
13152         
13153         rows.push(crow);
13154         ev.els = [];
13155         ev.rows = rows;
13156         ev.cells = cells;
13157         
13158         cells[0].events.push(ev);
13159         
13160         this.calevents.push(ev);
13161     },
13162     
13163     clearEvents: function() {
13164         
13165         if(!this.calevents){
13166             return;
13167         }
13168         
13169         Roo.each(this.cells.elements, function(c){
13170             c.row = 0;
13171             c.events = [];
13172             c.more = [];
13173         });
13174         
13175         Roo.each(this.calevents, function(e) {
13176             Roo.each(e.els, function(el) {
13177                 el.un('mouseenter' ,this.onEventEnter, this);
13178                 el.un('mouseleave' ,this.onEventLeave, this);
13179                 el.remove();
13180             },this);
13181         },this);
13182         
13183         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
13184             e.remove();
13185         });
13186         
13187     },
13188     
13189     renderEvents: function()
13190     {   
13191         var _this = this;
13192         
13193         this.cells.each(function(c) {
13194             
13195             if(c.row < 5){
13196                 return;
13197             }
13198             
13199             var ev = c.events;
13200             
13201             var r = 4;
13202             if(c.row != c.events.length){
13203                 r = 4 - (4 - (c.row - c.events.length));
13204             }
13205             
13206             c.events = ev.slice(0, r);
13207             c.more = ev.slice(r);
13208             
13209             if(c.more.length && c.more.length == 1){
13210                 c.events.push(c.more.pop());
13211             }
13212             
13213             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
13214             
13215         });
13216             
13217         this.cells.each(function(c) {
13218             
13219             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
13220             
13221             
13222             for (var e = 0; e < c.events.length; e++){
13223                 var ev = c.events[e];
13224                 var rows = ev.rows;
13225                 
13226                 for(var i = 0; i < rows.length; i++) {
13227                 
13228                     // how many rows should it span..
13229
13230                     var  cfg = {
13231                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
13232                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
13233
13234                         unselectable : "on",
13235                         cn : [
13236                             {
13237                                 cls: 'fc-event-inner',
13238                                 cn : [
13239     //                                {
13240     //                                  tag:'span',
13241     //                                  cls: 'fc-event-time',
13242     //                                  html : cells.length > 1 ? '' : ev.time
13243     //                                },
13244                                     {
13245                                       tag:'span',
13246                                       cls: 'fc-event-title',
13247                                       html : String.format('{0}', ev.title)
13248                                     }
13249
13250
13251                                 ]
13252                             },
13253                             {
13254                                 cls: 'ui-resizable-handle ui-resizable-e',
13255                                 html : '&nbsp;&nbsp;&nbsp'
13256                             }
13257
13258                         ]
13259                     };
13260
13261                     if (i == 0) {
13262                         cfg.cls += ' fc-event-start';
13263                     }
13264                     if ((i+1) == rows.length) {
13265                         cfg.cls += ' fc-event-end';
13266                     }
13267
13268                     var ctr = _this.el.select('.fc-event-container',true).first();
13269                     var cg = ctr.createChild(cfg);
13270
13271                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
13272                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
13273
13274                     var r = (c.more.length) ? 1 : 0;
13275                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
13276                     cg.setWidth(ebox.right - sbox.x -2);
13277
13278                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
13279                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
13280                     cg.on('click', _this.onEventClick, _this, ev);
13281
13282                     ev.els.push(cg);
13283                     
13284                 }
13285                 
13286             }
13287             
13288             
13289             if(c.more.length){
13290                 var  cfg = {
13291                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
13292                     style : 'position: absolute',
13293                     unselectable : "on",
13294                     cn : [
13295                         {
13296                             cls: 'fc-event-inner',
13297                             cn : [
13298                                 {
13299                                   tag:'span',
13300                                   cls: 'fc-event-title',
13301                                   html : 'More'
13302                                 }
13303
13304
13305                             ]
13306                         },
13307                         {
13308                             cls: 'ui-resizable-handle ui-resizable-e',
13309                             html : '&nbsp;&nbsp;&nbsp'
13310                         }
13311
13312                     ]
13313                 };
13314
13315                 var ctr = _this.el.select('.fc-event-container',true).first();
13316                 var cg = ctr.createChild(cfg);
13317
13318                 var sbox = c.select('.fc-day-content',true).first().getBox();
13319                 var ebox = c.select('.fc-day-content',true).first().getBox();
13320                 //Roo.log(cg);
13321                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
13322                 cg.setWidth(ebox.right - sbox.x -2);
13323
13324                 cg.on('click', _this.onMoreEventClick, _this, c.more);
13325                 
13326             }
13327             
13328         });
13329         
13330         
13331         
13332     },
13333     
13334     onEventEnter: function (e, el,event,d) {
13335         this.fireEvent('evententer', this, el, event);
13336     },
13337     
13338     onEventLeave: function (e, el,event,d) {
13339         this.fireEvent('eventleave', this, el, event);
13340     },
13341     
13342     onEventClick: function (e, el,event,d) {
13343         this.fireEvent('eventclick', this, el, event);
13344     },
13345     
13346     onMonthChange: function () {
13347         this.store.load();
13348     },
13349     
13350     onMoreEventClick: function(e, el, more)
13351     {
13352         var _this = this;
13353         
13354         this.calpopover.placement = 'right';
13355         this.calpopover.setTitle('More');
13356         
13357         this.calpopover.setContent('');
13358         
13359         var ctr = this.calpopover.el.select('.popover-content', true).first();
13360         
13361         Roo.each(more, function(m){
13362             var cfg = {
13363                 cls : 'fc-event-hori fc-event-draggable',
13364                 html : m.title
13365             }
13366             var cg = ctr.createChild(cfg);
13367             
13368             cg.on('click', _this.onEventClick, _this, m);
13369         });
13370         
13371         this.calpopover.show(el);
13372         
13373         
13374     },
13375     
13376     onLoad: function () 
13377     {   
13378         this.calevents = [];
13379         var cal = this;
13380         
13381         if(this.store.getCount() > 0){
13382             this.store.data.each(function(d){
13383                cal.addItem({
13384                     id : d.data.id,
13385                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
13386                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
13387                     time : d.data.start_time,
13388                     title : d.data.title,
13389                     description : d.data.description,
13390                     venue : d.data.venue
13391                 });
13392             });
13393         }
13394         
13395         this.renderEvents();
13396         
13397         if(this.calevents.length && this.loadMask){
13398             this.maskEl.hide();
13399         }
13400     },
13401     
13402     onBeforeLoad: function()
13403     {
13404         this.clearEvents();
13405         if(this.loadMask){
13406             this.maskEl.show();
13407         }
13408     }
13409 });
13410
13411  
13412  /*
13413  * - LGPL
13414  *
13415  * element
13416  * 
13417  */
13418
13419 /**
13420  * @class Roo.bootstrap.Popover
13421  * @extends Roo.bootstrap.Component
13422  * Bootstrap Popover class
13423  * @cfg {String} html contents of the popover   (or false to use children..)
13424  * @cfg {String} title of popover (or false to hide)
13425  * @cfg {String} placement how it is placed
13426  * @cfg {String} trigger click || hover (or false to trigger manually)
13427  * @cfg {String} over what (parent or false to trigger manually.)
13428  * 
13429  * @constructor
13430  * Create a new Popover
13431  * @param {Object} config The config object
13432  */
13433
13434 Roo.bootstrap.Popover = function(config){
13435     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
13436 };
13437
13438 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
13439     
13440     title: 'Fill in a title',
13441     html: false,
13442     
13443     placement : 'right',
13444     trigger : 'hover', // hover
13445     
13446     over: 'parent',
13447     
13448     can_build_overlaid : false,
13449     
13450     getChildContainer : function()
13451     {
13452         return this.el.select('.popover-content',true).first();
13453     },
13454     
13455     getAutoCreate : function(){
13456          Roo.log('make popover?');
13457         var cfg = {
13458            cls : 'popover roo-dynamic',
13459            style: 'display:block',
13460            cn : [
13461                 {
13462                     cls : 'arrow'
13463                 },
13464                 {
13465                     cls : 'popover-inner',
13466                     cn : [
13467                         {
13468                             tag: 'h3',
13469                             cls: 'popover-title',
13470                             html : this.title
13471                         },
13472                         {
13473                             cls : 'popover-content',
13474                             html : this.html
13475                         }
13476                     ]
13477                     
13478                 }
13479            ]
13480         };
13481         
13482         return cfg;
13483     },
13484     setTitle: function(str)
13485     {
13486         this.el.select('.popover-title',true).first().dom.innerHTML = str;
13487     },
13488     setContent: function(str)
13489     {
13490         this.el.select('.popover-content',true).first().dom.innerHTML = str;
13491     },
13492     // as it get's added to the bottom of the page.
13493     onRender : function(ct, position)
13494     {
13495         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
13496         if(!this.el){
13497             var cfg = Roo.apply({},  this.getAutoCreate());
13498             cfg.id = Roo.id();
13499             
13500             if (this.cls) {
13501                 cfg.cls += ' ' + this.cls;
13502             }
13503             if (this.style) {
13504                 cfg.style = this.style;
13505             }
13506             Roo.log("adding to ")
13507             this.el = Roo.get(document.body).createChild(cfg, position);
13508             Roo.log(this.el);
13509         }
13510         this.initEvents();
13511     },
13512     
13513     initEvents : function()
13514     {
13515         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
13516         this.el.enableDisplayMode('block');
13517         this.el.hide();
13518         if (this.over === false) {
13519             return; 
13520         }
13521         if (this.triggers === false) {
13522             return;
13523         }
13524         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13525         var triggers = this.trigger ? this.trigger.split(' ') : [];
13526         Roo.each(triggers, function(trigger) {
13527         
13528             if (trigger == 'click') {
13529                 on_el.on('click', this.toggle, this);
13530             } else if (trigger != 'manual') {
13531                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
13532                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
13533       
13534                 on_el.on(eventIn  ,this.enter, this);
13535                 on_el.on(eventOut, this.leave, this);
13536             }
13537         }, this);
13538         
13539     },
13540     
13541     
13542     // private
13543     timeout : null,
13544     hoverState : null,
13545     
13546     toggle : function () {
13547         this.hoverState == 'in' ? this.leave() : this.enter();
13548     },
13549     
13550     enter : function () {
13551        
13552     
13553         clearTimeout(this.timeout);
13554     
13555         this.hoverState = 'in'
13556     
13557         if (!this.delay || !this.delay.show) {
13558             this.show();
13559             return 
13560         }
13561         var _t = this;
13562         this.timeout = setTimeout(function () {
13563             if (_t.hoverState == 'in') {
13564                 _t.show();
13565             }
13566         }, this.delay.show)
13567     },
13568     leave : function() {
13569         clearTimeout(this.timeout);
13570     
13571         this.hoverState = 'out'
13572     
13573         if (!this.delay || !this.delay.hide) {
13574             this.hide();
13575             return 
13576         }
13577         var _t = this;
13578         this.timeout = setTimeout(function () {
13579             if (_t.hoverState == 'out') {
13580                 _t.hide();
13581             }
13582         }, this.delay.hide)
13583     },
13584     
13585     show : function (on_el)
13586     {
13587         if (!on_el) {
13588             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13589         }
13590         // set content.
13591         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
13592         if (this.html !== false) {
13593             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
13594         }
13595         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
13596         if (!this.title.length) {
13597             this.el.select('.popover-title',true).hide();
13598         }
13599         
13600         var placement = typeof this.placement == 'function' ?
13601             this.placement.call(this, this.el, on_el) :
13602             this.placement;
13603             
13604         var autoToken = /\s?auto?\s?/i;
13605         var autoPlace = autoToken.test(placement);
13606         if (autoPlace) {
13607             placement = placement.replace(autoToken, '') || 'top';
13608         }
13609         
13610         //this.el.detach()
13611         //this.el.setXY([0,0]);
13612         this.el.show();
13613         this.el.dom.style.display='block';
13614         this.el.addClass(placement);
13615         
13616         //this.el.appendTo(on_el);
13617         
13618         var p = this.getPosition();
13619         var box = this.el.getBox();
13620         
13621         if (autoPlace) {
13622             // fixme..
13623         }
13624         var align = Roo.bootstrap.Popover.alignment[placement]
13625         this.el.alignTo(on_el, align[0],align[1]);
13626         //var arrow = this.el.select('.arrow',true).first();
13627         //arrow.set(align[2], 
13628         
13629         this.el.addClass('in');
13630         this.hoverState = null;
13631         
13632         if (this.el.hasClass('fade')) {
13633             // fade it?
13634         }
13635         
13636     },
13637     hide : function()
13638     {
13639         this.el.setXY([0,0]);
13640         this.el.removeClass('in');
13641         this.el.hide();
13642         
13643     }
13644     
13645 });
13646
13647 Roo.bootstrap.Popover.alignment = {
13648     'left' : ['r-l', [-10,0], 'right'],
13649     'right' : ['l-r', [10,0], 'left'],
13650     'bottom' : ['t-b', [0,10], 'top'],
13651     'top' : [ 'b-t', [0,-10], 'bottom']
13652 };
13653
13654  /*
13655  * - LGPL
13656  *
13657  * Progress
13658  * 
13659  */
13660
13661 /**
13662  * @class Roo.bootstrap.Progress
13663  * @extends Roo.bootstrap.Component
13664  * Bootstrap Progress class
13665  * @cfg {Boolean} striped striped of the progress bar
13666  * @cfg {Boolean} active animated of the progress bar
13667  * 
13668  * 
13669  * @constructor
13670  * Create a new Progress
13671  * @param {Object} config The config object
13672  */
13673
13674 Roo.bootstrap.Progress = function(config){
13675     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
13676 };
13677
13678 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
13679     
13680     striped : false,
13681     active: false,
13682     
13683     getAutoCreate : function(){
13684         var cfg = {
13685             tag: 'div',
13686             cls: 'progress'
13687         };
13688         
13689         
13690         if(this.striped){
13691             cfg.cls += ' progress-striped';
13692         }
13693       
13694         if(this.active){
13695             cfg.cls += ' active';
13696         }
13697         
13698         
13699         return cfg;
13700     }
13701    
13702 });
13703
13704  
13705
13706  /*
13707  * - LGPL
13708  *
13709  * ProgressBar
13710  * 
13711  */
13712
13713 /**
13714  * @class Roo.bootstrap.ProgressBar
13715  * @extends Roo.bootstrap.Component
13716  * Bootstrap ProgressBar class
13717  * @cfg {Number} aria_valuenow aria-value now
13718  * @cfg {Number} aria_valuemin aria-value min
13719  * @cfg {Number} aria_valuemax aria-value max
13720  * @cfg {String} label label for the progress bar
13721  * @cfg {String} panel (success | info | warning | danger )
13722  * @cfg {String} role role of the progress bar
13723  * @cfg {String} sr_only text
13724  * 
13725  * 
13726  * @constructor
13727  * Create a new ProgressBar
13728  * @param {Object} config The config object
13729  */
13730
13731 Roo.bootstrap.ProgressBar = function(config){
13732     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
13733 };
13734
13735 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
13736     
13737     aria_valuenow : 0,
13738     aria_valuemin : 0,
13739     aria_valuemax : 100,
13740     label : false,
13741     panel : false,
13742     role : false,
13743     sr_only: false,
13744     
13745     getAutoCreate : function()
13746     {
13747         
13748         var cfg = {
13749             tag: 'div',
13750             cls: 'progress-bar',
13751             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
13752         };
13753         
13754         if(this.sr_only){
13755             cfg.cn = {
13756                 tag: 'span',
13757                 cls: 'sr-only',
13758                 html: this.sr_only
13759             }
13760         }
13761         
13762         if(this.role){
13763             cfg.role = this.role;
13764         }
13765         
13766         if(this.aria_valuenow){
13767             cfg['aria-valuenow'] = this.aria_valuenow;
13768         }
13769         
13770         if(this.aria_valuemin){
13771             cfg['aria-valuemin'] = this.aria_valuemin;
13772         }
13773         
13774         if(this.aria_valuemax){
13775             cfg['aria-valuemax'] = this.aria_valuemax;
13776         }
13777         
13778         if(this.label && !this.sr_only){
13779             cfg.html = this.label;
13780         }
13781         
13782         if(this.panel){
13783             cfg.cls += ' progress-bar-' + this.panel;
13784         }
13785         
13786         return cfg;
13787     },
13788     
13789     update : function(aria_valuenow)
13790     {
13791         this.aria_valuenow = aria_valuenow;
13792         
13793         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
13794     }
13795    
13796 });
13797
13798  
13799
13800  /*
13801  * - LGPL
13802  *
13803  * column
13804  * 
13805  */
13806
13807 /**
13808  * @class Roo.bootstrap.TabGroup
13809  * @extends Roo.bootstrap.Column
13810  * Bootstrap Column class
13811  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
13812  * @cfg {Boolean} carousel true to make the group behave like a carousel
13813  * 
13814  * @constructor
13815  * Create a new TabGroup
13816  * @param {Object} config The config object
13817  */
13818
13819 Roo.bootstrap.TabGroup = function(config){
13820     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
13821     if (!this.navId) {
13822         this.navId = Roo.id();
13823     }
13824     this.tabs = [];
13825     Roo.bootstrap.TabGroup.register(this);
13826     
13827 };
13828
13829 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
13830     
13831     carousel : false,
13832     transition : false,
13833      
13834     getAutoCreate : function()
13835     {
13836         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
13837         
13838         cfg.cls += ' tab-content';
13839         
13840         if (this.carousel) {
13841             cfg.cls += ' carousel slide';
13842             cfg.cn = [{
13843                cls : 'carousel-inner'
13844             }]
13845         }
13846         
13847         
13848         return cfg;
13849     },
13850     getChildContainer : function()
13851     {
13852         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
13853     },
13854     
13855     /**
13856     * register a Navigation item
13857     * @param {Roo.bootstrap.NavItem} the navitem to add
13858     */
13859     register : function(item)
13860     {
13861         this.tabs.push( item);
13862         item.navId = this.navId; // not really needed..
13863     
13864     },
13865     
13866     getActivePanel : function()
13867     {
13868         var r = false;
13869         Roo.each(this.tabs, function(t) {
13870             if (t.active) {
13871                 r = t;
13872                 return false;
13873             }
13874             return null;
13875         });
13876         return r;
13877         
13878     },
13879     getPanelByName : function(n)
13880     {
13881         var r = false;
13882         Roo.each(this.tabs, function(t) {
13883             if (t.tabId == n) {
13884                 r = t;
13885                 return false;
13886             }
13887             return null;
13888         });
13889         return r;
13890     },
13891     indexOfPanel : function(p)
13892     {
13893         var r = false;
13894         Roo.each(this.tabs, function(t,i) {
13895             if (t.tabId == p.tabId) {
13896                 r = i;
13897                 return false;
13898             }
13899             return null;
13900         });
13901         return r;
13902     },
13903     /**
13904      * show a specific panel
13905      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
13906      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
13907      */
13908     showPanel : function (pan)
13909     {
13910         
13911         if (typeof(pan) == 'number') {
13912             pan = this.tabs[pan];
13913         }
13914         if (typeof(pan) == 'string') {
13915             pan = this.getPanelByName(pan);
13916         }
13917         if (pan.tabId == this.getActivePanel().tabId) {
13918             return true;
13919         }
13920         var cur = this.getActivePanel();
13921         
13922         if (false === cur.fireEvent('beforedeactivate')) {
13923             return false;
13924         }
13925         
13926         if (this.carousel) {
13927             this.transition = true;
13928             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
13929             var lr = dir == 'next' ? 'left' : 'right';
13930             pan.el.addClass(dir); // or prev
13931             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
13932             cur.el.addClass(lr); // or right
13933             pan.el.addClass(lr);
13934             
13935             var _this = this;
13936             cur.el.on('transitionend', function() {
13937                 Roo.log("trans end?");
13938                 
13939                 pan.el.removeClass([lr,dir]);
13940                 pan.setActive(true);
13941                 
13942                 cur.el.removeClass([lr]);
13943                 cur.setActive(false);
13944                 
13945                 _this.transition = false;
13946                 
13947             }, this, { single:  true } );
13948             return true;
13949         }
13950         
13951         cur.setActive(false);
13952         pan.setActive(true);
13953         return true;
13954         
13955     },
13956     showPanelNext : function()
13957     {
13958         var i = this.indexOfPanel(this.getActivePanel());
13959         if (i > this.tabs.length) {
13960             return;
13961         }
13962         this.showPanel(this.tabs[i+1]);
13963     },
13964     showPanelPrev : function()
13965     {
13966         var i = this.indexOfPanel(this.getActivePanel());
13967         if (i  < 1) {
13968             return;
13969         }
13970         this.showPanel(this.tabs[i-1]);
13971     }
13972     
13973     
13974   
13975 });
13976
13977  
13978
13979  
13980  
13981 Roo.apply(Roo.bootstrap.TabGroup, {
13982     
13983     groups: {},
13984      /**
13985     * register a Navigation Group
13986     * @param {Roo.bootstrap.NavGroup} the navgroup to add
13987     */
13988     register : function(navgrp)
13989     {
13990         this.groups[navgrp.navId] = navgrp;
13991         
13992     },
13993     /**
13994     * fetch a Navigation Group based on the navigation ID
13995     * if one does not exist , it will get created.
13996     * @param {string} the navgroup to add
13997     * @returns {Roo.bootstrap.NavGroup} the navgroup 
13998     */
13999     get: function(navId) {
14000         if (typeof(this.groups[navId]) == 'undefined') {
14001             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
14002         }
14003         return this.groups[navId] ;
14004     }
14005     
14006     
14007     
14008 });
14009
14010  /*
14011  * - LGPL
14012  *
14013  * TabPanel
14014  * 
14015  */
14016
14017 /**
14018  * @class Roo.bootstrap.TabPanel
14019  * @extends Roo.bootstrap.Component
14020  * Bootstrap TabPanel class
14021  * @cfg {Boolean} active panel active
14022  * @cfg {String} html panel content
14023  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
14024  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
14025  * 
14026  * 
14027  * @constructor
14028  * Create a new TabPanel
14029  * @param {Object} config The config object
14030  */
14031
14032 Roo.bootstrap.TabPanel = function(config){
14033     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
14034     this.addEvents({
14035         /**
14036              * @event changed
14037              * Fires when the active status changes
14038              * @param {Roo.bootstrap.TabPanel} this
14039              * @param {Boolean} state the new state
14040             
14041          */
14042         'changed': true,
14043         /**
14044              * @event beforedeactivate
14045              * Fires before a tab is de-activated - can be used to do validation on a form.
14046              * @param {Roo.bootstrap.TabPanel} this
14047              * @return {Boolean} false if there is an error
14048             
14049          */
14050         'beforedeactivate': true
14051      });
14052     
14053     this.tabId = this.tabId || Roo.id();
14054   
14055 };
14056
14057 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
14058     
14059     active: false,
14060     html: false,
14061     tabId: false,
14062     navId : false,
14063     
14064     getAutoCreate : function(){
14065         var cfg = {
14066             tag: 'div',
14067             // item is needed for carousel - not sure if it has any effect otherwise
14068             cls: 'tab-pane item',
14069             html: this.html || ''
14070         };
14071         
14072         if(this.active){
14073             cfg.cls += ' active';
14074         }
14075         
14076         if(this.tabId){
14077             cfg.tabId = this.tabId;
14078         }
14079         
14080         
14081         return cfg;
14082     },
14083     
14084     initEvents:  function()
14085     {
14086         Roo.log('-------- init events on tab panel ---------');
14087         
14088         var p = this.parent();
14089         this.navId = this.navId || p.navId;
14090         
14091         if (typeof(this.navId) != 'undefined') {
14092             // not really needed.. but just in case.. parent should be a NavGroup.
14093             var tg = Roo.bootstrap.TabGroup.get(this.navId);
14094             Roo.log(['register', tg, this]);
14095             tg.register(this);
14096         }
14097     },
14098     
14099     
14100     onRender : function(ct, position)
14101     {
14102        // Roo.log("Call onRender: " + this.xtype);
14103         
14104         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
14105         
14106         
14107         
14108         
14109         
14110     },
14111     
14112     setActive: function(state)
14113     {
14114         Roo.log("panel - set active " + this.tabId + "=" + state);
14115         
14116         this.active = state;
14117         if (!state) {
14118             this.el.removeClass('active');
14119             
14120         } else  if (!this.el.hasClass('active')) {
14121             this.el.addClass('active');
14122         }
14123         this.fireEvent('changed', this, state);
14124     }
14125     
14126     
14127 });
14128  
14129
14130  
14131
14132  /*
14133  * - LGPL
14134  *
14135  * DateField
14136  * 
14137  */
14138
14139 /**
14140  * @class Roo.bootstrap.DateField
14141  * @extends Roo.bootstrap.Input
14142  * Bootstrap DateField class
14143  * @cfg {Number} weekStart default 0
14144  * @cfg {Number} weekStart default 0
14145  * @cfg {Number} viewMode default empty, (months|years)
14146  * @cfg {Number} minViewMode default empty, (months|years)
14147  * @cfg {Number} startDate default -Infinity
14148  * @cfg {Number} endDate default Infinity
14149  * @cfg {Boolean} todayHighlight default false
14150  * @cfg {Boolean} todayBtn default false
14151  * @cfg {Boolean} calendarWeeks default false
14152  * @cfg {Object} daysOfWeekDisabled default empty
14153  * 
14154  * @cfg {Boolean} keyboardNavigation default true
14155  * @cfg {String} language default en
14156  * 
14157  * @constructor
14158  * Create a new DateField
14159  * @param {Object} config The config object
14160  */
14161
14162 Roo.bootstrap.DateField = function(config){
14163     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
14164      this.addEvents({
14165             /**
14166              * @event show
14167              * Fires when this field show.
14168              * @param {Roo.bootstrap.DateField} this
14169              * @param {Mixed} date The date value
14170              */
14171             show : true,
14172             /**
14173              * @event show
14174              * Fires when this field hide.
14175              * @param {Roo.bootstrap.DateField} this
14176              * @param {Mixed} date The date value
14177              */
14178             hide : true,
14179             /**
14180              * @event select
14181              * Fires when select a date.
14182              * @param {Roo.bootstrap.DateField} this
14183              * @param {Mixed} date The date value
14184              */
14185             select : true
14186         });
14187 };
14188
14189 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
14190     
14191     /**
14192      * @cfg {String} format
14193      * The default date format string which can be overriden for localization support.  The format must be
14194      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
14195      */
14196     format : "m/d/y",
14197     /**
14198      * @cfg {String} altFormats
14199      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
14200      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
14201      */
14202     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
14203     
14204     weekStart : 0,
14205     
14206     viewMode : '',
14207     
14208     minViewMode : '',
14209     
14210     todayHighlight : false,
14211     
14212     todayBtn: false,
14213     
14214     language: 'en',
14215     
14216     keyboardNavigation: true,
14217     
14218     calendarWeeks: false,
14219     
14220     startDate: -Infinity,
14221     
14222     endDate: Infinity,
14223     
14224     daysOfWeekDisabled: [],
14225     
14226     _events: [],
14227     
14228     UTCDate: function()
14229     {
14230         return new Date(Date.UTC.apply(Date, arguments));
14231     },
14232     
14233     UTCToday: function()
14234     {
14235         var today = new Date();
14236         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
14237     },
14238     
14239     getDate: function() {
14240             var d = this.getUTCDate();
14241             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
14242     },
14243     
14244     getUTCDate: function() {
14245             return this.date;
14246     },
14247     
14248     setDate: function(d) {
14249             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
14250     },
14251     
14252     setUTCDate: function(d) {
14253             this.date = d;
14254             this.setValue(this.formatDate(this.date));
14255     },
14256         
14257     onRender: function(ct, position)
14258     {
14259         
14260         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
14261         
14262         this.language = this.language || 'en';
14263         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
14264         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
14265         
14266         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
14267         this.format = this.format || 'm/d/y';
14268         this.isInline = false;
14269         this.isInput = true;
14270         this.component = this.el.select('.add-on', true).first() || false;
14271         this.component = (this.component && this.component.length === 0) ? false : this.component;
14272         this.hasInput = this.component && this.inputEL().length;
14273         
14274         if (typeof(this.minViewMode === 'string')) {
14275             switch (this.minViewMode) {
14276                 case 'months':
14277                     this.minViewMode = 1;
14278                     break;
14279                 case 'years':
14280                     this.minViewMode = 2;
14281                     break;
14282                 default:
14283                     this.minViewMode = 0;
14284                     break;
14285             }
14286         }
14287         
14288         if (typeof(this.viewMode === 'string')) {
14289             switch (this.viewMode) {
14290                 case 'months':
14291                     this.viewMode = 1;
14292                     break;
14293                 case 'years':
14294                     this.viewMode = 2;
14295                     break;
14296                 default:
14297                     this.viewMode = 0;
14298                     break;
14299             }
14300         }
14301                 
14302         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
14303         
14304         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14305         
14306         this.picker().on('mousedown', this.onMousedown, this);
14307         this.picker().on('click', this.onClick, this);
14308         
14309         this.picker().addClass('datepicker-dropdown');
14310         
14311         this.startViewMode = this.viewMode;
14312         
14313         
14314         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
14315             if(!this.calendarWeeks){
14316                 v.remove();
14317                 return;
14318             };
14319             
14320             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
14321             v.attr('colspan', function(i, val){
14322                 return parseInt(val) + 1;
14323             });
14324         })
14325                         
14326         
14327         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
14328         
14329         this.setStartDate(this.startDate);
14330         this.setEndDate(this.endDate);
14331         
14332         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
14333         
14334         this.fillDow();
14335         this.fillMonths();
14336         this.update();
14337         this.showMode();
14338         
14339         if(this.isInline) {
14340             this.show();
14341         }
14342     },
14343     
14344     picker : function()
14345     {
14346         return this.el.select('.datepicker', true).first();
14347     },
14348     
14349     fillDow: function()
14350     {
14351         var dowCnt = this.weekStart;
14352         
14353         var dow = {
14354             tag: 'tr',
14355             cn: [
14356                 
14357             ]
14358         };
14359         
14360         if(this.calendarWeeks){
14361             dow.cn.push({
14362                 tag: 'th',
14363                 cls: 'cw',
14364                 html: '&nbsp;'
14365             })
14366         }
14367         
14368         while (dowCnt < this.weekStart + 7) {
14369             dow.cn.push({
14370                 tag: 'th',
14371                 cls: 'dow',
14372                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
14373             });
14374         }
14375         
14376         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
14377     },
14378     
14379     fillMonths: function()
14380     {    
14381         var i = 0
14382         var months = this.picker().select('>.datepicker-months td', true).first();
14383         
14384         months.dom.innerHTML = '';
14385         
14386         while (i < 12) {
14387             var month = {
14388                 tag: 'span',
14389                 cls: 'month',
14390                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
14391             }
14392             
14393             months.createChild(month);
14394         }
14395         
14396     },
14397     
14398     update: function()
14399     {
14400         
14401         this.date = (typeof(this.date) === 'undefined') ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
14402         
14403         if (this.date < this.startDate) {
14404             this.viewDate = new Date(this.startDate);
14405         } else if (this.date > this.endDate) {
14406             this.viewDate = new Date(this.endDate);
14407         } else {
14408             this.viewDate = new Date(this.date);
14409         }
14410         
14411         this.fill();
14412     },
14413     
14414     fill: function() 
14415     {
14416         var d = new Date(this.viewDate),
14417                 year = d.getUTCFullYear(),
14418                 month = d.getUTCMonth(),
14419                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
14420                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
14421                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
14422                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
14423                 currentDate = this.date && this.date.valueOf(),
14424                 today = this.UTCToday();
14425         
14426         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
14427         
14428 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
14429         
14430 //        this.picker.select('>tfoot th.today').
14431 //                                              .text(dates[this.language].today)
14432 //                                              .toggle(this.todayBtn !== false);
14433     
14434         this.updateNavArrows();
14435         this.fillMonths();
14436                                                 
14437         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
14438         
14439         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
14440          
14441         prevMonth.setUTCDate(day);
14442         
14443         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
14444         
14445         var nextMonth = new Date(prevMonth);
14446         
14447         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
14448         
14449         nextMonth = nextMonth.valueOf();
14450         
14451         var fillMonths = false;
14452         
14453         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
14454         
14455         while(prevMonth.valueOf() < nextMonth) {
14456             var clsName = '';
14457             
14458             if (prevMonth.getUTCDay() === this.weekStart) {
14459                 if(fillMonths){
14460                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
14461                 }
14462                     
14463                 fillMonths = {
14464                     tag: 'tr',
14465                     cn: []
14466                 };
14467                 
14468                 if(this.calendarWeeks){
14469                     // ISO 8601: First week contains first thursday.
14470                     // ISO also states week starts on Monday, but we can be more abstract here.
14471                     var
14472                     // Start of current week: based on weekstart/current date
14473                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
14474                     // Thursday of this week
14475                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
14476                     // First Thursday of year, year from thursday
14477                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
14478                     // Calendar week: ms between thursdays, div ms per day, div 7 days
14479                     calWeek =  (th - yth) / 864e5 / 7 + 1;
14480                     
14481                     fillMonths.cn.push({
14482                         tag: 'td',
14483                         cls: 'cw',
14484                         html: calWeek
14485                     });
14486                 }
14487             }
14488             
14489             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
14490                 clsName += ' old';
14491             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
14492                 clsName += ' new';
14493             }
14494             if (this.todayHighlight &&
14495                 prevMonth.getUTCFullYear() == today.getFullYear() &&
14496                 prevMonth.getUTCMonth() == today.getMonth() &&
14497                 prevMonth.getUTCDate() == today.getDate()) {
14498                 clsName += ' today';
14499             }
14500             
14501             if (currentDate && prevMonth.valueOf() === currentDate) {
14502                 clsName += ' active';
14503             }
14504             
14505             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
14506                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
14507                     clsName += ' disabled';
14508             }
14509             
14510             fillMonths.cn.push({
14511                 tag: 'td',
14512                 cls: 'day ' + clsName,
14513                 html: prevMonth.getDate()
14514             })
14515             
14516             prevMonth.setDate(prevMonth.getDate()+1);
14517         }
14518           
14519         var currentYear = this.date && this.date.getUTCFullYear();
14520         var currentMonth = this.date && this.date.getUTCMonth();
14521         
14522         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
14523         
14524         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
14525             v.removeClass('active');
14526             
14527             if(currentYear === year && k === currentMonth){
14528                 v.addClass('active');
14529             }
14530             
14531             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
14532                 v.addClass('disabled');
14533             }
14534             
14535         });
14536         
14537         
14538         year = parseInt(year/10, 10) * 10;
14539         
14540         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
14541         
14542         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
14543         
14544         year -= 1;
14545         for (var i = -1; i < 11; i++) {
14546             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
14547                 tag: 'span',
14548                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
14549                 html: year
14550             })
14551             
14552             year += 1;
14553         }
14554     },
14555     
14556     showMode: function(dir) 
14557     {
14558         if (dir) {
14559             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
14560         }
14561         Roo.each(this.picker().select('>div',true).elements, function(v){
14562             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14563             v.hide();
14564         });
14565         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
14566     },
14567     
14568     place: function()
14569     {
14570         if(this.isInline) return;
14571         
14572         this.picker().removeClass(['bottom', 'top']);
14573         
14574         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
14575             /*
14576              * place to the top of element!
14577              *
14578              */
14579             
14580             this.picker().addClass('top');
14581             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
14582             
14583             return;
14584         }
14585         
14586         this.picker().addClass('bottom');
14587         
14588         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
14589     },
14590     
14591     parseDate : function(value)
14592     {
14593         if(!value || value instanceof Date){
14594             return value;
14595         }
14596         var v = Date.parseDate(value, this.format);
14597         if (!v && this.useIso) {
14598             v = Date.parseDate(value, 'Y-m-d');
14599         }
14600         if(!v && this.altFormats){
14601             if(!this.altFormatsArray){
14602                 this.altFormatsArray = this.altFormats.split("|");
14603             }
14604             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
14605                 v = Date.parseDate(value, this.altFormatsArray[i]);
14606             }
14607         }
14608         return v;
14609     },
14610     
14611     formatDate : function(date, fmt)
14612     {
14613         return (!date || !(date instanceof Date)) ?
14614         date : date.dateFormat(fmt || this.format);
14615     },
14616     
14617     onFocus : function()
14618     {
14619         Roo.bootstrap.DateField.superclass.onFocus.call(this);
14620         this.show();
14621     },
14622     
14623     onBlur : function()
14624     {
14625         Roo.bootstrap.DateField.superclass.onBlur.call(this);
14626         
14627         var d = this.inputEl().getValue();
14628         
14629         if(d && d.length){
14630             this.setValue(d);
14631         }
14632                 
14633         this.hide();
14634     },
14635     
14636     show : function()
14637     {
14638         this.picker().show();
14639         this.update();
14640         this.place();
14641         
14642         this.fireEvent('show', this, this.date);
14643     },
14644     
14645     hide : function()
14646     {
14647         if(this.isInline) return;
14648         this.picker().hide();
14649         this.viewMode = this.startViewMode;
14650         this.showMode();
14651         
14652         this.fireEvent('hide', this, this.date);
14653         
14654     },
14655     
14656     onMousedown: function(e)
14657     {
14658         e.stopPropagation();
14659         e.preventDefault();
14660     },
14661     
14662     keyup: function(e)
14663     {
14664         Roo.bootstrap.DateField.superclass.keyup.call(this);
14665         this.update();
14666     },
14667
14668     setValue: function(v)
14669     {
14670         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
14671         
14672         var d = new Date(v);
14673         
14674         if(isNaN(d.getTime())){
14675             return;
14676         }
14677         
14678         this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
14679
14680         this.update();
14681
14682         this.fireEvent('select', this, this.date);
14683         
14684     },
14685     
14686     getValue: function()
14687     {
14688         return this.formatDate(this.date);
14689     },
14690     
14691     fireKey: function(e)
14692     {
14693         if (!this.picker().isVisible()){
14694             if (e.keyCode == 27) // allow escape to hide and re-show picker
14695                 this.show();
14696             return;
14697         }
14698         
14699         var dateChanged = false,
14700         dir, day, month,
14701         newDate, newViewDate;
14702         
14703         switch(e.keyCode){
14704             case 27: // escape
14705                 this.hide();
14706                 e.preventDefault();
14707                 break;
14708             case 37: // left
14709             case 39: // right
14710                 if (!this.keyboardNavigation) break;
14711                 dir = e.keyCode == 37 ? -1 : 1;
14712                 
14713                 if (e.ctrlKey){
14714                     newDate = this.moveYear(this.date, dir);
14715                     newViewDate = this.moveYear(this.viewDate, dir);
14716                 } else if (e.shiftKey){
14717                     newDate = this.moveMonth(this.date, dir);
14718                     newViewDate = this.moveMonth(this.viewDate, dir);
14719                 } else {
14720                     newDate = new Date(this.date);
14721                     newDate.setUTCDate(this.date.getUTCDate() + dir);
14722                     newViewDate = new Date(this.viewDate);
14723                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
14724                 }
14725                 if (this.dateWithinRange(newDate)){
14726                     this.date = newDate;
14727                     this.viewDate = newViewDate;
14728                     this.setValue(this.formatDate(this.date));
14729 //                    this.update();
14730                     e.preventDefault();
14731                     dateChanged = true;
14732                 }
14733                 break;
14734             case 38: // up
14735             case 40: // down
14736                 if (!this.keyboardNavigation) break;
14737                 dir = e.keyCode == 38 ? -1 : 1;
14738                 if (e.ctrlKey){
14739                     newDate = this.moveYear(this.date, dir);
14740                     newViewDate = this.moveYear(this.viewDate, dir);
14741                 } else if (e.shiftKey){
14742                     newDate = this.moveMonth(this.date, dir);
14743                     newViewDate = this.moveMonth(this.viewDate, dir);
14744                 } else {
14745                     newDate = new Date(this.date);
14746                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
14747                     newViewDate = new Date(this.viewDate);
14748                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
14749                 }
14750                 if (this.dateWithinRange(newDate)){
14751                     this.date = newDate;
14752                     this.viewDate = newViewDate;
14753                     this.setValue(this.formatDate(this.date));
14754 //                    this.update();
14755                     e.preventDefault();
14756                     dateChanged = true;
14757                 }
14758                 break;
14759             case 13: // enter
14760                 this.setValue(this.formatDate(this.date));
14761                 this.hide();
14762                 e.preventDefault();
14763                 break;
14764             case 9: // tab
14765                 this.setValue(this.formatDate(this.date));
14766                 this.hide();
14767                 break;
14768             case 16: // shift
14769             case 17: // ctrl
14770             case 18: // alt
14771                 break;
14772             default :
14773                 this.hide();
14774                 
14775         }
14776     },
14777     
14778     
14779     onClick: function(e) 
14780     {
14781         e.stopPropagation();
14782         e.preventDefault();
14783         
14784         var target = e.getTarget();
14785         
14786         if(target.nodeName.toLowerCase() === 'i'){
14787             target = Roo.get(target).dom.parentNode;
14788         }
14789         
14790         var nodeName = target.nodeName;
14791         var className = target.className;
14792         var html = target.innerHTML;
14793         
14794         switch(nodeName.toLowerCase()) {
14795             case 'th':
14796                 switch(className) {
14797                     case 'switch':
14798                         this.showMode(1);
14799                         break;
14800                     case 'prev':
14801                     case 'next':
14802                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
14803                         switch(this.viewMode){
14804                                 case 0:
14805                                         this.viewDate = this.moveMonth(this.viewDate, dir);
14806                                         break;
14807                                 case 1:
14808                                 case 2:
14809                                         this.viewDate = this.moveYear(this.viewDate, dir);
14810                                         break;
14811                         }
14812                         this.fill();
14813                         break;
14814                     case 'today':
14815                         var date = new Date();
14816                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
14817 //                        this.fill()
14818                         this.setValue(this.formatDate(this.date));
14819                         
14820                         this.hide();
14821                         break;
14822                 }
14823                 break;
14824             case 'span':
14825                 if (className.indexOf('disabled') === -1) {
14826                     this.viewDate.setUTCDate(1);
14827                     if (className.indexOf('month') !== -1) {
14828                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
14829                     } else {
14830                         var year = parseInt(html, 10) || 0;
14831                         this.viewDate.setUTCFullYear(year);
14832                         
14833                     }
14834                     this.showMode(-1);
14835                     this.fill();
14836                 }
14837                 break;
14838                 
14839             case 'td':
14840                 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
14841                     var day = parseInt(html, 10) || 1;
14842                     var year = this.viewDate.getUTCFullYear(),
14843                         month = this.viewDate.getUTCMonth();
14844
14845                     if (className.indexOf('old') !== -1) {
14846                         if(month === 0 ){
14847                             month = 11;
14848                             year -= 1;
14849                         }else{
14850                             month -= 1;
14851                         }
14852                     } else if (className.indexOf('new') !== -1) {
14853                         if (month == 11) {
14854                             month = 0;
14855                             year += 1;
14856                         } else {
14857                             month += 1;
14858                         }
14859                     }
14860                     this.date = this.UTCDate(year, month, day,0,0,0,0);
14861                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
14862 //                    this.fill();
14863                     this.setValue(this.formatDate(this.date));
14864                     this.hide();
14865                 }
14866                 break;
14867         }
14868     },
14869     
14870     setStartDate: function(startDate)
14871     {
14872         this.startDate = startDate || -Infinity;
14873         if (this.startDate !== -Infinity) {
14874             this.startDate = this.parseDate(this.startDate);
14875         }
14876         this.update();
14877         this.updateNavArrows();
14878     },
14879
14880     setEndDate: function(endDate)
14881     {
14882         this.endDate = endDate || Infinity;
14883         if (this.endDate !== Infinity) {
14884             this.endDate = this.parseDate(this.endDate);
14885         }
14886         this.update();
14887         this.updateNavArrows();
14888     },
14889     
14890     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
14891     {
14892         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
14893         if (typeof(this.daysOfWeekDisabled) !== 'object') {
14894             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
14895         }
14896         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
14897             return parseInt(d, 10);
14898         });
14899         this.update();
14900         this.updateNavArrows();
14901     },
14902     
14903     updateNavArrows: function() 
14904     {
14905         var d = new Date(this.viewDate),
14906         year = d.getUTCFullYear(),
14907         month = d.getUTCMonth();
14908         
14909         Roo.each(this.picker().select('.prev', true).elements, function(v){
14910             v.show();
14911             switch (this.viewMode) {
14912                 case 0:
14913
14914                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
14915                         v.hide();
14916                     }
14917                     break;
14918                 case 1:
14919                 case 2:
14920                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
14921                         v.hide();
14922                     }
14923                     break;
14924             }
14925         });
14926         
14927         Roo.each(this.picker().select('.next', true).elements, function(v){
14928             v.show();
14929             switch (this.viewMode) {
14930                 case 0:
14931
14932                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
14933                         v.hide();
14934                     }
14935                     break;
14936                 case 1:
14937                 case 2:
14938                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
14939                         v.hide();
14940                     }
14941                     break;
14942             }
14943         })
14944     },
14945     
14946     moveMonth: function(date, dir)
14947     {
14948         if (!dir) return date;
14949         var new_date = new Date(date.valueOf()),
14950         day = new_date.getUTCDate(),
14951         month = new_date.getUTCMonth(),
14952         mag = Math.abs(dir),
14953         new_month, test;
14954         dir = dir > 0 ? 1 : -1;
14955         if (mag == 1){
14956             test = dir == -1
14957             // If going back one month, make sure month is not current month
14958             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
14959             ? function(){
14960                 return new_date.getUTCMonth() == month;
14961             }
14962             // If going forward one month, make sure month is as expected
14963             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
14964             : function(){
14965                 return new_date.getUTCMonth() != new_month;
14966             };
14967             new_month = month + dir;
14968             new_date.setUTCMonth(new_month);
14969             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
14970             if (new_month < 0 || new_month > 11)
14971                 new_month = (new_month + 12) % 12;
14972         } else {
14973             // For magnitudes >1, move one month at a time...
14974             for (var i=0; i<mag; i++)
14975                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
14976                 new_date = this.moveMonth(new_date, dir);
14977             // ...then reset the day, keeping it in the new month
14978             new_month = new_date.getUTCMonth();
14979             new_date.setUTCDate(day);
14980             test = function(){
14981                 return new_month != new_date.getUTCMonth();
14982             };
14983         }
14984         // Common date-resetting loop -- if date is beyond end of month, make it
14985         // end of month
14986         while (test()){
14987             new_date.setUTCDate(--day);
14988             new_date.setUTCMonth(new_month);
14989         }
14990         return new_date;
14991     },
14992
14993     moveYear: function(date, dir)
14994     {
14995         return this.moveMonth(date, dir*12);
14996     },
14997
14998     dateWithinRange: function(date)
14999     {
15000         return date >= this.startDate && date <= this.endDate;
15001     },
15002
15003     
15004     remove: function() 
15005     {
15006         this.picker().remove();
15007     }
15008    
15009 });
15010
15011 Roo.apply(Roo.bootstrap.DateField,  {
15012     
15013     head : {
15014         tag: 'thead',
15015         cn: [
15016         {
15017             tag: 'tr',
15018             cn: [
15019             {
15020                 tag: 'th',
15021                 cls: 'prev',
15022                 html: '<i class="fa fa-arrow-left"/>'
15023             },
15024             {
15025                 tag: 'th',
15026                 cls: 'switch',
15027                 colspan: '5'
15028             },
15029             {
15030                 tag: 'th',
15031                 cls: 'next',
15032                 html: '<i class="fa fa-arrow-right"/>'
15033             }
15034
15035             ]
15036         }
15037         ]
15038     },
15039     
15040     content : {
15041         tag: 'tbody',
15042         cn: [
15043         {
15044             tag: 'tr',
15045             cn: [
15046             {
15047                 tag: 'td',
15048                 colspan: '7'
15049             }
15050             ]
15051         }
15052         ]
15053     },
15054     
15055     footer : {
15056         tag: 'tfoot',
15057         cn: [
15058         {
15059             tag: 'tr',
15060             cn: [
15061             {
15062                 tag: 'th',
15063                 colspan: '7',
15064                 cls: 'today'
15065             }
15066                     
15067             ]
15068         }
15069         ]
15070     },
15071     
15072     dates:{
15073         en: {
15074             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
15075             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
15076             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
15077             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
15078             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
15079             today: "Today"
15080         }
15081     },
15082     
15083     modes: [
15084     {
15085         clsName: 'days',
15086         navFnc: 'Month',
15087         navStep: 1
15088     },
15089     {
15090         clsName: 'months',
15091         navFnc: 'FullYear',
15092         navStep: 1
15093     },
15094     {
15095         clsName: 'years',
15096         navFnc: 'FullYear',
15097         navStep: 10
15098     }]
15099 });
15100
15101 Roo.apply(Roo.bootstrap.DateField,  {
15102   
15103     template : {
15104         tag: 'div',
15105         cls: 'datepicker dropdown-menu',
15106         cn: [
15107         {
15108             tag: 'div',
15109             cls: 'datepicker-days',
15110             cn: [
15111             {
15112                 tag: 'table',
15113                 cls: 'table-condensed',
15114                 cn:[
15115                 Roo.bootstrap.DateField.head,
15116                 {
15117                     tag: 'tbody'
15118                 },
15119                 Roo.bootstrap.DateField.footer
15120                 ]
15121             }
15122             ]
15123         },
15124         {
15125             tag: 'div',
15126             cls: 'datepicker-months',
15127             cn: [
15128             {
15129                 tag: 'table',
15130                 cls: 'table-condensed',
15131                 cn:[
15132                 Roo.bootstrap.DateField.head,
15133                 Roo.bootstrap.DateField.content,
15134                 Roo.bootstrap.DateField.footer
15135                 ]
15136             }
15137             ]
15138         },
15139         {
15140             tag: 'div',
15141             cls: 'datepicker-years',
15142             cn: [
15143             {
15144                 tag: 'table',
15145                 cls: 'table-condensed',
15146                 cn:[
15147                 Roo.bootstrap.DateField.head,
15148                 Roo.bootstrap.DateField.content,
15149                 Roo.bootstrap.DateField.footer
15150                 ]
15151             }
15152             ]
15153         }
15154         ]
15155     }
15156 });
15157
15158  
15159
15160  /*
15161  * - LGPL
15162  *
15163  * TimeField
15164  * 
15165  */
15166
15167 /**
15168  * @class Roo.bootstrap.TimeField
15169  * @extends Roo.bootstrap.Input
15170  * Bootstrap DateField class
15171  * 
15172  * 
15173  * @constructor
15174  * Create a new TimeField
15175  * @param {Object} config The config object
15176  */
15177
15178 Roo.bootstrap.TimeField = function(config){
15179     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
15180     this.addEvents({
15181             /**
15182              * @event show
15183              * Fires when this field show.
15184              * @param {Roo.bootstrap.DateField} this
15185              * @param {Mixed} date The date value
15186              */
15187             show : true,
15188             /**
15189              * @event show
15190              * Fires when this field hide.
15191              * @param {Roo.bootstrap.DateField} this
15192              * @param {Mixed} date The date value
15193              */
15194             hide : true,
15195             /**
15196              * @event select
15197              * Fires when select a date.
15198              * @param {Roo.bootstrap.DateField} this
15199              * @param {Mixed} date The date value
15200              */
15201             select : true
15202         });
15203 };
15204
15205 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
15206     
15207     /**
15208      * @cfg {String} format
15209      * The default time format string which can be overriden for localization support.  The format must be
15210      * valid according to {@link Date#parseDate} (defaults to 'H:i').
15211      */
15212     format : "H:i",
15213        
15214     onRender: function(ct, position)
15215     {
15216         
15217         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
15218                 
15219         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
15220         
15221         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15222         
15223         this.pop = this.picker().select('>.datepicker-time',true).first();
15224         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block' 
15225         
15226         this.picker().on('mousedown', this.onMousedown, this);
15227         this.picker().on('click', this.onClick, this);
15228         
15229         this.picker().addClass('datepicker-dropdown');
15230     
15231         this.fillTime();
15232         this.update();
15233             
15234         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
15235         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
15236         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
15237         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
15238         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
15239         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
15240
15241     },
15242     
15243     fireKey: function(e){
15244         if (!this.picker().isVisible()){
15245             if (e.keyCode == 27) // allow escape to hide and re-show picker
15246                 this.show();
15247             return;
15248         }
15249
15250         e.preventDefault();
15251         
15252         switch(e.keyCode){
15253             case 27: // escape
15254                 this.hide();
15255                 break;
15256             case 37: // left
15257             case 39: // right
15258                 this.onTogglePeriod();
15259                 break;
15260             case 38: // up
15261                 this.onIncrementMinutes();
15262                 break;
15263             case 40: // down
15264                 this.onDecrementMinutes();
15265                 break;
15266             case 13: // enter
15267             case 9: // tab
15268                 this.setTime();
15269                 break;
15270         }
15271     },
15272     
15273     onClick: function(e) {
15274         e.stopPropagation();
15275         e.preventDefault();
15276     },
15277     
15278     picker : function()
15279     {
15280         return this.el.select('.datepicker', true).first();
15281     },
15282     
15283     fillTime: function()
15284     {    
15285         var time = this.pop.select('tbody', true).first();
15286         
15287         time.dom.innerHTML = '';
15288         
15289         time.createChild({
15290             tag: 'tr',
15291             cn: [
15292                 {
15293                     tag: 'td',
15294                     cn: [
15295                         {
15296                             tag: 'a',
15297                             href: '#',
15298                             cls: 'btn',
15299                             cn: [
15300                                 {
15301                                     tag: 'span',
15302                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
15303                                 }
15304                             ]
15305                         } 
15306                     ]
15307                 },
15308                 {
15309                     tag: 'td',
15310                     cls: 'separator'
15311                 },
15312                 {
15313                     tag: 'td',
15314                     cn: [
15315                         {
15316                             tag: 'a',
15317                             href: '#',
15318                             cls: 'btn',
15319                             cn: [
15320                                 {
15321                                     tag: 'span',
15322                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
15323                                 }
15324                             ]
15325                         }
15326                     ]
15327                 },
15328                 {
15329                     tag: 'td',
15330                     cls: 'separator'
15331                 }
15332             ]
15333         });
15334         
15335         time.createChild({
15336             tag: 'tr',
15337             cn: [
15338                 {
15339                     tag: 'td',
15340                     cn: [
15341                         {
15342                             tag: 'span',
15343                             cls: 'timepicker-hour',
15344                             html: '00'
15345                         }  
15346                     ]
15347                 },
15348                 {
15349                     tag: 'td',
15350                     cls: 'separator',
15351                     html: ':'
15352                 },
15353                 {
15354                     tag: 'td',
15355                     cn: [
15356                         {
15357                             tag: 'span',
15358                             cls: 'timepicker-minute',
15359                             html: '00'
15360                         }  
15361                     ]
15362                 },
15363                 {
15364                     tag: 'td',
15365                     cls: 'separator'
15366                 },
15367                 {
15368                     tag: 'td',
15369                     cn: [
15370                         {
15371                             tag: 'button',
15372                             type: 'button',
15373                             cls: 'btn btn-primary period',
15374                             html: 'AM'
15375                             
15376                         }
15377                     ]
15378                 }
15379             ]
15380         });
15381         
15382         time.createChild({
15383             tag: 'tr',
15384             cn: [
15385                 {
15386                     tag: 'td',
15387                     cn: [
15388                         {
15389                             tag: 'a',
15390                             href: '#',
15391                             cls: 'btn',
15392                             cn: [
15393                                 {
15394                                     tag: 'span',
15395                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
15396                                 }
15397                             ]
15398                         }
15399                     ]
15400                 },
15401                 {
15402                     tag: 'td',
15403                     cls: 'separator'
15404                 },
15405                 {
15406                     tag: 'td',
15407                     cn: [
15408                         {
15409                             tag: 'a',
15410                             href: '#',
15411                             cls: 'btn',
15412                             cn: [
15413                                 {
15414                                     tag: 'span',
15415                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
15416                                 }
15417                             ]
15418                         }
15419                     ]
15420                 },
15421                 {
15422                     tag: 'td',
15423                     cls: 'separator'
15424                 }
15425             ]
15426         });
15427         
15428     },
15429     
15430     update: function()
15431     {
15432         
15433         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
15434         
15435         this.fill();
15436     },
15437     
15438     fill: function() 
15439     {
15440         var hours = this.time.getHours();
15441         var minutes = this.time.getMinutes();
15442         var period = 'AM';
15443         
15444         if(hours > 11){
15445             period = 'PM';
15446         }
15447         
15448         if(hours == 0){
15449             hours = 12;
15450         }
15451         
15452         
15453         if(hours > 12){
15454             hours = hours - 12;
15455         }
15456         
15457         if(hours < 10){
15458             hours = '0' + hours;
15459         }
15460         
15461         if(minutes < 10){
15462             minutes = '0' + minutes;
15463         }
15464         
15465         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
15466         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
15467         this.pop.select('button', true).first().dom.innerHTML = period;
15468         
15469     },
15470     
15471     place: function()
15472     {   
15473         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
15474         
15475         var cls = ['bottom'];
15476         
15477         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
15478             cls.pop();
15479             cls.push('top');
15480         }
15481         
15482         cls.push('right');
15483         
15484         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
15485             cls.pop();
15486             cls.push('left');
15487         }
15488         
15489         this.picker().addClass(cls.join('-'));
15490         
15491         var _this = this;
15492         
15493         Roo.each(cls, function(c){
15494             if(c == 'bottom'){
15495                 _this.picker().setTop(_this.inputEl().getHeight());
15496                 return;
15497             }
15498             if(c == 'top'){
15499                 _this.picker().setTop(0 - _this.picker().getHeight());
15500                 return;
15501             }
15502             
15503             if(c == 'left'){
15504                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
15505                 return;
15506             }
15507             if(c == 'right'){
15508                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
15509                 return;
15510             }
15511         });
15512         
15513     },
15514   
15515     onFocus : function()
15516     {
15517         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
15518         this.show();
15519     },
15520     
15521     onBlur : function()
15522     {
15523         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
15524         this.hide();
15525     },
15526     
15527     show : function()
15528     {
15529         this.picker().show();
15530         this.pop.show();
15531         this.update();
15532         this.place();
15533         
15534         this.fireEvent('show', this, this.date);
15535     },
15536     
15537     hide : function()
15538     {
15539         this.picker().hide();
15540         this.pop.hide();
15541         
15542         this.fireEvent('hide', this, this.date);
15543     },
15544     
15545     setTime : function()
15546     {
15547         this.hide();
15548         this.setValue(this.time.format(this.format));
15549         
15550         this.fireEvent('select', this, this.date);
15551         
15552         
15553     },
15554     
15555     onMousedown: function(e){
15556         e.stopPropagation();
15557         e.preventDefault();
15558     },
15559     
15560     onIncrementHours: function()
15561     {
15562         Roo.log('onIncrementHours');
15563         this.time = this.time.add(Date.HOUR, 1);
15564         this.update();
15565         
15566     },
15567     
15568     onDecrementHours: function()
15569     {
15570         Roo.log('onDecrementHours');
15571         this.time = this.time.add(Date.HOUR, -1);
15572         this.update();
15573     },
15574     
15575     onIncrementMinutes: function()
15576     {
15577         Roo.log('onIncrementMinutes');
15578         this.time = this.time.add(Date.MINUTE, 1);
15579         this.update();
15580     },
15581     
15582     onDecrementMinutes: function()
15583     {
15584         Roo.log('onDecrementMinutes');
15585         this.time = this.time.add(Date.MINUTE, -1);
15586         this.update();
15587     },
15588     
15589     onTogglePeriod: function()
15590     {
15591         Roo.log('onTogglePeriod');
15592         this.time = this.time.add(Date.HOUR, 12);
15593         this.update();
15594     }
15595     
15596    
15597 });
15598
15599 Roo.apply(Roo.bootstrap.TimeField,  {
15600     
15601     content : {
15602         tag: 'tbody',
15603         cn: [
15604             {
15605                 tag: 'tr',
15606                 cn: [
15607                 {
15608                     tag: 'td',
15609                     colspan: '7'
15610                 }
15611                 ]
15612             }
15613         ]
15614     },
15615     
15616     footer : {
15617         tag: 'tfoot',
15618         cn: [
15619             {
15620                 tag: 'tr',
15621                 cn: [
15622                 {
15623                     tag: 'th',
15624                     colspan: '7',
15625                     cls: '',
15626                     cn: [
15627                         {
15628                             tag: 'button',
15629                             cls: 'btn btn-info ok',
15630                             html: 'OK'
15631                         }
15632                     ]
15633                 }
15634
15635                 ]
15636             }
15637         ]
15638     }
15639 });
15640
15641 Roo.apply(Roo.bootstrap.TimeField,  {
15642   
15643     template : {
15644         tag: 'div',
15645         cls: 'datepicker dropdown-menu',
15646         cn: [
15647             {
15648                 tag: 'div',
15649                 cls: 'datepicker-time',
15650                 cn: [
15651                 {
15652                     tag: 'table',
15653                     cls: 'table-condensed',
15654                     cn:[
15655                     Roo.bootstrap.TimeField.content,
15656                     Roo.bootstrap.TimeField.footer
15657                     ]
15658                 }
15659                 ]
15660             }
15661         ]
15662     }
15663 });
15664
15665  
15666
15667  /*
15668  * - LGPL
15669  *
15670  * CheckBox
15671  * 
15672  */
15673
15674 /**
15675  * @class Roo.bootstrap.CheckBox
15676  * @extends Roo.bootstrap.Input
15677  * Bootstrap CheckBox class
15678  * 
15679  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
15680  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
15681  * @cfg {String} boxLabel The text that appears beside the checkbox
15682  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
15683  * @cfg {Boolean} checked initnal the element
15684  * 
15685  * 
15686  * @constructor
15687  * Create a new CheckBox
15688  * @param {Object} config The config object
15689  */
15690
15691 Roo.bootstrap.CheckBox = function(config){
15692     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
15693    
15694         this.addEvents({
15695             /**
15696             * @event check
15697             * Fires when the element is checked or unchecked.
15698             * @param {Roo.bootstrap.CheckBox} this This input
15699             * @param {Boolean} checked The new checked value
15700             */
15701            check : true
15702         });
15703 };
15704
15705 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
15706     
15707     inputType: 'checkbox',
15708     inputValue: 1,
15709     valueOff: 0,
15710     boxLabel: false,
15711     checked: false,
15712     weight : false,
15713     
15714     getAutoCreate : function()
15715     {
15716         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
15717         
15718         var id = Roo.id();
15719         
15720         var cfg = {};
15721         
15722         cfg.cls = 'form-group checkbox' //input-group
15723         
15724         
15725         
15726         
15727         var input =  {
15728             tag: 'input',
15729             id : id,
15730             type : this.inputType,
15731             value : (!this.checked) ? this.valueOff : this.inputValue,
15732             cls : 'roo-checkbox', //'form-box',
15733             placeholder : this.placeholder || ''
15734             
15735         };
15736         
15737         if (this.weight) { // Validity check?
15738             cfg.cls += " checkbox-" + this.weight;
15739         }
15740         
15741         if (this.disabled) {
15742             input.disabled=true;
15743         }
15744         
15745         if(this.checked){
15746             input.checked = this.checked;
15747         }
15748         
15749         if (this.name) {
15750             input.name = this.name;
15751         }
15752         
15753         if (this.size) {
15754             input.cls += ' input-' + this.size;
15755         }
15756         
15757         var settings=this;
15758         ['xs','sm','md','lg'].map(function(size){
15759             if (settings[size]) {
15760                 cfg.cls += ' col-' + size + '-' + settings[size];
15761             }
15762         });
15763         
15764        
15765         
15766         var inputblock = input;
15767         
15768         
15769         
15770         
15771         if (this.before || this.after) {
15772             
15773             inputblock = {
15774                 cls : 'input-group',
15775                 cn :  [] 
15776             };
15777             if (this.before) {
15778                 inputblock.cn.push({
15779                     tag :'span',
15780                     cls : 'input-group-addon',
15781                     html : this.before
15782                 });
15783             }
15784             inputblock.cn.push(input);
15785             if (this.after) {
15786                 inputblock.cn.push({
15787                     tag :'span',
15788                     cls : 'input-group-addon',
15789                     html : this.after
15790                 });
15791             }
15792             
15793         };
15794         
15795         if (align ==='left' && this.fieldLabel.length) {
15796                 Roo.log("left and has label");
15797                 cfg.cn = [
15798                     
15799                     {
15800                         tag: 'label',
15801                         'for' :  id,
15802                         cls : 'control-label col-md-' + this.labelWidth,
15803                         html : this.fieldLabel
15804                         
15805                     },
15806                     {
15807                         cls : "col-md-" + (12 - this.labelWidth), 
15808                         cn: [
15809                             inputblock
15810                         ]
15811                     }
15812                     
15813                 ];
15814         } else if ( this.fieldLabel.length) {
15815                 Roo.log(" label");
15816                 cfg.cn = [
15817                    
15818                     {
15819                         tag: this.boxLabel ? 'span' : 'label',
15820                         'for': id,
15821                         cls: 'control-label box-input-label',
15822                         //cls : 'input-group-addon',
15823                         html : this.fieldLabel
15824                         
15825                     },
15826                     
15827                     inputblock
15828                     
15829                 ];
15830
15831         } else {
15832             
15833                 Roo.log(" no label && no align");
15834                 cfg.cn = [  inputblock ] ;
15835                 
15836                 
15837         };
15838          if(this.boxLabel){
15839             cfg.cn.push( {
15840                 tag: 'label',
15841                 'for': id,
15842                 cls: 'box-label',
15843                 html: this.boxLabel
15844                 
15845             });
15846         }
15847         
15848         
15849        
15850         return cfg;
15851         
15852     },
15853     
15854     /**
15855      * return the real input element.
15856      */
15857     inputEl: function ()
15858     {
15859         return this.el.select('input.roo-checkbox',true).first();
15860     },
15861     
15862     label: function()
15863     {
15864         return this.el.select('label.control-label',true).first();
15865     },
15866     
15867     initEvents : function()
15868     {
15869 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
15870         
15871         this.inputEl().on('click', this.onClick,  this);
15872         
15873     },
15874     
15875     onClick : function()
15876     {   
15877         this.setChecked(!this.checked);
15878     },
15879     
15880     setChecked : function(state,suppressEvent)
15881     {
15882         this.checked = state;
15883         
15884         this.inputEl().dom.checked = state;
15885         
15886         if(suppressEvent !== true){
15887             this.fireEvent('check', this, state);
15888         }
15889         
15890         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
15891         
15892     },
15893     
15894     setValue : function(v,suppressEvent)
15895     {
15896         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
15897     }
15898     
15899 });
15900
15901  
15902 /*
15903  * - LGPL
15904  *
15905  * Radio
15906  * 
15907  */
15908
15909 /**
15910  * @class Roo.bootstrap.Radio
15911  * @extends Roo.bootstrap.CheckBox
15912  * Bootstrap Radio class
15913
15914  * @constructor
15915  * Create a new Radio
15916  * @param {Object} config The config object
15917  */
15918
15919 Roo.bootstrap.Radio = function(config){
15920     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
15921    
15922 };
15923
15924 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
15925     
15926     inputType: 'radio',
15927     inputValue: '',
15928     valueOff: '',
15929     
15930     getAutoCreate : function()
15931     {
15932         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
15933         
15934         var id = Roo.id();
15935         
15936         var cfg = {};
15937         
15938         cfg.cls = 'form-group radio' //input-group
15939         
15940         var input =  {
15941             tag: 'input',
15942             id : id,
15943             type : this.inputType,
15944             value : (!this.checked) ? this.valueOff : this.inputValue,
15945             cls : 'roo-radio',
15946             placeholder : this.placeholder || ''
15947             
15948         };
15949           if (this.weight) { // Validity check?
15950             cfg.cls += " radio-" + this.weight;
15951         }
15952         if (this.disabled) {
15953             input.disabled=true;
15954         }
15955         
15956         if(this.checked){
15957             input.checked = this.checked;
15958         }
15959         
15960         if (this.name) {
15961             input.name = this.name;
15962         }
15963         
15964         if (this.size) {
15965             input.cls += ' input-' + this.size;
15966         }
15967         
15968         var settings=this;
15969         ['xs','sm','md','lg'].map(function(size){
15970             if (settings[size]) {
15971                 cfg.cls += ' col-' + size + '-' + settings[size];
15972             }
15973         });
15974         
15975         var inputblock = input;
15976         
15977         if (this.before || this.after) {
15978             
15979             inputblock = {
15980                 cls : 'input-group',
15981                 cn :  [] 
15982             };
15983             if (this.before) {
15984                 inputblock.cn.push({
15985                     tag :'span',
15986                     cls : 'input-group-addon',
15987                     html : this.before
15988                 });
15989             }
15990             inputblock.cn.push(input);
15991             if (this.after) {
15992                 inputblock.cn.push({
15993                     tag :'span',
15994                     cls : 'input-group-addon',
15995                     html : this.after
15996                 });
15997             }
15998             
15999         };
16000         
16001         if (align ==='left' && this.fieldLabel.length) {
16002                 Roo.log("left and has label");
16003                 cfg.cn = [
16004                     
16005                     {
16006                         tag: 'label',
16007                         'for' :  id,
16008                         cls : 'control-label col-md-' + this.labelWidth,
16009                         html : this.fieldLabel
16010                         
16011                     },
16012                     {
16013                         cls : "col-md-" + (12 - this.labelWidth), 
16014                         cn: [
16015                             inputblock
16016                         ]
16017                     }
16018                     
16019                 ];
16020         } else if ( this.fieldLabel.length) {
16021                 Roo.log(" label");
16022                  cfg.cn = [
16023                    
16024                     {
16025                         tag: 'label',
16026                         'for': id,
16027                         cls: 'control-label box-input-label',
16028                         //cls : 'input-group-addon',
16029                         html : this.fieldLabel
16030                         
16031                     },
16032                     
16033                     inputblock
16034                     
16035                 ];
16036
16037         } else {
16038             
16039                    Roo.log(" no label && no align");
16040                 cfg.cn = [
16041                     
16042                         inputblock
16043                     
16044                 ];
16045                 
16046                 
16047         };
16048         
16049         if(this.boxLabel){
16050             cfg.cn.push({
16051                 tag: 'label',
16052                 'for': id,
16053                 cls: 'box-label',
16054                 html: this.boxLabel
16055             })
16056         }
16057         
16058         return cfg;
16059         
16060     },
16061     inputEl: function ()
16062     {
16063         return this.el.select('input.roo-radio',true).first();
16064     },
16065     onClick : function()
16066     {   
16067         this.setChecked(true);
16068     },
16069     
16070     setChecked : function(state,suppressEvent)
16071     {
16072         if(state){
16073             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
16074                 v.dom.checked = false;
16075             });
16076         }
16077         
16078         this.checked = state;
16079         this.inputEl().dom.checked = state;
16080         
16081         if(suppressEvent !== true){
16082             this.fireEvent('check', this, state);
16083         }
16084         
16085         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
16086         
16087     },
16088     
16089     getGroupValue : function()
16090     {
16091         var value = ''
16092         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
16093             if(v.dom.checked == true){
16094                 value = v.dom.value;
16095             }
16096         });
16097         
16098         return value;
16099     },
16100     
16101     /**
16102      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
16103      * @return {Mixed} value The field value
16104      */
16105     getValue : function(){
16106         return this.getGroupValue();
16107     }
16108     
16109 });
16110
16111  
16112 //<script type="text/javascript">
16113
16114 /*
16115  * Based  Ext JS Library 1.1.1
16116  * Copyright(c) 2006-2007, Ext JS, LLC.
16117  * LGPL
16118  *
16119  */
16120  
16121 /**
16122  * @class Roo.HtmlEditorCore
16123  * @extends Roo.Component
16124  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
16125  *
16126  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
16127  */
16128
16129 Roo.HtmlEditorCore = function(config){
16130     
16131     
16132     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
16133     this.addEvents({
16134         /**
16135          * @event initialize
16136          * Fires when the editor is fully initialized (including the iframe)
16137          * @param {Roo.HtmlEditorCore} this
16138          */
16139         initialize: true,
16140         /**
16141          * @event activate
16142          * Fires when the editor is first receives the focus. Any insertion must wait
16143          * until after this event.
16144          * @param {Roo.HtmlEditorCore} this
16145          */
16146         activate: true,
16147          /**
16148          * @event beforesync
16149          * Fires before the textarea is updated with content from the editor iframe. Return false
16150          * to cancel the sync.
16151          * @param {Roo.HtmlEditorCore} this
16152          * @param {String} html
16153          */
16154         beforesync: true,
16155          /**
16156          * @event beforepush
16157          * Fires before the iframe editor is updated with content from the textarea. Return false
16158          * to cancel the push.
16159          * @param {Roo.HtmlEditorCore} this
16160          * @param {String} html
16161          */
16162         beforepush: true,
16163          /**
16164          * @event sync
16165          * Fires when the textarea is updated with content from the editor iframe.
16166          * @param {Roo.HtmlEditorCore} this
16167          * @param {String} html
16168          */
16169         sync: true,
16170          /**
16171          * @event push
16172          * Fires when the iframe editor is updated with content from the textarea.
16173          * @param {Roo.HtmlEditorCore} this
16174          * @param {String} html
16175          */
16176         push: true,
16177         
16178         /**
16179          * @event editorevent
16180          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
16181          * @param {Roo.HtmlEditorCore} this
16182          */
16183         editorevent: true
16184     });
16185      
16186 };
16187
16188
16189 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
16190
16191
16192      /**
16193      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
16194      */
16195     
16196     owner : false,
16197     
16198      /**
16199      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
16200      *                        Roo.resizable.
16201      */
16202     resizable : false,
16203      /**
16204      * @cfg {Number} height (in pixels)
16205      */   
16206     height: 300,
16207    /**
16208      * @cfg {Number} width (in pixels)
16209      */   
16210     width: 500,
16211     
16212     /**
16213      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
16214      * 
16215      */
16216     stylesheets: false,
16217     
16218     // id of frame..
16219     frameId: false,
16220     
16221     // private properties
16222     validationEvent : false,
16223     deferHeight: true,
16224     initialized : false,
16225     activated : false,
16226     sourceEditMode : false,
16227     onFocus : Roo.emptyFn,
16228     iframePad:3,
16229     hideMode:'offsets',
16230     
16231     clearUp: true,
16232     
16233      
16234     
16235
16236     /**
16237      * Protected method that will not generally be called directly. It
16238      * is called when the editor initializes the iframe with HTML contents. Override this method if you
16239      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
16240      */
16241     getDocMarkup : function(){
16242         // body styles..
16243         var st = '';
16244         Roo.log(this.stylesheets);
16245         
16246         // inherit styels from page...?? 
16247         if (this.stylesheets === false) {
16248             
16249             Roo.get(document.head).select('style').each(function(node) {
16250                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16251             });
16252             
16253             Roo.get(document.head).select('link').each(function(node) { 
16254                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16255             });
16256             
16257         } else if (!this.stylesheets.length) {
16258                 // simple..
16259                 st = '<style type="text/css">' +
16260                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16261                    '</style>';
16262         } else {
16263             Roo.each(this.stylesheets, function(s) {
16264                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
16265             });
16266             
16267         }
16268         
16269         st +=  '<style type="text/css">' +
16270             'IMG { cursor: pointer } ' +
16271         '</style>';
16272
16273         
16274         return '<html><head>' + st  +
16275             //<style type="text/css">' +
16276             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16277             //'</style>' +
16278             ' </head><body class="roo-htmleditor-body"></body></html>';
16279     },
16280
16281     // private
16282     onRender : function(ct, position)
16283     {
16284         var _t = this;
16285         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
16286         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
16287         
16288         
16289         this.el.dom.style.border = '0 none';
16290         this.el.dom.setAttribute('tabIndex', -1);
16291         this.el.addClass('x-hidden hide');
16292         
16293         
16294         
16295         if(Roo.isIE){ // fix IE 1px bogus margin
16296             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
16297         }
16298        
16299         
16300         this.frameId = Roo.id();
16301         
16302          
16303         
16304         var iframe = this.owner.wrap.createChild({
16305             tag: 'iframe',
16306             cls: 'form-control', // bootstrap..
16307             id: this.frameId,
16308             name: this.frameId,
16309             frameBorder : 'no',
16310             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
16311         }, this.el
16312         );
16313         
16314         
16315         this.iframe = iframe.dom;
16316
16317          this.assignDocWin();
16318         
16319         this.doc.designMode = 'on';
16320        
16321         this.doc.open();
16322         this.doc.write(this.getDocMarkup());
16323         this.doc.close();
16324
16325         
16326         var task = { // must defer to wait for browser to be ready
16327             run : function(){
16328                 //console.log("run task?" + this.doc.readyState);
16329                 this.assignDocWin();
16330                 if(this.doc.body || this.doc.readyState == 'complete'){
16331                     try {
16332                         this.doc.designMode="on";
16333                     } catch (e) {
16334                         return;
16335                     }
16336                     Roo.TaskMgr.stop(task);
16337                     this.initEditor.defer(10, this);
16338                 }
16339             },
16340             interval : 10,
16341             duration: 10000,
16342             scope: this
16343         };
16344         Roo.TaskMgr.start(task);
16345
16346         
16347          
16348     },
16349
16350     // private
16351     onResize : function(w, h)
16352     {
16353          Roo.log('resize: ' +w + ',' + h );
16354         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
16355         if(!this.iframe){
16356             return;
16357         }
16358         if(typeof w == 'number'){
16359             
16360             this.iframe.style.width = w + 'px';
16361         }
16362         if(typeof h == 'number'){
16363             
16364             this.iframe.style.height = h + 'px';
16365             if(this.doc){
16366                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
16367             }
16368         }
16369         
16370     },
16371
16372     /**
16373      * Toggles the editor between standard and source edit mode.
16374      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
16375      */
16376     toggleSourceEdit : function(sourceEditMode){
16377         
16378         this.sourceEditMode = sourceEditMode === true;
16379         
16380         if(this.sourceEditMode){
16381  
16382             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
16383             
16384         }else{
16385             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
16386             //this.iframe.className = '';
16387             this.deferFocus();
16388         }
16389         //this.setSize(this.owner.wrap.getSize());
16390         //this.fireEvent('editmodechange', this, this.sourceEditMode);
16391     },
16392
16393     
16394   
16395
16396     /**
16397      * Protected method that will not generally be called directly. If you need/want
16398      * custom HTML cleanup, this is the method you should override.
16399      * @param {String} html The HTML to be cleaned
16400      * return {String} The cleaned HTML
16401      */
16402     cleanHtml : function(html){
16403         html = String(html);
16404         if(html.length > 5){
16405             if(Roo.isSafari){ // strip safari nonsense
16406                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
16407             }
16408         }
16409         if(html == '&nbsp;'){
16410             html = '';
16411         }
16412         return html;
16413     },
16414
16415     /**
16416      * HTML Editor -> Textarea
16417      * Protected method that will not generally be called directly. Syncs the contents
16418      * of the editor iframe with the textarea.
16419      */
16420     syncValue : function(){
16421         if(this.initialized){
16422             var bd = (this.doc.body || this.doc.documentElement);
16423             //this.cleanUpPaste(); -- this is done else where and causes havoc..
16424             var html = bd.innerHTML;
16425             if(Roo.isSafari){
16426                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
16427                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
16428                 if(m && m[1]){
16429                     html = '<div style="'+m[0]+'">' + html + '</div>';
16430                 }
16431             }
16432             html = this.cleanHtml(html);
16433             // fix up the special chars.. normaly like back quotes in word...
16434             // however we do not want to do this with chinese..
16435             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
16436                 var cc = b.charCodeAt();
16437                 if (
16438                     (cc >= 0x4E00 && cc < 0xA000 ) ||
16439                     (cc >= 0x3400 && cc < 0x4E00 ) ||
16440                     (cc >= 0xf900 && cc < 0xfb00 )
16441                 ) {
16442                         return b;
16443                 }
16444                 return "&#"+cc+";" 
16445             });
16446             if(this.owner.fireEvent('beforesync', this, html) !== false){
16447                 this.el.dom.value = html;
16448                 this.owner.fireEvent('sync', this, html);
16449             }
16450         }
16451     },
16452
16453     /**
16454      * Protected method that will not generally be called directly. Pushes the value of the textarea
16455      * into the iframe editor.
16456      */
16457     pushValue : function(){
16458         if(this.initialized){
16459             var v = this.el.dom.value.trim();
16460             
16461 //            if(v.length < 1){
16462 //                v = '&#160;';
16463 //            }
16464             
16465             if(this.owner.fireEvent('beforepush', this, v) !== false){
16466                 var d = (this.doc.body || this.doc.documentElement);
16467                 d.innerHTML = v;
16468                 this.cleanUpPaste();
16469                 this.el.dom.value = d.innerHTML;
16470                 this.owner.fireEvent('push', this, v);
16471             }
16472         }
16473     },
16474
16475     // private
16476     deferFocus : function(){
16477         this.focus.defer(10, this);
16478     },
16479
16480     // doc'ed in Field
16481     focus : function(){
16482         if(this.win && !this.sourceEditMode){
16483             this.win.focus();
16484         }else{
16485             this.el.focus();
16486         }
16487     },
16488     
16489     assignDocWin: function()
16490     {
16491         var iframe = this.iframe;
16492         
16493          if(Roo.isIE){
16494             this.doc = iframe.contentWindow.document;
16495             this.win = iframe.contentWindow;
16496         } else {
16497             if (!Roo.get(this.frameId)) {
16498                 return;
16499             }
16500             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
16501             this.win = Roo.get(this.frameId).dom.contentWindow;
16502         }
16503     },
16504     
16505     // private
16506     initEditor : function(){
16507         //console.log("INIT EDITOR");
16508         this.assignDocWin();
16509         
16510         
16511         
16512         this.doc.designMode="on";
16513         this.doc.open();
16514         this.doc.write(this.getDocMarkup());
16515         this.doc.close();
16516         
16517         var dbody = (this.doc.body || this.doc.documentElement);
16518         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
16519         // this copies styles from the containing element into thsi one..
16520         // not sure why we need all of this..
16521         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
16522         
16523         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
16524         //ss['background-attachment'] = 'fixed'; // w3c
16525         dbody.bgProperties = 'fixed'; // ie
16526         //Roo.DomHelper.applyStyles(dbody, ss);
16527         Roo.EventManager.on(this.doc, {
16528             //'mousedown': this.onEditorEvent,
16529             'mouseup': this.onEditorEvent,
16530             'dblclick': this.onEditorEvent,
16531             'click': this.onEditorEvent,
16532             'keyup': this.onEditorEvent,
16533             buffer:100,
16534             scope: this
16535         });
16536         if(Roo.isGecko){
16537             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
16538         }
16539         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
16540             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
16541         }
16542         this.initialized = true;
16543
16544         this.owner.fireEvent('initialize', this);
16545         this.pushValue();
16546     },
16547
16548     // private
16549     onDestroy : function(){
16550         
16551         
16552         
16553         if(this.rendered){
16554             
16555             //for (var i =0; i < this.toolbars.length;i++) {
16556             //    // fixme - ask toolbars for heights?
16557             //    this.toolbars[i].onDestroy();
16558            // }
16559             
16560             //this.wrap.dom.innerHTML = '';
16561             //this.wrap.remove();
16562         }
16563     },
16564
16565     // private
16566     onFirstFocus : function(){
16567         
16568         this.assignDocWin();
16569         
16570         
16571         this.activated = true;
16572          
16573     
16574         if(Roo.isGecko){ // prevent silly gecko errors
16575             this.win.focus();
16576             var s = this.win.getSelection();
16577             if(!s.focusNode || s.focusNode.nodeType != 3){
16578                 var r = s.getRangeAt(0);
16579                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
16580                 r.collapse(true);
16581                 this.deferFocus();
16582             }
16583             try{
16584                 this.execCmd('useCSS', true);
16585                 this.execCmd('styleWithCSS', false);
16586             }catch(e){}
16587         }
16588         this.owner.fireEvent('activate', this);
16589     },
16590
16591     // private
16592     adjustFont: function(btn){
16593         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
16594         //if(Roo.isSafari){ // safari
16595         //    adjust *= 2;
16596        // }
16597         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
16598         if(Roo.isSafari){ // safari
16599             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
16600             v =  (v < 10) ? 10 : v;
16601             v =  (v > 48) ? 48 : v;
16602             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
16603             
16604         }
16605         
16606         
16607         v = Math.max(1, v+adjust);
16608         
16609         this.execCmd('FontSize', v  );
16610     },
16611
16612     onEditorEvent : function(e){
16613         this.owner.fireEvent('editorevent', this, e);
16614       //  this.updateToolbar();
16615         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
16616     },
16617
16618     insertTag : function(tg)
16619     {
16620         // could be a bit smarter... -> wrap the current selected tRoo..
16621         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
16622             
16623             range = this.createRange(this.getSelection());
16624             var wrappingNode = this.doc.createElement(tg.toLowerCase());
16625             wrappingNode.appendChild(range.extractContents());
16626             range.insertNode(wrappingNode);
16627
16628             return;
16629             
16630             
16631             
16632         }
16633         this.execCmd("formatblock",   tg);
16634         
16635     },
16636     
16637     insertText : function(txt)
16638     {
16639         
16640         
16641         var range = this.createRange();
16642         range.deleteContents();
16643                //alert(Sender.getAttribute('label'));
16644                
16645         range.insertNode(this.doc.createTextNode(txt));
16646     } ,
16647     
16648      
16649
16650     /**
16651      * Executes a Midas editor command on the editor document and performs necessary focus and
16652      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
16653      * @param {String} cmd The Midas command
16654      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16655      */
16656     relayCmd : function(cmd, value){
16657         this.win.focus();
16658         this.execCmd(cmd, value);
16659         this.owner.fireEvent('editorevent', this);
16660         //this.updateToolbar();
16661         this.owner.deferFocus();
16662     },
16663
16664     /**
16665      * Executes a Midas editor command directly on the editor document.
16666      * For visual commands, you should use {@link #relayCmd} instead.
16667      * <b>This should only be called after the editor is initialized.</b>
16668      * @param {String} cmd The Midas command
16669      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16670      */
16671     execCmd : function(cmd, value){
16672         this.doc.execCommand(cmd, false, value === undefined ? null : value);
16673         this.syncValue();
16674     },
16675  
16676  
16677    
16678     /**
16679      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
16680      * to insert tRoo.
16681      * @param {String} text | dom node.. 
16682      */
16683     insertAtCursor : function(text)
16684     {
16685         
16686         
16687         
16688         if(!this.activated){
16689             return;
16690         }
16691         /*
16692         if(Roo.isIE){
16693             this.win.focus();
16694             var r = this.doc.selection.createRange();
16695             if(r){
16696                 r.collapse(true);
16697                 r.pasteHTML(text);
16698                 this.syncValue();
16699                 this.deferFocus();
16700             
16701             }
16702             return;
16703         }
16704         */
16705         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
16706             this.win.focus();
16707             
16708             
16709             // from jquery ui (MIT licenced)
16710             var range, node;
16711             var win = this.win;
16712             
16713             if (win.getSelection && win.getSelection().getRangeAt) {
16714                 range = win.getSelection().getRangeAt(0);
16715                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
16716                 range.insertNode(node);
16717             } else if (win.document.selection && win.document.selection.createRange) {
16718                 // no firefox support
16719                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
16720                 win.document.selection.createRange().pasteHTML(txt);
16721             } else {
16722                 // no firefox support
16723                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
16724                 this.execCmd('InsertHTML', txt);
16725             } 
16726             
16727             this.syncValue();
16728             
16729             this.deferFocus();
16730         }
16731     },
16732  // private
16733     mozKeyPress : function(e){
16734         if(e.ctrlKey){
16735             var c = e.getCharCode(), cmd;
16736           
16737             if(c > 0){
16738                 c = String.fromCharCode(c).toLowerCase();
16739                 switch(c){
16740                     case 'b':
16741                         cmd = 'bold';
16742                         break;
16743                     case 'i':
16744                         cmd = 'italic';
16745                         break;
16746                     
16747                     case 'u':
16748                         cmd = 'underline';
16749                         break;
16750                     
16751                     case 'v':
16752                         this.cleanUpPaste.defer(100, this);
16753                         return;
16754                         
16755                 }
16756                 if(cmd){
16757                     this.win.focus();
16758                     this.execCmd(cmd);
16759                     this.deferFocus();
16760                     e.preventDefault();
16761                 }
16762                 
16763             }
16764         }
16765     },
16766
16767     // private
16768     fixKeys : function(){ // load time branching for fastest keydown performance
16769         if(Roo.isIE){
16770             return function(e){
16771                 var k = e.getKey(), r;
16772                 if(k == e.TAB){
16773                     e.stopEvent();
16774                     r = this.doc.selection.createRange();
16775                     if(r){
16776                         r.collapse(true);
16777                         r.pasteHTML('&#160;&#160;&#160;&#160;');
16778                         this.deferFocus();
16779                     }
16780                     return;
16781                 }
16782                 
16783                 if(k == e.ENTER){
16784                     r = this.doc.selection.createRange();
16785                     if(r){
16786                         var target = r.parentElement();
16787                         if(!target || target.tagName.toLowerCase() != 'li'){
16788                             e.stopEvent();
16789                             r.pasteHTML('<br />');
16790                             r.collapse(false);
16791                             r.select();
16792                         }
16793                     }
16794                 }
16795                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
16796                     this.cleanUpPaste.defer(100, this);
16797                     return;
16798                 }
16799                 
16800                 
16801             };
16802         }else if(Roo.isOpera){
16803             return function(e){
16804                 var k = e.getKey();
16805                 if(k == e.TAB){
16806                     e.stopEvent();
16807                     this.win.focus();
16808                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
16809                     this.deferFocus();
16810                 }
16811                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
16812                     this.cleanUpPaste.defer(100, this);
16813                     return;
16814                 }
16815                 
16816             };
16817         }else if(Roo.isSafari){
16818             return function(e){
16819                 var k = e.getKey();
16820                 
16821                 if(k == e.TAB){
16822                     e.stopEvent();
16823                     this.execCmd('InsertText','\t');
16824                     this.deferFocus();
16825                     return;
16826                 }
16827                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
16828                     this.cleanUpPaste.defer(100, this);
16829                     return;
16830                 }
16831                 
16832              };
16833         }
16834     }(),
16835     
16836     getAllAncestors: function()
16837     {
16838         var p = this.getSelectedNode();
16839         var a = [];
16840         if (!p) {
16841             a.push(p); // push blank onto stack..
16842             p = this.getParentElement();
16843         }
16844         
16845         
16846         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
16847             a.push(p);
16848             p = p.parentNode;
16849         }
16850         a.push(this.doc.body);
16851         return a;
16852     },
16853     lastSel : false,
16854     lastSelNode : false,
16855     
16856     
16857     getSelection : function() 
16858     {
16859         this.assignDocWin();
16860         return Roo.isIE ? this.doc.selection : this.win.getSelection();
16861     },
16862     
16863     getSelectedNode: function() 
16864     {
16865         // this may only work on Gecko!!!
16866         
16867         // should we cache this!!!!
16868         
16869         
16870         
16871          
16872         var range = this.createRange(this.getSelection()).cloneRange();
16873         
16874         if (Roo.isIE) {
16875             var parent = range.parentElement();
16876             while (true) {
16877                 var testRange = range.duplicate();
16878                 testRange.moveToElementText(parent);
16879                 if (testRange.inRange(range)) {
16880                     break;
16881                 }
16882                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
16883                     break;
16884                 }
16885                 parent = parent.parentElement;
16886             }
16887             return parent;
16888         }
16889         
16890         // is ancestor a text element.
16891         var ac =  range.commonAncestorContainer;
16892         if (ac.nodeType == 3) {
16893             ac = ac.parentNode;
16894         }
16895         
16896         var ar = ac.childNodes;
16897          
16898         var nodes = [];
16899         var other_nodes = [];
16900         var has_other_nodes = false;
16901         for (var i=0;i<ar.length;i++) {
16902             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
16903                 continue;
16904             }
16905             // fullly contained node.
16906             
16907             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
16908                 nodes.push(ar[i]);
16909                 continue;
16910             }
16911             
16912             // probably selected..
16913             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
16914                 other_nodes.push(ar[i]);
16915                 continue;
16916             }
16917             // outer..
16918             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
16919                 continue;
16920             }
16921             
16922             
16923             has_other_nodes = true;
16924         }
16925         if (!nodes.length && other_nodes.length) {
16926             nodes= other_nodes;
16927         }
16928         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
16929             return false;
16930         }
16931         
16932         return nodes[0];
16933     },
16934     createRange: function(sel)
16935     {
16936         // this has strange effects when using with 
16937         // top toolbar - not sure if it's a great idea.
16938         //this.editor.contentWindow.focus();
16939         if (typeof sel != "undefined") {
16940             try {
16941                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
16942             } catch(e) {
16943                 return this.doc.createRange();
16944             }
16945         } else {
16946             return this.doc.createRange();
16947         }
16948     },
16949     getParentElement: function()
16950     {
16951         
16952         this.assignDocWin();
16953         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
16954         
16955         var range = this.createRange(sel);
16956          
16957         try {
16958             var p = range.commonAncestorContainer;
16959             while (p.nodeType == 3) { // text node
16960                 p = p.parentNode;
16961             }
16962             return p;
16963         } catch (e) {
16964             return null;
16965         }
16966     
16967     },
16968     /***
16969      *
16970      * Range intersection.. the hard stuff...
16971      *  '-1' = before
16972      *  '0' = hits..
16973      *  '1' = after.
16974      *         [ -- selected range --- ]
16975      *   [fail]                        [fail]
16976      *
16977      *    basically..
16978      *      if end is before start or  hits it. fail.
16979      *      if start is after end or hits it fail.
16980      *
16981      *   if either hits (but other is outside. - then it's not 
16982      *   
16983      *    
16984      **/
16985     
16986     
16987     // @see http://www.thismuchiknow.co.uk/?p=64.
16988     rangeIntersectsNode : function(range, node)
16989     {
16990         var nodeRange = node.ownerDocument.createRange();
16991         try {
16992             nodeRange.selectNode(node);
16993         } catch (e) {
16994             nodeRange.selectNodeContents(node);
16995         }
16996     
16997         var rangeStartRange = range.cloneRange();
16998         rangeStartRange.collapse(true);
16999     
17000         var rangeEndRange = range.cloneRange();
17001         rangeEndRange.collapse(false);
17002     
17003         var nodeStartRange = nodeRange.cloneRange();
17004         nodeStartRange.collapse(true);
17005     
17006         var nodeEndRange = nodeRange.cloneRange();
17007         nodeEndRange.collapse(false);
17008     
17009         return rangeStartRange.compareBoundaryPoints(
17010                  Range.START_TO_START, nodeEndRange) == -1 &&
17011                rangeEndRange.compareBoundaryPoints(
17012                  Range.START_TO_START, nodeStartRange) == 1;
17013         
17014          
17015     },
17016     rangeCompareNode : function(range, node)
17017     {
17018         var nodeRange = node.ownerDocument.createRange();
17019         try {
17020             nodeRange.selectNode(node);
17021         } catch (e) {
17022             nodeRange.selectNodeContents(node);
17023         }
17024         
17025         
17026         range.collapse(true);
17027     
17028         nodeRange.collapse(true);
17029      
17030         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
17031         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
17032          
17033         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
17034         
17035         var nodeIsBefore   =  ss == 1;
17036         var nodeIsAfter    = ee == -1;
17037         
17038         if (nodeIsBefore && nodeIsAfter)
17039             return 0; // outer
17040         if (!nodeIsBefore && nodeIsAfter)
17041             return 1; //right trailed.
17042         
17043         if (nodeIsBefore && !nodeIsAfter)
17044             return 2;  // left trailed.
17045         // fully contined.
17046         return 3;
17047     },
17048
17049     // private? - in a new class?
17050     cleanUpPaste :  function()
17051     {
17052         // cleans up the whole document..
17053         Roo.log('cleanuppaste');
17054         
17055         this.cleanUpChildren(this.doc.body);
17056         var clean = this.cleanWordChars(this.doc.body.innerHTML);
17057         if (clean != this.doc.body.innerHTML) {
17058             this.doc.body.innerHTML = clean;
17059         }
17060         
17061     },
17062     
17063     cleanWordChars : function(input) {// change the chars to hex code
17064         var he = Roo.HtmlEditorCore;
17065         
17066         var output = input;
17067         Roo.each(he.swapCodes, function(sw) { 
17068             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
17069             
17070             output = output.replace(swapper, sw[1]);
17071         });
17072         
17073         return output;
17074     },
17075     
17076     
17077     cleanUpChildren : function (n)
17078     {
17079         if (!n.childNodes.length) {
17080             return;
17081         }
17082         for (var i = n.childNodes.length-1; i > -1 ; i--) {
17083            this.cleanUpChild(n.childNodes[i]);
17084         }
17085     },
17086     
17087     
17088         
17089     
17090     cleanUpChild : function (node)
17091     {
17092         var ed = this;
17093         //console.log(node);
17094         if (node.nodeName == "#text") {
17095             // clean up silly Windows -- stuff?
17096             return; 
17097         }
17098         if (node.nodeName == "#comment") {
17099             node.parentNode.removeChild(node);
17100             // clean up silly Windows -- stuff?
17101             return; 
17102         }
17103         
17104         if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1 && this.clearUp) {
17105             // remove node.
17106             node.parentNode.removeChild(node);
17107             return;
17108             
17109         }
17110         
17111         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
17112         
17113         // remove <a name=....> as rendering on yahoo mailer is borked with this.
17114         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
17115         
17116         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
17117         //    remove_keep_children = true;
17118         //}
17119         
17120         if (remove_keep_children) {
17121             this.cleanUpChildren(node);
17122             // inserts everything just before this node...
17123             while (node.childNodes.length) {
17124                 var cn = node.childNodes[0];
17125                 node.removeChild(cn);
17126                 node.parentNode.insertBefore(cn, node);
17127             }
17128             node.parentNode.removeChild(node);
17129             return;
17130         }
17131         
17132         if (!node.attributes || !node.attributes.length) {
17133             this.cleanUpChildren(node);
17134             return;
17135         }
17136         
17137         function cleanAttr(n,v)
17138         {
17139             
17140             if (v.match(/^\./) || v.match(/^\//)) {
17141                 return;
17142             }
17143             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
17144                 return;
17145             }
17146             if (v.match(/^#/)) {
17147                 return;
17148             }
17149 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
17150             node.removeAttribute(n);
17151             
17152         }
17153         
17154         function cleanStyle(n,v)
17155         {
17156             if (v.match(/expression/)) { //XSS?? should we even bother..
17157                 node.removeAttribute(n);
17158                 return;
17159             }
17160             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
17161             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
17162             
17163             
17164             var parts = v.split(/;/);
17165             var clean = [];
17166             
17167             Roo.each(parts, function(p) {
17168                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
17169                 if (!p.length) {
17170                     return true;
17171                 }
17172                 var l = p.split(':').shift().replace(/\s+/g,'');
17173                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
17174                 
17175                 if ( cblack.indexOf(l) > -1) {
17176 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17177                     //node.removeAttribute(n);
17178                     return true;
17179                 }
17180                 //Roo.log()
17181                 // only allow 'c whitelisted system attributes'
17182                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
17183 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17184                     //node.removeAttribute(n);
17185                     return true;
17186                 }
17187                 
17188                 
17189                  
17190                 
17191                 clean.push(p);
17192                 return true;
17193             });
17194             if (clean.length) { 
17195                 node.setAttribute(n, clean.join(';'));
17196             } else {
17197                 node.removeAttribute(n);
17198             }
17199             
17200         }
17201         
17202         
17203         for (var i = node.attributes.length-1; i > -1 ; i--) {
17204             var a = node.attributes[i];
17205             //console.log(a);
17206             
17207             if (a.name.toLowerCase().substr(0,2)=='on')  {
17208                 node.removeAttribute(a.name);
17209                 continue;
17210             }
17211             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
17212                 node.removeAttribute(a.name);
17213                 continue;
17214             }
17215             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
17216                 cleanAttr(a.name,a.value); // fixme..
17217                 continue;
17218             }
17219             if (a.name == 'style') {
17220                 cleanStyle(a.name,a.value);
17221                 continue;
17222             }
17223             /// clean up MS crap..
17224             // tecnically this should be a list of valid class'es..
17225             
17226             
17227             if (a.name == 'class') {
17228                 if (a.value.match(/^Mso/)) {
17229                     node.className = '';
17230                 }
17231                 
17232                 if (a.value.match(/body/)) {
17233                     node.className = '';
17234                 }
17235                 continue;
17236             }
17237             
17238             // style cleanup!?
17239             // class cleanup?
17240             
17241         }
17242         
17243         
17244         this.cleanUpChildren(node);
17245         
17246         
17247     },
17248     /**
17249      * Clean up MS wordisms...
17250      */
17251     cleanWord : function(node)
17252     {
17253         var _t = this;
17254         var cleanWordChildren = function()
17255         {
17256             if (!node.childNodes.length) {
17257                 return;
17258             }
17259             for (var i = node.childNodes.length-1; i > -1 ; i--) {
17260                _t.cleanWord(node.childNodes[i]);
17261             }
17262         }
17263         
17264         
17265         if (!node) {
17266             this.cleanWord(this.doc.body);
17267             return;
17268         }
17269         if (node.nodeName == "#text") {
17270             // clean up silly Windows -- stuff?
17271             return; 
17272         }
17273         if (node.nodeName == "#comment") {
17274             node.parentNode.removeChild(node);
17275             // clean up silly Windows -- stuff?
17276             return; 
17277         }
17278         
17279         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
17280             node.parentNode.removeChild(node);
17281             return;
17282         }
17283         
17284         // remove - but keep children..
17285         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
17286             while (node.childNodes.length) {
17287                 var cn = node.childNodes[0];
17288                 node.removeChild(cn);
17289                 node.parentNode.insertBefore(cn, node);
17290             }
17291             node.parentNode.removeChild(node);
17292             cleanWordChildren();
17293             return;
17294         }
17295         // clean styles
17296         if (node.className.length) {
17297             
17298             var cn = node.className.split(/\W+/);
17299             var cna = [];
17300             Roo.each(cn, function(cls) {
17301                 if (cls.match(/Mso[a-zA-Z]+/)) {
17302                     return;
17303                 }
17304                 cna.push(cls);
17305             });
17306             node.className = cna.length ? cna.join(' ') : '';
17307             if (!cna.length) {
17308                 node.removeAttribute("class");
17309             }
17310         }
17311         
17312         if (node.hasAttribute("lang")) {
17313             node.removeAttribute("lang");
17314         }
17315         
17316         if (node.hasAttribute("style")) {
17317             
17318             var styles = node.getAttribute("style").split(";");
17319             var nstyle = [];
17320             Roo.each(styles, function(s) {
17321                 if (!s.match(/:/)) {
17322                     return;
17323                 }
17324                 var kv = s.split(":");
17325                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
17326                     return;
17327                 }
17328                 // what ever is left... we allow.
17329                 nstyle.push(s);
17330             });
17331             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
17332             if (!nstyle.length) {
17333                 node.removeAttribute('style');
17334             }
17335         }
17336         
17337         cleanWordChildren();
17338         
17339         
17340     },
17341     domToHTML : function(currentElement, depth, nopadtext) {
17342         
17343             depth = depth || 0;
17344             nopadtext = nopadtext || false;
17345         
17346             if (!currentElement) {
17347                 return this.domToHTML(this.doc.body);
17348             }
17349             
17350             //Roo.log(currentElement);
17351             var j;
17352             var allText = false;
17353             var nodeName = currentElement.nodeName;
17354             var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
17355             
17356             if  (nodeName == '#text') {
17357                 return currentElement.nodeValue;
17358             }
17359             
17360             
17361             var ret = '';
17362             if (nodeName != 'BODY') {
17363                  
17364                 var i = 0;
17365                 // Prints the node tagName, such as <A>, <IMG>, etc
17366                 if (tagName) {
17367                     var attr = [];
17368                     for(i = 0; i < currentElement.attributes.length;i++) {
17369                         // quoting?
17370                         var aname = currentElement.attributes.item(i).name;
17371                         if (!currentElement.attributes.item(i).value.length) {
17372                             continue;
17373                         }
17374                         attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
17375                     }
17376                     
17377                     ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
17378                 } 
17379                 else {
17380                     
17381                     // eack
17382                 }
17383             } else {
17384                 tagName = false;
17385             }
17386             if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
17387                 return ret;
17388             }
17389             if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
17390                 nopadtext = true;
17391             }
17392             
17393             
17394             // Traverse the tree
17395             i = 0;
17396             var currentElementChild = currentElement.childNodes.item(i);
17397             var allText = true;
17398             var innerHTML  = '';
17399             lastnode = '';
17400             while (currentElementChild) {
17401                 // Formatting code (indent the tree so it looks nice on the screen)
17402                 var nopad = nopadtext;
17403                 if (lastnode == 'SPAN') {
17404                     nopad  = true;
17405                 }
17406                 // text
17407                 if  (currentElementChild.nodeName == '#text') {
17408                     var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
17409                     if (!nopad && toadd.length > 80) {
17410                         innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
17411                     }
17412                     innerHTML  += toadd;
17413                     
17414                     i++;
17415                     currentElementChild = currentElement.childNodes.item(i);
17416                     lastNode = '';
17417                     continue;
17418                 }
17419                 allText = false;
17420                 
17421                 innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
17422                     
17423                 // Recursively traverse the tree structure of the child node
17424                 innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
17425                 lastnode = currentElementChild.nodeName;
17426                 i++;
17427                 currentElementChild=currentElement.childNodes.item(i);
17428             }
17429             
17430             ret += innerHTML;
17431             
17432             if (!allText) {
17433                     // The remaining code is mostly for formatting the tree
17434                 ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
17435             }
17436             
17437             
17438             if (tagName) {
17439                 ret+= "</"+tagName+">";
17440             }
17441             return ret;
17442             
17443         }
17444     
17445     // hide stuff that is not compatible
17446     /**
17447      * @event blur
17448      * @hide
17449      */
17450     /**
17451      * @event change
17452      * @hide
17453      */
17454     /**
17455      * @event focus
17456      * @hide
17457      */
17458     /**
17459      * @event specialkey
17460      * @hide
17461      */
17462     /**
17463      * @cfg {String} fieldClass @hide
17464      */
17465     /**
17466      * @cfg {String} focusClass @hide
17467      */
17468     /**
17469      * @cfg {String} autoCreate @hide
17470      */
17471     /**
17472      * @cfg {String} inputType @hide
17473      */
17474     /**
17475      * @cfg {String} invalidClass @hide
17476      */
17477     /**
17478      * @cfg {String} invalidText @hide
17479      */
17480     /**
17481      * @cfg {String} msgFx @hide
17482      */
17483     /**
17484      * @cfg {String} validateOnBlur @hide
17485      */
17486 });
17487
17488 Roo.HtmlEditorCore.white = [
17489         'area', 'br', 'img', 'input', 'hr', 'wbr',
17490         
17491        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
17492        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
17493        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
17494        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
17495        'table',   'ul',         'xmp', 
17496        
17497        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
17498       'thead',   'tr', 
17499      
17500       'dir', 'menu', 'ol', 'ul', 'dl',
17501        
17502       'embed',  'object'
17503 ];
17504
17505
17506 Roo.HtmlEditorCore.black = [
17507     //    'embed',  'object', // enable - backend responsiblity to clean thiese
17508         'applet', // 
17509         'base',   'basefont', 'bgsound', 'blink',  'body', 
17510         'frame',  'frameset', 'head',    'html',   'ilayer', 
17511         'iframe', 'layer',  'link',     'meta',    'object',   
17512         'script', 'style' ,'title',  'xml' // clean later..
17513 ];
17514 Roo.HtmlEditorCore.clean = [
17515     'script', 'style', 'title', 'xml'
17516 ];
17517 Roo.HtmlEditorCore.remove = [
17518     'font'
17519 ];
17520 // attributes..
17521
17522 Roo.HtmlEditorCore.ablack = [
17523     'on'
17524 ];
17525     
17526 Roo.HtmlEditorCore.aclean = [ 
17527     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
17528 ];
17529
17530 // protocols..
17531 Roo.HtmlEditorCore.pwhite= [
17532         'http',  'https',  'mailto'
17533 ];
17534
17535 // white listed style attributes.
17536 Roo.HtmlEditorCore.cwhite= [
17537       //  'text-align', /// default is to allow most things..
17538       
17539          
17540 //        'font-size'//??
17541 ];
17542
17543 // black listed style attributes.
17544 Roo.HtmlEditorCore.cblack= [
17545       //  'font-size' -- this can be set by the project 
17546 ];
17547
17548
17549 Roo.HtmlEditorCore.swapCodes   =[ 
17550     [    8211, "--" ], 
17551     [    8212, "--" ], 
17552     [    8216,  "'" ],  
17553     [    8217, "'" ],  
17554     [    8220, '"' ],  
17555     [    8221, '"' ],  
17556     [    8226, "*" ],  
17557     [    8230, "..." ]
17558 ]; 
17559
17560     /*
17561  * - LGPL
17562  *
17563  * HtmlEditor
17564  * 
17565  */
17566
17567 /**
17568  * @class Roo.bootstrap.HtmlEditor
17569  * @extends Roo.bootstrap.TextArea
17570  * Bootstrap HtmlEditor class
17571
17572  * @constructor
17573  * Create a new HtmlEditor
17574  * @param {Object} config The config object
17575  */
17576
17577 Roo.bootstrap.HtmlEditor = function(config){
17578     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
17579     if (!this.toolbars) {
17580         this.toolbars = [];
17581     }
17582     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
17583     this.addEvents({
17584             /**
17585              * @event initialize
17586              * Fires when the editor is fully initialized (including the iframe)
17587              * @param {HtmlEditor} this
17588              */
17589             initialize: true,
17590             /**
17591              * @event activate
17592              * Fires when the editor is first receives the focus. Any insertion must wait
17593              * until after this event.
17594              * @param {HtmlEditor} this
17595              */
17596             activate: true,
17597              /**
17598              * @event beforesync
17599              * Fires before the textarea is updated with content from the editor iframe. Return false
17600              * to cancel the sync.
17601              * @param {HtmlEditor} this
17602              * @param {String} html
17603              */
17604             beforesync: true,
17605              /**
17606              * @event beforepush
17607              * Fires before the iframe editor is updated with content from the textarea. Return false
17608              * to cancel the push.
17609              * @param {HtmlEditor} this
17610              * @param {String} html
17611              */
17612             beforepush: true,
17613              /**
17614              * @event sync
17615              * Fires when the textarea is updated with content from the editor iframe.
17616              * @param {HtmlEditor} this
17617              * @param {String} html
17618              */
17619             sync: true,
17620              /**
17621              * @event push
17622              * Fires when the iframe editor is updated with content from the textarea.
17623              * @param {HtmlEditor} this
17624              * @param {String} html
17625              */
17626             push: true,
17627              /**
17628              * @event editmodechange
17629              * Fires when the editor switches edit modes
17630              * @param {HtmlEditor} this
17631              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
17632              */
17633             editmodechange: true,
17634             /**
17635              * @event editorevent
17636              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
17637              * @param {HtmlEditor} this
17638              */
17639             editorevent: true,
17640             /**
17641              * @event firstfocus
17642              * Fires when on first focus - needed by toolbars..
17643              * @param {HtmlEditor} this
17644              */
17645             firstfocus: true,
17646             /**
17647              * @event autosave
17648              * Auto save the htmlEditor value as a file into Events
17649              * @param {HtmlEditor} this
17650              */
17651             autosave: true,
17652             /**
17653              * @event savedpreview
17654              * preview the saved version of htmlEditor
17655              * @param {HtmlEditor} this
17656              */
17657             savedpreview: true
17658         });
17659 };
17660
17661
17662 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
17663     
17664     
17665       /**
17666      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
17667      */
17668     toolbars : false,
17669    
17670      /**
17671      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
17672      *                        Roo.resizable.
17673      */
17674     resizable : false,
17675      /**
17676      * @cfg {Number} height (in pixels)
17677      */   
17678     height: 300,
17679    /**
17680      * @cfg {Number} width (in pixels)
17681      */   
17682     width: false,
17683     
17684     /**
17685      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
17686      * 
17687      */
17688     stylesheets: false,
17689     
17690     // id of frame..
17691     frameId: false,
17692     
17693     // private properties
17694     validationEvent : false,
17695     deferHeight: true,
17696     initialized : false,
17697     activated : false,
17698     
17699     onFocus : Roo.emptyFn,
17700     iframePad:3,
17701     hideMode:'offsets',
17702     
17703     
17704     tbContainer : false,
17705     
17706     toolbarContainer :function() {
17707         return this.wrap.select('.x-html-editor-tb',true).first();
17708     },
17709
17710     /**
17711      * Protected method that will not generally be called directly. It
17712      * is called when the editor creates its toolbar. Override this method if you need to
17713      * add custom toolbar buttons.
17714      * @param {HtmlEditor} editor
17715      */
17716     createToolbar : function(){
17717         
17718         Roo.log("create toolbars");
17719         
17720         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
17721         this.toolbars[0].render(this.toolbarContainer());
17722         
17723         return;
17724         
17725 //        if (!editor.toolbars || !editor.toolbars.length) {
17726 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
17727 //        }
17728 //        
17729 //        for (var i =0 ; i < editor.toolbars.length;i++) {
17730 //            editor.toolbars[i] = Roo.factory(
17731 //                    typeof(editor.toolbars[i]) == 'string' ?
17732 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
17733 //                Roo.bootstrap.HtmlEditor);
17734 //            editor.toolbars[i].init(editor);
17735 //        }
17736     },
17737
17738      
17739     // private
17740     onRender : function(ct, position)
17741     {
17742        // Roo.log("Call onRender: " + this.xtype);
17743         var _t = this;
17744         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
17745       
17746         this.wrap = this.inputEl().wrap({
17747             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
17748         });
17749         
17750         this.editorcore.onRender(ct, position);
17751          
17752         if (this.resizable) {
17753             this.resizeEl = new Roo.Resizable(this.wrap, {
17754                 pinned : true,
17755                 wrap: true,
17756                 dynamic : true,
17757                 minHeight : this.height,
17758                 height: this.height,
17759                 handles : this.resizable,
17760                 width: this.width,
17761                 listeners : {
17762                     resize : function(r, w, h) {
17763                         _t.onResize(w,h); // -something
17764                     }
17765                 }
17766             });
17767             
17768         }
17769         this.createToolbar(this);
17770        
17771         
17772         if(!this.width && this.resizable){
17773             this.setSize(this.wrap.getSize());
17774         }
17775         if (this.resizeEl) {
17776             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
17777             // should trigger onReize..
17778         }
17779         
17780     },
17781
17782     // private
17783     onResize : function(w, h)
17784     {
17785         Roo.log('resize: ' +w + ',' + h );
17786         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
17787         var ew = false;
17788         var eh = false;
17789         
17790         if(this.inputEl() ){
17791             if(typeof w == 'number'){
17792                 var aw = w - this.wrap.getFrameWidth('lr');
17793                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
17794                 ew = aw;
17795             }
17796             if(typeof h == 'number'){
17797                  var tbh = -11;  // fixme it needs to tool bar size!
17798                 for (var i =0; i < this.toolbars.length;i++) {
17799                     // fixme - ask toolbars for heights?
17800                     tbh += this.toolbars[i].el.getHeight();
17801                     //if (this.toolbars[i].footer) {
17802                     //    tbh += this.toolbars[i].footer.el.getHeight();
17803                     //}
17804                 }
17805               
17806                 
17807                 
17808                 
17809                 
17810                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
17811                 ah -= 5; // knock a few pixes off for look..
17812                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
17813                 var eh = ah;
17814             }
17815         }
17816         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
17817         this.editorcore.onResize(ew,eh);
17818         
17819     },
17820
17821     /**
17822      * Toggles the editor between standard and source edit mode.
17823      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
17824      */
17825     toggleSourceEdit : function(sourceEditMode)
17826     {
17827         this.editorcore.toggleSourceEdit(sourceEditMode);
17828         
17829         if(this.editorcore.sourceEditMode){
17830             Roo.log('editor - showing textarea');
17831             
17832 //            Roo.log('in');
17833 //            Roo.log(this.syncValue());
17834             this.syncValue();
17835             this.inputEl().removeClass(['hide', 'x-hidden']);
17836             this.inputEl().dom.removeAttribute('tabIndex');
17837             this.inputEl().focus();
17838         }else{
17839             Roo.log('editor - hiding textarea');
17840 //            Roo.log('out')
17841 //            Roo.log(this.pushValue()); 
17842             this.pushValue();
17843             
17844             this.inputEl().addClass(['hide', 'x-hidden']);
17845             this.inputEl().dom.setAttribute('tabIndex', -1);
17846             //this.deferFocus();
17847         }
17848          
17849         if(this.resizable){
17850             this.setSize(this.wrap.getSize());
17851         }
17852         
17853         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
17854     },
17855  
17856     // private (for BoxComponent)
17857     adjustSize : Roo.BoxComponent.prototype.adjustSize,
17858
17859     // private (for BoxComponent)
17860     getResizeEl : function(){
17861         return this.wrap;
17862     },
17863
17864     // private (for BoxComponent)
17865     getPositionEl : function(){
17866         return this.wrap;
17867     },
17868
17869     // private
17870     initEvents : function(){
17871         this.originalValue = this.getValue();
17872     },
17873
17874 //    /**
17875 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
17876 //     * @method
17877 //     */
17878 //    markInvalid : Roo.emptyFn,
17879 //    /**
17880 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
17881 //     * @method
17882 //     */
17883 //    clearInvalid : Roo.emptyFn,
17884
17885     setValue : function(v){
17886         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
17887         this.editorcore.pushValue();
17888     },
17889
17890      
17891     // private
17892     deferFocus : function(){
17893         this.focus.defer(10, this);
17894     },
17895
17896     // doc'ed in Field
17897     focus : function(){
17898         this.editorcore.focus();
17899         
17900     },
17901       
17902
17903     // private
17904     onDestroy : function(){
17905         
17906         
17907         
17908         if(this.rendered){
17909             
17910             for (var i =0; i < this.toolbars.length;i++) {
17911                 // fixme - ask toolbars for heights?
17912                 this.toolbars[i].onDestroy();
17913             }
17914             
17915             this.wrap.dom.innerHTML = '';
17916             this.wrap.remove();
17917         }
17918     },
17919
17920     // private
17921     onFirstFocus : function(){
17922         //Roo.log("onFirstFocus");
17923         this.editorcore.onFirstFocus();
17924          for (var i =0; i < this.toolbars.length;i++) {
17925             this.toolbars[i].onFirstFocus();
17926         }
17927         
17928     },
17929     
17930     // private
17931     syncValue : function()
17932     {   
17933         this.editorcore.syncValue();
17934     },
17935     
17936     pushValue : function()
17937     {   
17938         this.editorcore.pushValue();
17939     }
17940      
17941     
17942     // hide stuff that is not compatible
17943     /**
17944      * @event blur
17945      * @hide
17946      */
17947     /**
17948      * @event change
17949      * @hide
17950      */
17951     /**
17952      * @event focus
17953      * @hide
17954      */
17955     /**
17956      * @event specialkey
17957      * @hide
17958      */
17959     /**
17960      * @cfg {String} fieldClass @hide
17961      */
17962     /**
17963      * @cfg {String} focusClass @hide
17964      */
17965     /**
17966      * @cfg {String} autoCreate @hide
17967      */
17968     /**
17969      * @cfg {String} inputType @hide
17970      */
17971     /**
17972      * @cfg {String} invalidClass @hide
17973      */
17974     /**
17975      * @cfg {String} invalidText @hide
17976      */
17977     /**
17978      * @cfg {String} msgFx @hide
17979      */
17980     /**
17981      * @cfg {String} validateOnBlur @hide
17982      */
17983 });
17984  
17985     
17986    
17987    
17988    
17989       
17990 Roo.namespace('Roo.bootstrap.htmleditor');
17991 /**
17992  * @class Roo.bootstrap.HtmlEditorToolbar1
17993  * Basic Toolbar
17994  * 
17995  * Usage:
17996  *
17997  new Roo.bootstrap.HtmlEditor({
17998     ....
17999     toolbars : [
18000         new Roo.bootstrap.HtmlEditorToolbar1({
18001             disable : { fonts: 1 , format: 1, ..., ... , ...],
18002             btns : [ .... ]
18003         })
18004     }
18005      
18006  * 
18007  * @cfg {Object} disable List of elements to disable..
18008  * @cfg {Array} btns List of additional buttons.
18009  * 
18010  * 
18011  * NEEDS Extra CSS? 
18012  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
18013  */
18014  
18015 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
18016 {
18017     
18018     Roo.apply(this, config);
18019     
18020     // default disabled, based on 'good practice'..
18021     this.disable = this.disable || {};
18022     Roo.applyIf(this.disable, {
18023         fontSize : true,
18024         colors : true,
18025         specialElements : true
18026     });
18027     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
18028     
18029     this.editor = config.editor;
18030     this.editorcore = config.editor.editorcore;
18031     
18032     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
18033     
18034     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
18035     // dont call parent... till later.
18036 }
18037 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
18038      
18039     bar : true,
18040     
18041     editor : false,
18042     editorcore : false,
18043     
18044     
18045     formats : [
18046         "p" ,  
18047         "h1","h2","h3","h4","h5","h6", 
18048         "pre", "code", 
18049         "abbr", "acronym", "address", "cite", "samp", "var",
18050         'div','span'
18051     ],
18052     
18053     onRender : function(ct, position)
18054     {
18055        // Roo.log("Call onRender: " + this.xtype);
18056         
18057        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
18058        Roo.log(this.el);
18059        this.el.dom.style.marginBottom = '0';
18060        var _this = this;
18061        var editorcore = this.editorcore;
18062        var editor= this.editor;
18063        
18064        var children = [];
18065        var btn = function(id,cmd , toggle, handler){
18066        
18067             var  event = toggle ? 'toggle' : 'click';
18068        
18069             var a = {
18070                 size : 'sm',
18071                 xtype: 'Button',
18072                 xns: Roo.bootstrap,
18073                 glyphicon : id,
18074                 cmd : id || cmd,
18075                 enableToggle:toggle !== false,
18076                 //html : 'submit'
18077                 pressed : toggle ? false : null,
18078                 listeners : {}
18079             }
18080             a.listeners[toggle ? 'toggle' : 'click'] = function() {
18081                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
18082             }
18083             children.push(a);
18084             return a;
18085        }
18086         
18087         var style = {
18088                 xtype: 'Button',
18089                 size : 'sm',
18090                 xns: Roo.bootstrap,
18091                 glyphicon : 'font',
18092                 //html : 'submit'
18093                 menu : {
18094                     xtype: 'Menu',
18095                     xns: Roo.bootstrap,
18096                     items:  []
18097                 }
18098         };
18099         Roo.each(this.formats, function(f) {
18100             style.menu.items.push({
18101                 xtype :'MenuItem',
18102                 xns: Roo.bootstrap,
18103                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
18104                 tagname : f,
18105                 listeners : {
18106                     click : function()
18107                     {
18108                         editorcore.insertTag(this.tagname);
18109                         editor.focus();
18110                     }
18111                 }
18112                 
18113             });
18114         });
18115          children.push(style);   
18116             
18117             
18118         btn('bold',false,true);
18119         btn('italic',false,true);
18120         btn('align-left', 'justifyleft',true);
18121         btn('align-center', 'justifycenter',true);
18122         btn('align-right' , 'justifyright',true);
18123         btn('link', false, false, function(btn) {
18124             //Roo.log("create link?");
18125             var url = prompt(this.createLinkText, this.defaultLinkValue);
18126             if(url && url != 'http:/'+'/'){
18127                 this.editorcore.relayCmd('createlink', url);
18128             }
18129         }),
18130         btn('list','insertunorderedlist',true);
18131         btn('pencil', false,true, function(btn){
18132                 Roo.log(this);
18133                 
18134                 this.toggleSourceEdit(btn.pressed);
18135         });
18136         /*
18137         var cog = {
18138                 xtype: 'Button',
18139                 size : 'sm',
18140                 xns: Roo.bootstrap,
18141                 glyphicon : 'cog',
18142                 //html : 'submit'
18143                 menu : {
18144                     xtype: 'Menu',
18145                     xns: Roo.bootstrap,
18146                     items:  []
18147                 }
18148         };
18149         
18150         cog.menu.items.push({
18151             xtype :'MenuItem',
18152             xns: Roo.bootstrap,
18153             html : Clean styles,
18154             tagname : f,
18155             listeners : {
18156                 click : function()
18157                 {
18158                     editorcore.insertTag(this.tagname);
18159                     editor.focus();
18160                 }
18161             }
18162             
18163         });
18164        */
18165         
18166          
18167        this.xtype = 'NavSimplebar';
18168         
18169         for(var i=0;i< children.length;i++) {
18170             
18171             this.buttons.add(this.addxtypeChild(children[i]));
18172             
18173         }
18174         
18175         editor.on('editorevent', this.updateToolbar, this);
18176     },
18177     onBtnClick : function(id)
18178     {
18179        this.editorcore.relayCmd(id);
18180        this.editorcore.focus();
18181     },
18182     
18183     /**
18184      * Protected method that will not generally be called directly. It triggers
18185      * a toolbar update by reading the markup state of the current selection in the editor.
18186      */
18187     updateToolbar: function(){
18188
18189         if(!this.editorcore.activated){
18190             this.editor.onFirstFocus(); // is this neeed?
18191             return;
18192         }
18193
18194         var btns = this.buttons; 
18195         var doc = this.editorcore.doc;
18196         btns.get('bold').setActive(doc.queryCommandState('bold'));
18197         btns.get('italic').setActive(doc.queryCommandState('italic'));
18198         //btns.get('underline').setActive(doc.queryCommandState('underline'));
18199         
18200         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
18201         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
18202         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
18203         
18204         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
18205         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
18206          /*
18207         
18208         var ans = this.editorcore.getAllAncestors();
18209         if (this.formatCombo) {
18210             
18211             
18212             var store = this.formatCombo.store;
18213             this.formatCombo.setValue("");
18214             for (var i =0; i < ans.length;i++) {
18215                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
18216                     // select it..
18217                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
18218                     break;
18219                 }
18220             }
18221         }
18222         
18223         
18224         
18225         // hides menus... - so this cant be on a menu...
18226         Roo.bootstrap.MenuMgr.hideAll();
18227         */
18228         Roo.bootstrap.MenuMgr.hideAll();
18229         //this.editorsyncValue();
18230     },
18231     onFirstFocus: function() {
18232         this.buttons.each(function(item){
18233            item.enable();
18234         });
18235     },
18236     toggleSourceEdit : function(sourceEditMode){
18237         
18238           
18239         if(sourceEditMode){
18240             Roo.log("disabling buttons");
18241            this.buttons.each( function(item){
18242                 if(item.cmd != 'pencil'){
18243                     item.disable();
18244                 }
18245             });
18246           
18247         }else{
18248             Roo.log("enabling buttons");
18249             if(this.editorcore.initialized){
18250                 this.buttons.each( function(item){
18251                     item.enable();
18252                 });
18253             }
18254             
18255         }
18256         Roo.log("calling toggole on editor");
18257         // tell the editor that it's been pressed..
18258         this.editor.toggleSourceEdit(sourceEditMode);
18259        
18260     }
18261 });
18262
18263
18264
18265
18266
18267 /**
18268  * @class Roo.bootstrap.Table.AbstractSelectionModel
18269  * @extends Roo.util.Observable
18270  * Abstract base class for grid SelectionModels.  It provides the interface that should be
18271  * implemented by descendant classes.  This class should not be directly instantiated.
18272  * @constructor
18273  */
18274 Roo.bootstrap.Table.AbstractSelectionModel = function(){
18275     this.locked = false;
18276     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
18277 };
18278
18279
18280 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
18281     /** @ignore Called by the grid automatically. Do not call directly. */
18282     init : function(grid){
18283         this.grid = grid;
18284         this.initEvents();
18285     },
18286
18287     /**
18288      * Locks the selections.
18289      */
18290     lock : function(){
18291         this.locked = true;
18292     },
18293
18294     /**
18295      * Unlocks the selections.
18296      */
18297     unlock : function(){
18298         this.locked = false;
18299     },
18300
18301     /**
18302      * Returns true if the selections are locked.
18303      * @return {Boolean}
18304      */
18305     isLocked : function(){
18306         return this.locked;
18307     }
18308 });
18309 /**
18310  * @extends Roo.bootstrap.Table.AbstractSelectionModel
18311  * @class Roo.bootstrap.Table.RowSelectionModel
18312  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
18313  * It supports multiple selections and keyboard selection/navigation. 
18314  * @constructor
18315  * @param {Object} config
18316  */
18317
18318 Roo.bootstrap.Table.RowSelectionModel = function(config){
18319     Roo.apply(this, config);
18320     this.selections = new Roo.util.MixedCollection(false, function(o){
18321         return o.id;
18322     });
18323
18324     this.last = false;
18325     this.lastActive = false;
18326
18327     this.addEvents({
18328         /**
18329              * @event selectionchange
18330              * Fires when the selection changes
18331              * @param {SelectionModel} this
18332              */
18333             "selectionchange" : true,
18334         /**
18335              * @event afterselectionchange
18336              * Fires after the selection changes (eg. by key press or clicking)
18337              * @param {SelectionModel} this
18338              */
18339             "afterselectionchange" : true,
18340         /**
18341              * @event beforerowselect
18342              * Fires when a row is selected being selected, return false to cancel.
18343              * @param {SelectionModel} this
18344              * @param {Number} rowIndex The selected index
18345              * @param {Boolean} keepExisting False if other selections will be cleared
18346              */
18347             "beforerowselect" : true,
18348         /**
18349              * @event rowselect
18350              * Fires when a row is selected.
18351              * @param {SelectionModel} this
18352              * @param {Number} rowIndex The selected index
18353              * @param {Roo.data.Record} r The record
18354              */
18355             "rowselect" : true,
18356         /**
18357              * @event rowdeselect
18358              * Fires when a row is deselected.
18359              * @param {SelectionModel} this
18360              * @param {Number} rowIndex The selected index
18361              */
18362         "rowdeselect" : true
18363     });
18364     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
18365     this.locked = false;
18366 };
18367
18368 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
18369     /**
18370      * @cfg {Boolean} singleSelect
18371      * True to allow selection of only one row at a time (defaults to false)
18372      */
18373     singleSelect : false,
18374
18375     // private
18376     initEvents : function(){
18377
18378         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
18379             this.grid.on("mousedown", this.handleMouseDown, this);
18380         }else{ // allow click to work like normal
18381             this.grid.on("rowclick", this.handleDragableRowClick, this);
18382         }
18383
18384         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
18385             "up" : function(e){
18386                 if(!e.shiftKey){
18387                     this.selectPrevious(e.shiftKey);
18388                 }else if(this.last !== false && this.lastActive !== false){
18389                     var last = this.last;
18390                     this.selectRange(this.last,  this.lastActive-1);
18391                     this.grid.getView().focusRow(this.lastActive);
18392                     if(last !== false){
18393                         this.last = last;
18394                     }
18395                 }else{
18396                     this.selectFirstRow();
18397                 }
18398                 this.fireEvent("afterselectionchange", this);
18399             },
18400             "down" : function(e){
18401                 if(!e.shiftKey){
18402                     this.selectNext(e.shiftKey);
18403                 }else if(this.last !== false && this.lastActive !== false){
18404                     var last = this.last;
18405                     this.selectRange(this.last,  this.lastActive+1);
18406                     this.grid.getView().focusRow(this.lastActive);
18407                     if(last !== false){
18408                         this.last = last;
18409                     }
18410                 }else{
18411                     this.selectFirstRow();
18412                 }
18413                 this.fireEvent("afterselectionchange", this);
18414             },
18415             scope: this
18416         });
18417
18418         var view = this.grid.view;
18419         view.on("refresh", this.onRefresh, this);
18420         view.on("rowupdated", this.onRowUpdated, this);
18421         view.on("rowremoved", this.onRemove, this);
18422     },
18423
18424     // private
18425     onRefresh : function(){
18426         var ds = this.grid.dataSource, i, v = this.grid.view;
18427         var s = this.selections;
18428         s.each(function(r){
18429             if((i = ds.indexOfId(r.id)) != -1){
18430                 v.onRowSelect(i);
18431             }else{
18432                 s.remove(r);
18433             }
18434         });
18435     },
18436
18437     // private
18438     onRemove : function(v, index, r){
18439         this.selections.remove(r);
18440     },
18441
18442     // private
18443     onRowUpdated : function(v, index, r){
18444         if(this.isSelected(r)){
18445             v.onRowSelect(index);
18446         }
18447     },
18448
18449     /**
18450      * Select records.
18451      * @param {Array} records The records to select
18452      * @param {Boolean} keepExisting (optional) True to keep existing selections
18453      */
18454     selectRecords : function(records, keepExisting){
18455         if(!keepExisting){
18456             this.clearSelections();
18457         }
18458         var ds = this.grid.dataSource;
18459         for(var i = 0, len = records.length; i < len; i++){
18460             this.selectRow(ds.indexOf(records[i]), true);
18461         }
18462     },
18463
18464     /**
18465      * Gets the number of selected rows.
18466      * @return {Number}
18467      */
18468     getCount : function(){
18469         return this.selections.length;
18470     },
18471
18472     /**
18473      * Selects the first row in the grid.
18474      */
18475     selectFirstRow : function(){
18476         this.selectRow(0);
18477     },
18478
18479     /**
18480      * Select the last row.
18481      * @param {Boolean} keepExisting (optional) True to keep existing selections
18482      */
18483     selectLastRow : function(keepExisting){
18484         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
18485     },
18486
18487     /**
18488      * Selects the row immediately following the last selected row.
18489      * @param {Boolean} keepExisting (optional) True to keep existing selections
18490      */
18491     selectNext : function(keepExisting){
18492         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
18493             this.selectRow(this.last+1, keepExisting);
18494             this.grid.getView().focusRow(this.last);
18495         }
18496     },
18497
18498     /**
18499      * Selects the row that precedes the last selected row.
18500      * @param {Boolean} keepExisting (optional) True to keep existing selections
18501      */
18502     selectPrevious : function(keepExisting){
18503         if(this.last){
18504             this.selectRow(this.last-1, keepExisting);
18505             this.grid.getView().focusRow(this.last);
18506         }
18507     },
18508
18509     /**
18510      * Returns the selected records
18511      * @return {Array} Array of selected records
18512      */
18513     getSelections : function(){
18514         return [].concat(this.selections.items);
18515     },
18516
18517     /**
18518      * Returns the first selected record.
18519      * @return {Record}
18520      */
18521     getSelected : function(){
18522         return this.selections.itemAt(0);
18523     },
18524
18525
18526     /**
18527      * Clears all selections.
18528      */
18529     clearSelections : function(fast){
18530         if(this.locked) return;
18531         if(fast !== true){
18532             var ds = this.grid.dataSource;
18533             var s = this.selections;
18534             s.each(function(r){
18535                 this.deselectRow(ds.indexOfId(r.id));
18536             }, this);
18537             s.clear();
18538         }else{
18539             this.selections.clear();
18540         }
18541         this.last = false;
18542     },
18543
18544
18545     /**
18546      * Selects all rows.
18547      */
18548     selectAll : function(){
18549         if(this.locked) return;
18550         this.selections.clear();
18551         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
18552             this.selectRow(i, true);
18553         }
18554     },
18555
18556     /**
18557      * Returns True if there is a selection.
18558      * @return {Boolean}
18559      */
18560     hasSelection : function(){
18561         return this.selections.length > 0;
18562     },
18563
18564     /**
18565      * Returns True if the specified row is selected.
18566      * @param {Number/Record} record The record or index of the record to check
18567      * @return {Boolean}
18568      */
18569     isSelected : function(index){
18570         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
18571         return (r && this.selections.key(r.id) ? true : false);
18572     },
18573
18574     /**
18575      * Returns True if the specified record id is selected.
18576      * @param {String} id The id of record to check
18577      * @return {Boolean}
18578      */
18579     isIdSelected : function(id){
18580         return (this.selections.key(id) ? true : false);
18581     },
18582
18583     // private
18584     handleMouseDown : function(e, t){
18585         var view = this.grid.getView(), rowIndex;
18586         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
18587             return;
18588         };
18589         if(e.shiftKey && this.last !== false){
18590             var last = this.last;
18591             this.selectRange(last, rowIndex, e.ctrlKey);
18592             this.last = last; // reset the last
18593             view.focusRow(rowIndex);
18594         }else{
18595             var isSelected = this.isSelected(rowIndex);
18596             if(e.button !== 0 && isSelected){
18597                 view.focusRow(rowIndex);
18598             }else if(e.ctrlKey && isSelected){
18599                 this.deselectRow(rowIndex);
18600             }else if(!isSelected){
18601                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
18602                 view.focusRow(rowIndex);
18603             }
18604         }
18605         this.fireEvent("afterselectionchange", this);
18606     },
18607     // private
18608     handleDragableRowClick :  function(grid, rowIndex, e) 
18609     {
18610         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
18611             this.selectRow(rowIndex, false);
18612             grid.view.focusRow(rowIndex);
18613              this.fireEvent("afterselectionchange", this);
18614         }
18615     },
18616     
18617     /**
18618      * Selects multiple rows.
18619      * @param {Array} rows Array of the indexes of the row to select
18620      * @param {Boolean} keepExisting (optional) True to keep existing selections
18621      */
18622     selectRows : function(rows, keepExisting){
18623         if(!keepExisting){
18624             this.clearSelections();
18625         }
18626         for(var i = 0, len = rows.length; i < len; i++){
18627             this.selectRow(rows[i], true);
18628         }
18629     },
18630
18631     /**
18632      * Selects a range of rows. All rows in between startRow and endRow are also selected.
18633      * @param {Number} startRow The index of the first row in the range
18634      * @param {Number} endRow The index of the last row in the range
18635      * @param {Boolean} keepExisting (optional) True to retain existing selections
18636      */
18637     selectRange : function(startRow, endRow, keepExisting){
18638         if(this.locked) return;
18639         if(!keepExisting){
18640             this.clearSelections();
18641         }
18642         if(startRow <= endRow){
18643             for(var i = startRow; i <= endRow; i++){
18644                 this.selectRow(i, true);
18645             }
18646         }else{
18647             for(var i = startRow; i >= endRow; i--){
18648                 this.selectRow(i, true);
18649             }
18650         }
18651     },
18652
18653     /**
18654      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
18655      * @param {Number} startRow The index of the first row in the range
18656      * @param {Number} endRow The index of the last row in the range
18657      */
18658     deselectRange : function(startRow, endRow, preventViewNotify){
18659         if(this.locked) return;
18660         for(var i = startRow; i <= endRow; i++){
18661             this.deselectRow(i, preventViewNotify);
18662         }
18663     },
18664
18665     /**
18666      * Selects a row.
18667      * @param {Number} row The index of the row to select
18668      * @param {Boolean} keepExisting (optional) True to keep existing selections
18669      */
18670     selectRow : function(index, keepExisting, preventViewNotify){
18671         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
18672         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
18673             if(!keepExisting || this.singleSelect){
18674                 this.clearSelections();
18675             }
18676             var r = this.grid.dataSource.getAt(index);
18677             this.selections.add(r);
18678             this.last = this.lastActive = index;
18679             if(!preventViewNotify){
18680                 this.grid.getView().onRowSelect(index);
18681             }
18682             this.fireEvent("rowselect", this, index, r);
18683             this.fireEvent("selectionchange", this);
18684         }
18685     },
18686
18687     /**
18688      * Deselects a row.
18689      * @param {Number} row The index of the row to deselect
18690      */
18691     deselectRow : function(index, preventViewNotify){
18692         if(this.locked) return;
18693         if(this.last == index){
18694             this.last = false;
18695         }
18696         if(this.lastActive == index){
18697             this.lastActive = false;
18698         }
18699         var r = this.grid.dataSource.getAt(index);
18700         this.selections.remove(r);
18701         if(!preventViewNotify){
18702             this.grid.getView().onRowDeselect(index);
18703         }
18704         this.fireEvent("rowdeselect", this, index);
18705         this.fireEvent("selectionchange", this);
18706     },
18707
18708     // private
18709     restoreLast : function(){
18710         if(this._last){
18711             this.last = this._last;
18712         }
18713     },
18714
18715     // private
18716     acceptsNav : function(row, col, cm){
18717         return !cm.isHidden(col) && cm.isCellEditable(col, row);
18718     },
18719
18720     // private
18721     onEditorKey : function(field, e){
18722         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
18723         if(k == e.TAB){
18724             e.stopEvent();
18725             ed.completeEdit();
18726             if(e.shiftKey){
18727                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
18728             }else{
18729                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
18730             }
18731         }else if(k == e.ENTER && !e.ctrlKey){
18732             e.stopEvent();
18733             ed.completeEdit();
18734             if(e.shiftKey){
18735                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
18736             }else{
18737                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
18738             }
18739         }else if(k == e.ESC){
18740             ed.cancelEdit();
18741         }
18742         if(newCell){
18743             g.startEditing(newCell[0], newCell[1]);
18744         }
18745     }
18746 });/*
18747  * Based on:
18748  * Ext JS Library 1.1.1
18749  * Copyright(c) 2006-2007, Ext JS, LLC.
18750  *
18751  * Originally Released Under LGPL - original licence link has changed is not relivant.
18752  *
18753  * Fork - LGPL
18754  * <script type="text/javascript">
18755  */
18756  
18757 /**
18758  * @class Roo.bootstrap.PagingToolbar
18759  * @extends Roo.Row
18760  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
18761  * @constructor
18762  * Create a new PagingToolbar
18763  * @param {Object} config The config object
18764  */
18765 Roo.bootstrap.PagingToolbar = function(config)
18766 {
18767     // old args format still supported... - xtype is prefered..
18768         // created from xtype...
18769     var ds = config.dataSource;
18770     this.toolbarItems = [];
18771     if (config.items) {
18772         this.toolbarItems = config.items;
18773 //        config.items = [];
18774     }
18775     
18776     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
18777     this.ds = ds;
18778     this.cursor = 0;
18779     if (ds) { 
18780         this.bind(ds);
18781     }
18782     
18783     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
18784     
18785 };
18786
18787 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
18788     /**
18789      * @cfg {Roo.data.Store} dataSource
18790      * The underlying data store providing the paged data
18791      */
18792     /**
18793      * @cfg {String/HTMLElement/Element} container
18794      * container The id or element that will contain the toolbar
18795      */
18796     /**
18797      * @cfg {Boolean} displayInfo
18798      * True to display the displayMsg (defaults to false)
18799      */
18800     /**
18801      * @cfg {Number} pageSize
18802      * The number of records to display per page (defaults to 20)
18803      */
18804     pageSize: 20,
18805     /**
18806      * @cfg {String} displayMsg
18807      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
18808      */
18809     displayMsg : 'Displaying {0} - {1} of {2}',
18810     /**
18811      * @cfg {String} emptyMsg
18812      * The message to display when no records are found (defaults to "No data to display")
18813      */
18814     emptyMsg : 'No data to display',
18815     /**
18816      * Customizable piece of the default paging text (defaults to "Page")
18817      * @type String
18818      */
18819     beforePageText : "Page",
18820     /**
18821      * Customizable piece of the default paging text (defaults to "of %0")
18822      * @type String
18823      */
18824     afterPageText : "of {0}",
18825     /**
18826      * Customizable piece of the default paging text (defaults to "First Page")
18827      * @type String
18828      */
18829     firstText : "First Page",
18830     /**
18831      * Customizable piece of the default paging text (defaults to "Previous Page")
18832      * @type String
18833      */
18834     prevText : "Previous Page",
18835     /**
18836      * Customizable piece of the default paging text (defaults to "Next Page")
18837      * @type String
18838      */
18839     nextText : "Next Page",
18840     /**
18841      * Customizable piece of the default paging text (defaults to "Last Page")
18842      * @type String
18843      */
18844     lastText : "Last Page",
18845     /**
18846      * Customizable piece of the default paging text (defaults to "Refresh")
18847      * @type String
18848      */
18849     refreshText : "Refresh",
18850
18851     buttons : false,
18852     // private
18853     onRender : function(ct, position) 
18854     {
18855         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
18856         this.navgroup.parentId = this.id;
18857         this.navgroup.onRender(this.el, null);
18858         // add the buttons to the navgroup
18859         
18860         if(this.displayInfo){
18861             Roo.log(this.el.select('ul.navbar-nav',true).first());
18862             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
18863             this.displayEl = this.el.select('.x-paging-info', true).first();
18864 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
18865 //            this.displayEl = navel.el.select('span',true).first();
18866         }
18867         
18868         var _this = this;
18869         
18870         if(this.buttons){
18871             Roo.each(_this.buttons, function(e){
18872                Roo.factory(e).onRender(_this.el, null);
18873             });
18874         }
18875             
18876         Roo.each(_this.toolbarItems, function(e) {
18877             _this.navgroup.addItem(e);
18878         });
18879         
18880         this.first = this.navgroup.addItem({
18881             tooltip: this.firstText,
18882             cls: "prev",
18883             icon : 'fa fa-backward',
18884             disabled: true,
18885             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
18886         });
18887         
18888         this.prev =  this.navgroup.addItem({
18889             tooltip: this.prevText,
18890             cls: "prev",
18891             icon : 'fa fa-step-backward',
18892             disabled: true,
18893             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
18894         });
18895     //this.addSeparator();
18896         
18897         
18898         var field = this.navgroup.addItem( {
18899             tagtype : 'span',
18900             cls : 'x-paging-position',
18901             
18902             html : this.beforePageText  +
18903                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
18904                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
18905          } ); //?? escaped?
18906         
18907         this.field = field.el.select('input', true).first();
18908         this.field.on("keydown", this.onPagingKeydown, this);
18909         this.field.on("focus", function(){this.dom.select();});
18910     
18911     
18912         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
18913         //this.field.setHeight(18);
18914         //this.addSeparator();
18915         this.next = this.navgroup.addItem({
18916             tooltip: this.nextText,
18917             cls: "next",
18918             html : ' <i class="fa fa-step-forward">',
18919             disabled: true,
18920             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
18921         });
18922         this.last = this.navgroup.addItem({
18923             tooltip: this.lastText,
18924             icon : 'fa fa-forward',
18925             cls: "next",
18926             disabled: true,
18927             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
18928         });
18929     //this.addSeparator();
18930         this.loading = this.navgroup.addItem({
18931             tooltip: this.refreshText,
18932             icon: 'fa fa-refresh',
18933             
18934             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
18935         });
18936
18937     },
18938
18939     // private
18940     updateInfo : function(){
18941         if(this.displayEl){
18942             var count = this.ds.getCount();
18943             var msg = count == 0 ?
18944                 this.emptyMsg :
18945                 String.format(
18946                     this.displayMsg,
18947                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
18948                 );
18949             this.displayEl.update(msg);
18950         }
18951     },
18952
18953     // private
18954     onLoad : function(ds, r, o){
18955        this.cursor = o.params ? o.params.start : 0;
18956        var d = this.getPageData(),
18957             ap = d.activePage,
18958             ps = d.pages;
18959         
18960        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
18961        this.field.dom.value = ap;
18962        this.first.setDisabled(ap == 1);
18963        this.prev.setDisabled(ap == 1);
18964        this.next.setDisabled(ap == ps);
18965        this.last.setDisabled(ap == ps);
18966        this.loading.enable();
18967        this.updateInfo();
18968     },
18969
18970     // private
18971     getPageData : function(){
18972         var total = this.ds.getTotalCount();
18973         return {
18974             total : total,
18975             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
18976             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
18977         };
18978     },
18979
18980     // private
18981     onLoadError : function(){
18982         this.loading.enable();
18983     },
18984
18985     // private
18986     onPagingKeydown : function(e){
18987         var k = e.getKey();
18988         var d = this.getPageData();
18989         if(k == e.RETURN){
18990             var v = this.field.dom.value, pageNum;
18991             if(!v || isNaN(pageNum = parseInt(v, 10))){
18992                 this.field.dom.value = d.activePage;
18993                 return;
18994             }
18995             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
18996             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
18997             e.stopEvent();
18998         }
18999         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))
19000         {
19001           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
19002           this.field.dom.value = pageNum;
19003           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
19004           e.stopEvent();
19005         }
19006         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
19007         {
19008           var v = this.field.dom.value, pageNum; 
19009           var increment = (e.shiftKey) ? 10 : 1;
19010           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
19011             increment *= -1;
19012           if(!v || isNaN(pageNum = parseInt(v, 10))) {
19013             this.field.dom.value = d.activePage;
19014             return;
19015           }
19016           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
19017           {
19018             this.field.dom.value = parseInt(v, 10) + increment;
19019             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
19020             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
19021           }
19022           e.stopEvent();
19023         }
19024     },
19025
19026     // private
19027     beforeLoad : function(){
19028         if(this.loading){
19029             this.loading.disable();
19030         }
19031     },
19032
19033     // private
19034     onClick : function(which){
19035         var ds = this.ds;
19036         if (!ds) {
19037             return;
19038         }
19039         switch(which){
19040             case "first":
19041                 ds.load({params:{start: 0, limit: this.pageSize}});
19042             break;
19043             case "prev":
19044                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
19045             break;
19046             case "next":
19047                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
19048             break;
19049             case "last":
19050                 var total = ds.getTotalCount();
19051                 var extra = total % this.pageSize;
19052                 var lastStart = extra ? (total - extra) : total-this.pageSize;
19053                 ds.load({params:{start: lastStart, limit: this.pageSize}});
19054             break;
19055             case "refresh":
19056                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
19057             break;
19058         }
19059     },
19060
19061     /**
19062      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
19063      * @param {Roo.data.Store} store The data store to unbind
19064      */
19065     unbind : function(ds){
19066         ds.un("beforeload", this.beforeLoad, this);
19067         ds.un("load", this.onLoad, this);
19068         ds.un("loadexception", this.onLoadError, this);
19069         ds.un("remove", this.updateInfo, this);
19070         ds.un("add", this.updateInfo, this);
19071         this.ds = undefined;
19072     },
19073
19074     /**
19075      * Binds the paging toolbar to the specified {@link Roo.data.Store}
19076      * @param {Roo.data.Store} store The data store to bind
19077      */
19078     bind : function(ds){
19079         ds.on("beforeload", this.beforeLoad, this);
19080         ds.on("load", this.onLoad, this);
19081         ds.on("loadexception", this.onLoadError, this);
19082         ds.on("remove", this.updateInfo, this);
19083         ds.on("add", this.updateInfo, this);
19084         this.ds = ds;
19085     }
19086 });/*
19087  * - LGPL
19088  *
19089  * element
19090  * 
19091  */
19092
19093 /**
19094  * @class Roo.bootstrap.MessageBar
19095  * @extends Roo.bootstrap.Component
19096  * Bootstrap MessageBar class
19097  * @cfg {String} html contents of the MessageBar
19098  * @cfg {String} weight (info | success | warning | danger) default info
19099  * @cfg {String} beforeClass insert the bar before the given class
19100  * @cfg {Boolean} closable (true | false) default false
19101  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
19102  * 
19103  * @constructor
19104  * Create a new Element
19105  * @param {Object} config The config object
19106  */
19107
19108 Roo.bootstrap.MessageBar = function(config){
19109     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
19110 };
19111
19112 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
19113     
19114     html: '',
19115     weight: 'info',
19116     closable: false,
19117     fixed: false,
19118     beforeClass: 'bootstrap-sticky-wrap',
19119     
19120     getAutoCreate : function(){
19121         
19122         var cfg = {
19123             tag: 'div',
19124             cls: 'alert alert-dismissable alert-' + this.weight,
19125             cn: [
19126                 {
19127                     tag: 'span',
19128                     cls: 'message',
19129                     html: this.html || ''
19130                 }
19131             ]
19132         }
19133         
19134         if(this.fixed){
19135             cfg.cls += ' alert-messages-fixed';
19136         }
19137         
19138         if(this.closable){
19139             cfg.cn.push({
19140                 tag: 'button',
19141                 cls: 'close',
19142                 html: 'x'
19143             });
19144         }
19145         
19146         return cfg;
19147     },
19148     
19149     onRender : function(ct, position)
19150     {
19151         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19152         
19153         if(!this.el){
19154             var cfg = Roo.apply({},  this.getAutoCreate());
19155             cfg.id = Roo.id();
19156             
19157             if (this.cls) {
19158                 cfg.cls += ' ' + this.cls;
19159             }
19160             if (this.style) {
19161                 cfg.style = this.style;
19162             }
19163             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
19164             
19165             this.el.setVisibilityMode(Roo.Element.DISPLAY);
19166         }
19167         
19168         this.el.select('>button.close').on('click', this.hide, this);
19169         
19170     },
19171     
19172     show : function()
19173     {
19174         if (!this.rendered) {
19175             this.render();
19176         }
19177         
19178         this.el.show();
19179         
19180         this.fireEvent('show', this);
19181         
19182     },
19183     
19184     hide : function()
19185     {
19186         if (!this.rendered) {
19187             this.render();
19188         }
19189         
19190         this.el.hide();
19191         
19192         this.fireEvent('hide', this);
19193     },
19194     
19195     update : function()
19196     {
19197 //        var e = this.el.dom.firstChild;
19198 //        
19199 //        if(this.closable){
19200 //            e = e.nextSibling;
19201 //        }
19202 //        
19203 //        e.data = this.html || '';
19204
19205         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
19206     }
19207    
19208 });
19209
19210  
19211
19212      /*
19213  * - LGPL
19214  *
19215  * Graph
19216  * 
19217  */
19218
19219
19220 /**
19221  * @class Roo.bootstrap.Graph
19222  * @extends Roo.bootstrap.Component
19223  * Bootstrap Graph class
19224 > Prameters
19225  -sm {number} sm 4
19226  -md {number} md 5
19227  @cfg {String} graphtype  bar | vbar | pie
19228  @cfg {number} g_x coodinator | centre x (pie)
19229  @cfg {number} g_y coodinator | centre y (pie)
19230  @cfg {number} g_r radius (pie)
19231  @cfg {number} g_height height of the chart (respected by all elements in the set)
19232  @cfg {number} g_width width of the chart (respected by all elements in the set)
19233  @cfg {Object} title The title of the chart
19234     
19235  -{Array}  values
19236  -opts (object) options for the chart 
19237      o {
19238      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
19239      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
19240      o vgutter (number)
19241      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.
19242      o stacked (boolean) whether or not to tread values as in a stacked bar chart
19243      o to
19244      o stretch (boolean)
19245      o }
19246  -opts (object) options for the pie
19247      o{
19248      o cut
19249      o startAngle (number)
19250      o endAngle (number)
19251      } 
19252  *
19253  * @constructor
19254  * Create a new Input
19255  * @param {Object} config The config object
19256  */
19257
19258 Roo.bootstrap.Graph = function(config){
19259     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
19260     
19261     this.addEvents({
19262         // img events
19263         /**
19264          * @event click
19265          * The img click event for the img.
19266          * @param {Roo.EventObject} e
19267          */
19268         "click" : true
19269     });
19270 };
19271
19272 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
19273     
19274     sm: 4,
19275     md: 5,
19276     graphtype: 'bar',
19277     g_height: 250,
19278     g_width: 400,
19279     g_x: 50,
19280     g_y: 50,
19281     g_r: 30,
19282     opts:{
19283         //g_colors: this.colors,
19284         g_type: 'soft',
19285         g_gutter: '20%'
19286
19287     },
19288     title : false,
19289
19290     getAutoCreate : function(){
19291         
19292         var cfg = {
19293             tag: 'div',
19294             html : null
19295         }
19296         
19297         
19298         return  cfg;
19299     },
19300
19301     onRender : function(ct,position){
19302         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
19303         this.raphael = Raphael(this.el.dom);
19304         
19305                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19306                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19307                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19308                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
19309                 /*
19310                 r.text(160, 10, "Single Series Chart").attr(txtattr);
19311                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
19312                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
19313                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
19314                 
19315                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
19316                 r.barchart(330, 10, 300, 220, data1);
19317                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
19318                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
19319                 */
19320                 
19321                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19322                 // r.barchart(30, 30, 560, 250,  xdata, {
19323                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
19324                 //     axis : "0 0 1 1",
19325                 //     axisxlabels :  xdata
19326                 //     //yvalues : cols,
19327                    
19328                 // });
19329 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19330 //        
19331 //        this.load(null,xdata,{
19332 //                axis : "0 0 1 1",
19333 //                axisxlabels :  xdata
19334 //                });
19335
19336     },
19337
19338     load : function(graphtype,xdata,opts){
19339         this.raphael.clear();
19340         if(!graphtype) {
19341             graphtype = this.graphtype;
19342         }
19343         if(!opts){
19344             opts = this.opts;
19345         }
19346         var r = this.raphael,
19347             fin = function () {
19348                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
19349             },
19350             fout = function () {
19351                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
19352             },
19353             pfin = function() {
19354                 this.sector.stop();
19355                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
19356
19357                 if (this.label) {
19358                     this.label[0].stop();
19359                     this.label[0].attr({ r: 7.5 });
19360                     this.label[1].attr({ "font-weight": 800 });
19361                 }
19362             },
19363             pfout = function() {
19364                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
19365
19366                 if (this.label) {
19367                     this.label[0].animate({ r: 5 }, 500, "bounce");
19368                     this.label[1].attr({ "font-weight": 400 });
19369                 }
19370             };
19371
19372         switch(graphtype){
19373             case 'bar':
19374                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
19375                 break;
19376             case 'hbar':
19377                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
19378                 break;
19379             case 'pie':
19380 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
19381 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
19382 //            
19383                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
19384                 
19385                 break;
19386
19387         }
19388         
19389         if(this.title){
19390             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
19391         }
19392         
19393     },
19394     
19395     setTitle: function(o)
19396     {
19397         this.title = o;
19398     },
19399     
19400     initEvents: function() {
19401         
19402         if(!this.href){
19403             this.el.on('click', this.onClick, this);
19404         }
19405     },
19406     
19407     onClick : function(e)
19408     {
19409         Roo.log('img onclick');
19410         this.fireEvent('click', this, e);
19411     }
19412    
19413 });
19414
19415  
19416 /*
19417  * - LGPL
19418  *
19419  * numberBox
19420  * 
19421  */
19422 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19423
19424 /**
19425  * @class Roo.bootstrap.dash.NumberBox
19426  * @extends Roo.bootstrap.Component
19427  * Bootstrap NumberBox class
19428  * @cfg {String} bgcolor Box background color, such as (aqua | green | yellow | red etc..) Default aqua
19429  * @cfg {String} headline Box headline
19430  * @cfg {String} content Box content
19431  * @cfg {String} icon Box icon
19432  * @cfg {String} footer Footer text
19433  * @cfg {String} fhref Footer href
19434  * 
19435  * @constructor
19436  * Create a new NumberBox
19437  * @param {Object} config The config object
19438  */
19439
19440
19441 Roo.bootstrap.dash.NumberBox = function(config){
19442     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
19443     
19444 };
19445
19446 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
19447     
19448     bgcolor : 'aqua',
19449     headline : '',
19450     content : '',
19451     icon : '',
19452     footer : '',
19453     fhref : '',
19454     ficon : '',
19455     
19456     getAutoCreate : function(){
19457         
19458         var cfg = {
19459             tag : 'div',
19460             cls : 'small-box bg-' + this.bgcolor,
19461             cn : [
19462                 {
19463                     tag : 'div',
19464                     cls : 'inner',
19465                     cn :[
19466                         {
19467                             tag : 'h3',
19468                             cls : 'roo-headline',
19469                             html : this.headline
19470                         },
19471                         {
19472                             tag : 'p',
19473                             cls : 'roo-content',
19474                             html : this.content
19475                         }
19476                     ]
19477                 }
19478             ]
19479         }
19480         
19481         if(this.icon){
19482             cfg.cn.push({
19483                 tag : 'div',
19484                 cls : 'icon',
19485                 cn :[
19486                     {
19487                         tag : 'i',
19488                         cls : 'ion ' + this.icon
19489                     }
19490                 ]
19491             });
19492         }
19493         
19494         if(this.footer){
19495             var footer = {
19496                 tag : 'a',
19497                 cls : 'small-box-footer',
19498                 href : this.fhref || '#',
19499                 html : this.footer
19500             };
19501             
19502             cfg.cn.push(footer);
19503             
19504         }
19505         
19506         return  cfg;
19507     },
19508
19509     onRender : function(ct,position){
19510         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
19511
19512
19513        
19514                 
19515     },
19516
19517     setHeadline: function (value)
19518     {
19519         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
19520     },
19521     
19522     setFooter: function (value, href)
19523     {
19524         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
19525         
19526         if(href){
19527             this.el.select('a.small-box-footer',true).first().attr('href', href);
19528         }
19529         
19530     },
19531
19532     setContent: function (value)
19533     {
19534         this.el.select('.roo-content',true).first().dom.innerHTML = value;
19535     },
19536
19537     initEvents: function() 
19538     {   
19539         
19540     }
19541     
19542 });
19543
19544  
19545 /*
19546  * - LGPL
19547  *
19548  * TabBox
19549  * 
19550  */
19551 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19552
19553 /**
19554  * @class Roo.bootstrap.dash.TabBox
19555  * @extends Roo.bootstrap.Component
19556  * Bootstrap TabBox class
19557  * @cfg {String} title Title of the TabBox
19558  * @cfg {String} icon Icon of the TabBox
19559  * @cfg {Boolean} showtabs (true|false) show the tabs default true
19560  * 
19561  * @constructor
19562  * Create a new TabBox
19563  * @param {Object} config The config object
19564  */
19565
19566
19567 Roo.bootstrap.dash.TabBox = function(config){
19568     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
19569     this.addEvents({
19570         // raw events
19571         /**
19572          * @event addpane
19573          * When a pane is added
19574          * @param {Roo.bootstrap.dash.TabPane} pane
19575          */
19576         "addpane" : true
19577          
19578     });
19579 };
19580
19581 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
19582
19583     title : '',
19584     icon : false,
19585     showtabs : true,
19586     
19587     getChildContainer : function()
19588     {
19589         return this.el.select('.tab-content', true).first();
19590     },
19591     
19592     getAutoCreate : function(){
19593         
19594         var header = {
19595             tag: 'li',
19596             cls: 'pull-left header',
19597             html: this.title,
19598             cn : []
19599         };
19600         
19601         if(this.icon){
19602             header.cn.push({
19603                 tag: 'i',
19604                 cls: 'fa ' + this.icon
19605             });
19606         }
19607         
19608         
19609         var cfg = {
19610             tag: 'div',
19611             cls: 'nav-tabs-custom',
19612             cn: [
19613                 {
19614                     tag: 'ul',
19615                     cls: 'nav nav-tabs pull-right',
19616                     cn: [
19617                         header
19618                     ]
19619                 },
19620                 {
19621                     tag: 'div',
19622                     cls: 'tab-content no-padding',
19623                     cn: []
19624                 }
19625             ]
19626         }
19627
19628         return  cfg;
19629     },
19630     initEvents : function()
19631     {
19632         //Roo.log('add add pane handler');
19633         this.on('addpane', this.onAddPane, this);
19634     },
19635      /**
19636      * Updates the box title
19637      * @param {String} html to set the title to.
19638      */
19639     setTitle : function(value)
19640     {
19641         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
19642     },
19643     onAddPane : function(pane)
19644     {
19645         //Roo.log('addpane');
19646         //Roo.log(pane);
19647         // tabs are rendere left to right..
19648         if(!this.showtabs){
19649             return;
19650         }
19651         
19652         var ctr = this.el.select('.nav-tabs', true).first();
19653          
19654          
19655         var existing = ctr.select('.nav-tab',true);
19656         var qty = existing.getCount();;
19657         
19658         
19659         var tab = ctr.createChild({
19660             tag : 'li',
19661             cls : 'nav-tab' + (qty ? '' : ' active'),
19662             cn : [
19663                 {
19664                     tag : 'a',
19665                     href:'#',
19666                     html : pane.title
19667                 }
19668             ]
19669         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
19670         pane.tab = tab;
19671         
19672         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
19673         if (!qty) {
19674             pane.el.addClass('active');
19675         }
19676         
19677                 
19678     },
19679     onTabClick : function(ev,un,ob,pane)
19680     {
19681         //Roo.log('tab - prev default');
19682         ev.preventDefault();
19683         
19684         
19685         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
19686         pane.tab.addClass('active');
19687         //Roo.log(pane.title);
19688         this.getChildContainer().select('.tab-pane',true).removeClass('active');
19689         // technically we should have a deactivate event.. but maybe add later.
19690         // and it should not de-activate the selected tab...
19691         
19692         pane.el.addClass('active');
19693         pane.fireEvent('activate');
19694         
19695         
19696     }
19697     
19698     
19699 });
19700
19701  
19702 /*
19703  * - LGPL
19704  *
19705  * Tab pane
19706  * 
19707  */
19708 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19709 /**
19710  * @class Roo.bootstrap.TabPane
19711  * @extends Roo.bootstrap.Component
19712  * Bootstrap TabPane class
19713  * @cfg {Boolean} active (false | true) Default false
19714  * @cfg {String} title title of panel
19715
19716  * 
19717  * @constructor
19718  * Create a new TabPane
19719  * @param {Object} config The config object
19720  */
19721
19722 Roo.bootstrap.dash.TabPane = function(config){
19723     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
19724     
19725 };
19726
19727 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
19728     
19729     active : false,
19730     title : '',
19731     
19732     // the tabBox that this is attached to.
19733     tab : false,
19734      
19735     getAutoCreate : function() 
19736     {
19737         var cfg = {
19738             tag: 'div',
19739             cls: 'tab-pane'
19740         }
19741         
19742         if(this.active){
19743             cfg.cls += ' active';
19744         }
19745         
19746         return cfg;
19747     },
19748     initEvents  : function()
19749     {
19750         //Roo.log('trigger add pane handler');
19751         this.parent().fireEvent('addpane', this)
19752     },
19753     
19754      /**
19755      * Updates the tab title 
19756      * @param {String} html to set the title to.
19757      */
19758     setTitle: function(str)
19759     {
19760         if (!this.tab) {
19761             return;
19762         }
19763         this.title = str;
19764         this.tab.select('a'.true).first().dom.innerHTML = str;
19765         
19766     }
19767     
19768     
19769     
19770 });
19771
19772  
19773
19774
19775  /*
19776  * - LGPL
19777  *
19778  * menu
19779  * 
19780  */
19781 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
19782
19783 /**
19784  * @class Roo.bootstrap.menu.Menu
19785  * @extends Roo.bootstrap.Component
19786  * Bootstrap Menu class - container for Menu
19787  * @cfg {String} html Text of the menu
19788  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
19789  * @cfg {String} icon Font awesome icon
19790  * @cfg {String} pos Menu align to (top | bottom) default bottom
19791  * 
19792  * 
19793  * @constructor
19794  * Create a new Menu
19795  * @param {Object} config The config object
19796  */
19797
19798
19799 Roo.bootstrap.menu.Menu = function(config){
19800     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
19801     
19802     this.addEvents({
19803         /**
19804          * @event beforeshow
19805          * Fires before this menu is displayed
19806          * @param {Roo.bootstrap.menu.Menu} this
19807          */
19808         beforeshow : true,
19809         /**
19810          * @event beforehide
19811          * Fires before this menu is hidden
19812          * @param {Roo.bootstrap.menu.Menu} this
19813          */
19814         beforehide : true,
19815         /**
19816          * @event show
19817          * Fires after this menu is displayed
19818          * @param {Roo.bootstrap.menu.Menu} this
19819          */
19820         show : true,
19821         /**
19822          * @event hide
19823          * Fires after this menu is hidden
19824          * @param {Roo.bootstrap.menu.Menu} this
19825          */
19826         hide : true,
19827         /**
19828          * @event click
19829          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19830          * @param {Roo.bootstrap.menu.Menu} this
19831          * @param {Roo.EventObject} e
19832          */
19833         click : true
19834     });
19835     
19836 };
19837
19838 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
19839     
19840     submenu : false,
19841     html : '',
19842     weight : 'default',
19843     icon : false,
19844     pos : 'bottom',
19845     
19846     
19847     getChildContainer : function() {
19848         if(this.isSubMenu){
19849             return this.el;
19850         }
19851         
19852         return this.el.select('ul.dropdown-menu', true).first();  
19853     },
19854     
19855     getAutoCreate : function()
19856     {
19857         var text = [
19858             {
19859                 tag : 'span',
19860                 cls : 'roo-menu-text',
19861                 html : this.html
19862             }
19863         ];
19864         
19865         if(this.icon){
19866             text.unshift({
19867                 tag : 'i',
19868                 cls : 'fa ' + this.icon
19869             })
19870         }
19871         
19872         
19873         var cfg = {
19874             tag : 'div',
19875             cls : 'btn-group',
19876             cn : [
19877                 {
19878                     tag : 'button',
19879                     cls : 'dropdown-button btn btn-' + this.weight,
19880                     cn : text
19881                 },
19882                 {
19883                     tag : 'button',
19884                     cls : 'dropdown-toggle btn btn-' + this.weight,
19885                     cn : [
19886                         {
19887                             tag : 'span',
19888                             cls : 'caret'
19889                         }
19890                     ]
19891                 },
19892                 {
19893                     tag : 'ul',
19894                     cls : 'dropdown-menu'
19895                 }
19896             ]
19897             
19898         };
19899         
19900         if(this.pos == 'top'){
19901             cfg.cls += ' dropup';
19902         }
19903         
19904         if(this.isSubMenu){
19905             cfg = {
19906                 tag : 'ul',
19907                 cls : 'dropdown-menu'
19908             }
19909         }
19910         
19911         return cfg;
19912     },
19913     
19914     onRender : function(ct, position)
19915     {
19916         this.isSubMenu = ct.hasClass('dropdown-submenu');
19917         
19918         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
19919     },
19920     
19921     initEvents : function() 
19922     {
19923         if(this.isSubMenu){
19924             return;
19925         }
19926         
19927         this.hidden = true;
19928         
19929         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
19930         this.triggerEl.on('click', this.onTriggerPress, this);
19931         
19932         this.buttonEl = this.el.select('button.dropdown-button', true).first();
19933         this.buttonEl.on('click', this.onClick, this);
19934         
19935     },
19936     
19937     list : function()
19938     {
19939         if(this.isSubMenu){
19940             return this.el;
19941         }
19942         
19943         return this.el.select('ul.dropdown-menu', true).first();
19944     },
19945     
19946     onClick : function(e)
19947     {
19948         this.fireEvent("click", this, e);
19949     },
19950     
19951     onTriggerPress  : function(e)
19952     {   
19953         if (this.isVisible()) {
19954             this.hide();
19955         } else {
19956             this.show();
19957         }
19958     },
19959     
19960     isVisible : function(){
19961         return !this.hidden;
19962     },
19963     
19964     show : function()
19965     {
19966         this.fireEvent("beforeshow", this);
19967         
19968         this.hidden = false;
19969         this.el.addClass('open');
19970         
19971         Roo.get(document).on("mouseup", this.onMouseUp, this);
19972         
19973         this.fireEvent("show", this);
19974         
19975         
19976     },
19977     
19978     hide : function()
19979     {
19980         this.fireEvent("beforehide", this);
19981         
19982         this.hidden = true;
19983         this.el.removeClass('open');
19984         
19985         Roo.get(document).un("mouseup", this.onMouseUp);
19986         
19987         this.fireEvent("hide", this);
19988     },
19989     
19990     onMouseUp : function()
19991     {
19992         this.hide();
19993     }
19994     
19995 });
19996
19997  
19998  /*
19999  * - LGPL
20000  *
20001  * menu item
20002  * 
20003  */
20004 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20005
20006 /**
20007  * @class Roo.bootstrap.menu.Item
20008  * @extends Roo.bootstrap.Component
20009  * Bootstrap MenuItem class
20010  * @cfg {Boolean} submenu (true | false) default false
20011  * @cfg {String} html text of the item
20012  * @cfg {String} href the link
20013  * @cfg {Boolean} disable (true | false) default false
20014  * @cfg {Boolean} preventDefault (true | false) default true
20015  * @cfg {String} icon Font awesome icon
20016  * @cfg {String} pos Submenu align to (left | right) default right 
20017  * 
20018  * 
20019  * @constructor
20020  * Create a new Item
20021  * @param {Object} config The config object
20022  */
20023
20024
20025 Roo.bootstrap.menu.Item = function(config){
20026     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
20027     this.addEvents({
20028         /**
20029          * @event mouseover
20030          * Fires when the mouse is hovering over this menu
20031          * @param {Roo.bootstrap.menu.Item} this
20032          * @param {Roo.EventObject} e
20033          */
20034         mouseover : true,
20035         /**
20036          * @event mouseout
20037          * Fires when the mouse exits this menu
20038          * @param {Roo.bootstrap.menu.Item} this
20039          * @param {Roo.EventObject} e
20040          */
20041         mouseout : true,
20042         // raw events
20043         /**
20044          * @event click
20045          * The raw click event for the entire grid.
20046          * @param {Roo.EventObject} e
20047          */
20048         click : true
20049     });
20050 };
20051
20052 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
20053     
20054     submenu : false,
20055     href : '',
20056     html : '',
20057     preventDefault: true,
20058     disable : false,
20059     icon : false,
20060     pos : 'right',
20061     
20062     getAutoCreate : function()
20063     {
20064         var text = [
20065             {
20066                 tag : 'span',
20067                 cls : 'roo-menu-item-text',
20068                 html : this.html
20069             }
20070         ];
20071         
20072         if(this.icon){
20073             text.unshift({
20074                 tag : 'i',
20075                 cls : 'fa ' + this.icon
20076             })
20077         }
20078         
20079         var cfg = {
20080             tag : 'li',
20081             cn : [
20082                 {
20083                     tag : 'a',
20084                     href : this.href || '#',
20085                     cn : text
20086                 }
20087             ]
20088         };
20089         
20090         if(this.disable){
20091             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
20092         }
20093         
20094         if(this.submenu){
20095             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
20096             
20097             if(this.pos == 'left'){
20098                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
20099             }
20100         }
20101         
20102         return cfg;
20103     },
20104     
20105     initEvents : function() 
20106     {
20107         this.el.on('mouseover', this.onMouseOver, this);
20108         this.el.on('mouseout', this.onMouseOut, this);
20109         
20110         this.el.select('a', true).first().on('click', this.onClick, this);
20111         
20112     },
20113     
20114     onClick : function(e)
20115     {
20116         if(this.preventDefault){
20117             e.preventDefault();
20118         }
20119         
20120         this.fireEvent("click", this, e);
20121     },
20122     
20123     onMouseOver : function(e)
20124     {
20125         if(this.submenu && this.pos == 'left'){
20126             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
20127         }
20128         
20129         this.fireEvent("mouseover", this, e);
20130     },
20131     
20132     onMouseOut : function(e)
20133     {
20134         this.fireEvent("mouseout", this, e);
20135     }
20136 });
20137
20138  
20139
20140  /*
20141  * - LGPL
20142  *
20143  * menu separator
20144  * 
20145  */
20146 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20147
20148 /**
20149  * @class Roo.bootstrap.menu.Separator
20150  * @extends Roo.bootstrap.Component
20151  * Bootstrap Separator class
20152  * 
20153  * @constructor
20154  * Create a new Separator
20155  * @param {Object} config The config object
20156  */
20157
20158
20159 Roo.bootstrap.menu.Separator = function(config){
20160     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
20161 };
20162
20163 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
20164     
20165     getAutoCreate : function(){
20166         var cfg = {
20167             tag : 'li',
20168             cls: 'divider'
20169         };
20170         
20171         return cfg;
20172     }
20173    
20174 });
20175
20176  
20177
20178