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             this.addItem(o);
11109             return;
11110         }
11111             
11112         var dv = ''; // display value
11113         var vv = ''; // value value..
11114         this.lastData = o;
11115         if (this.displayField) {
11116             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11117         } else {
11118             // this is an error condition!!!
11119             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
11120         }
11121         
11122         if(this.valueField){
11123             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
11124         }
11125         
11126         if(this.hiddenField){
11127             this.hiddenField.dom.value = vv;
11128             
11129             this.lastSelectionText = dv;
11130             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11131             this.value = vv;
11132             return;
11133         }
11134         // no hidden field.. - we store the value in 'value', but still display
11135         // display field!!!!
11136         this.lastSelectionText = dv;
11137         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11138         this.value = vv;
11139         
11140         
11141     },
11142     // private
11143     reset : function(){
11144         // overridden so that last data is reset..
11145         this.setValue(this.originalValue);
11146         this.clearInvalid();
11147         this.lastData = false;
11148         if (this.view) {
11149             this.view.clearSelections();
11150         }
11151     },
11152     // private
11153     findRecord : function(prop, value){
11154         var record;
11155         if(this.store.getCount() > 0){
11156             this.store.each(function(r){
11157                 if(r.data[prop] == value){
11158                     record = r;
11159                     return false;
11160                 }
11161                 return true;
11162             });
11163         }
11164         return record;
11165     },
11166     
11167     getName: function()
11168     {
11169         // returns hidden if it's set..
11170         if (!this.rendered) {return ''};
11171         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
11172         
11173     },
11174     // private
11175     onViewMove : function(e, t){
11176         this.inKeyMode = false;
11177     },
11178
11179     // private
11180     onViewOver : function(e, t){
11181         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
11182             return;
11183         }
11184         var item = this.view.findItemFromChild(t);
11185         
11186         if(item){
11187             var index = this.view.indexOf(item);
11188             this.select(index, false);
11189         }
11190     },
11191
11192     // private
11193     onViewClick : function(view, doFocus, el, e)
11194     {
11195         var index = this.view.getSelectedIndexes()[0];
11196         
11197         var r = this.store.getAt(index);
11198         
11199         if(this.tickable){
11200             
11201             if(e.getTarget().nodeName.toLowerCase() != 'input'){
11202                 return;
11203             }
11204             
11205             var rm = false;
11206             var _this = this;
11207             
11208             Roo.each(this.tickItems, function(v,k){
11209                 
11210                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
11211                     _this.tickItems.splice(k, 1);
11212                     rm = true;
11213                     return;
11214                 }
11215             })
11216             
11217             if(rm){
11218                 return;
11219             }
11220             
11221             this.tickItems.push(r.data);
11222             return;
11223         }
11224         
11225         if(r){
11226             this.onSelect(r, index);
11227         }
11228         if(doFocus !== false && !this.blockFocus){
11229             this.inputEl().focus();
11230         }
11231     },
11232
11233     // private
11234     restrictHeight : function(){
11235         //this.innerList.dom.style.height = '';
11236         //var inner = this.innerList.dom;
11237         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
11238         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
11239         //this.list.beginUpdate();
11240         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
11241         this.list.alignTo(this.inputEl(), this.listAlign);
11242         this.list.alignTo(this.inputEl(), this.listAlign);
11243         //this.list.endUpdate();
11244     },
11245
11246     // private
11247     onEmptyResults : function(){
11248         this.collapse();
11249     },
11250
11251     /**
11252      * Returns true if the dropdown list is expanded, else false.
11253      */
11254     isExpanded : function(){
11255         return this.list.isVisible();
11256     },
11257
11258     /**
11259      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
11260      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11261      * @param {String} value The data value of the item to select
11262      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11263      * selected item if it is not currently in view (defaults to true)
11264      * @return {Boolean} True if the value matched an item in the list, else false
11265      */
11266     selectByValue : function(v, scrollIntoView){
11267         if(v !== undefined && v !== null){
11268             var r = this.findRecord(this.valueField || this.displayField, v);
11269             if(r){
11270                 this.select(this.store.indexOf(r), scrollIntoView);
11271                 return true;
11272             }
11273         }
11274         return false;
11275     },
11276
11277     /**
11278      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
11279      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11280      * @param {Number} index The zero-based index of the list item to select
11281      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11282      * selected item if it is not currently in view (defaults to true)
11283      */
11284     select : function(index, scrollIntoView){
11285         this.selectedIndex = index;
11286         this.view.select(index);
11287         if(scrollIntoView !== false){
11288             var el = this.view.getNode(index);
11289             if(el && !this.multiple && !this.tickable){
11290                 this.list.scrollChildIntoView(el, false);
11291             }
11292         }
11293     },
11294
11295     // private
11296     selectNext : function(){
11297         var ct = this.store.getCount();
11298         if(ct > 0){
11299             if(this.selectedIndex == -1){
11300                 this.select(0);
11301             }else if(this.selectedIndex < ct-1){
11302                 this.select(this.selectedIndex+1);
11303             }
11304         }
11305     },
11306
11307     // private
11308     selectPrev : function(){
11309         var ct = this.store.getCount();
11310         if(ct > 0){
11311             if(this.selectedIndex == -1){
11312                 this.select(0);
11313             }else if(this.selectedIndex != 0){
11314                 this.select(this.selectedIndex-1);
11315             }
11316         }
11317     },
11318
11319     // private
11320     onKeyUp : function(e){
11321         if(this.editable !== false && !e.isSpecialKey()){
11322             this.lastKey = e.getKey();
11323             this.dqTask.delay(this.queryDelay);
11324         }
11325     },
11326
11327     // private
11328     validateBlur : function(){
11329         return !this.list || !this.list.isVisible();   
11330     },
11331
11332     // private
11333     initQuery : function(){
11334         this.doQuery(this.getRawValue());
11335     },
11336
11337     // private
11338     doForce : function(){
11339         if(this.inputEl().dom.value.length > 0){
11340             this.inputEl().dom.value =
11341                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
11342              
11343         }
11344     },
11345
11346     /**
11347      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
11348      * query allowing the query action to be canceled if needed.
11349      * @param {String} query The SQL query to execute
11350      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
11351      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
11352      * saved in the current store (defaults to false)
11353      */
11354     doQuery : function(q, forceAll){
11355         
11356         if(q === undefined || q === null){
11357             q = '';
11358         }
11359         var qe = {
11360             query: q,
11361             forceAll: forceAll,
11362             combo: this,
11363             cancel:false
11364         };
11365         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
11366             return false;
11367         }
11368         q = qe.query;
11369         
11370         forceAll = qe.forceAll;
11371         if(forceAll === true || (q.length >= this.minChars)){
11372             
11373             this.hasQuery = true;
11374             
11375             if(this.lastQuery != q || this.alwaysQuery){
11376                 this.lastQuery = q;
11377                 if(this.mode == 'local'){
11378                     this.selectedIndex = -1;
11379                     if(forceAll){
11380                         this.store.clearFilter();
11381                     }else{
11382                         this.store.filter(this.displayField, q);
11383                     }
11384                     this.onLoad();
11385                 }else{
11386                     this.store.baseParams[this.queryParam] = q;
11387                     
11388                     var options = {params : this.getParams(q)};
11389                     
11390                     if(this.loadNext){
11391                         options.add = true;
11392                         options.params.start = this.page * this.pageSize;
11393                     }
11394                     
11395                     this.store.load(options);
11396                     /*
11397                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
11398                      *  we should expand the list on onLoad
11399                      *  so command out it
11400                      */
11401 //                    this.expand();
11402                 }
11403             }else{
11404                 this.selectedIndex = -1;
11405                 this.onLoad();   
11406             }
11407         }
11408         
11409         this.loadNext = false;
11410     },
11411
11412     // private
11413     getParams : function(q){
11414         var p = {};
11415         //p[this.queryParam] = q;
11416         
11417         if(this.pageSize){
11418             p.start = 0;
11419             p.limit = this.pageSize;
11420         }
11421         return p;
11422     },
11423
11424     /**
11425      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
11426      */
11427     collapse : function(){
11428         if(!this.isExpanded()){
11429             return;
11430         }
11431         
11432         this.hasFocus = false;
11433         
11434         this.list.hide();
11435         
11436         if(this.tickable){
11437             this.okBtn.hide();
11438             this.cancelBtn.hide();
11439             this.trigger.show();
11440         }
11441         
11442         Roo.get(document).un('mousedown', this.collapseIf, this);
11443         Roo.get(document).un('mousewheel', this.collapseIf, this);
11444         if (!this.editable) {
11445             Roo.get(document).un('keydown', this.listKeyPress, this);
11446         }
11447         this.fireEvent('collapse', this);
11448     },
11449
11450     // private
11451     collapseIf : function(e){
11452         var in_combo  = e.within(this.el);
11453         var in_list =  e.within(this.list);
11454         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
11455         
11456         if (in_combo || in_list || is_list) {
11457             //e.stopPropagation();
11458             return;
11459         }
11460         
11461         if(this.tickable){
11462             this.onTickableFooterButtonClick(e, false, false);
11463         }
11464
11465         this.collapse();
11466         
11467     },
11468
11469     /**
11470      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
11471      */
11472     expand : function(){
11473        
11474         if(this.isExpanded() || !this.hasFocus){
11475             return;
11476         }
11477          Roo.log('expand');
11478         
11479         this.list.show();
11480         
11481         this.restrictHeight();
11482         
11483         if(this.tickable){
11484             
11485             this.tickItems = Roo.apply([], this.item);
11486             
11487             this.okBtn.show();
11488             this.cancelBtn.show();
11489             this.trigger.hide();
11490             
11491         }
11492         
11493         Roo.get(document).on('mousedown', this.collapseIf, this);
11494         Roo.get(document).on('mousewheel', this.collapseIf, this);
11495         if (!this.editable) {
11496             Roo.get(document).on('keydown', this.listKeyPress, this);
11497         }
11498         
11499         this.fireEvent('expand', this);
11500     },
11501
11502     // private
11503     // Implements the default empty TriggerField.onTriggerClick function
11504     onTriggerClick : function(e)
11505     {
11506         Roo.log('trigger click');
11507         
11508         if(this.disabled || !this.triggerList){
11509             return;
11510         }
11511         
11512         this.page = 0;
11513         this.loadNext = false;
11514         
11515         if(this.isExpanded()){
11516             this.collapse();
11517             if (!this.blockFocus) {
11518                 this.inputEl().focus();
11519             }
11520             
11521         }else {
11522             this.hasFocus = true;
11523             if(this.triggerAction == 'all') {
11524                 this.doQuery(this.allQuery, true);
11525             } else {
11526                 this.doQuery(this.getRawValue());
11527             }
11528             if (!this.blockFocus) {
11529                 this.inputEl().focus();
11530             }
11531         }
11532     },
11533     
11534     onTickableTriggerClick : function(e)
11535     {
11536         if(this.disabled){
11537             return;
11538         }
11539         
11540         this.page = 0;
11541         this.loadNext = false;
11542         this.hasFocus = true;
11543         
11544         if(this.triggerAction == 'all') {
11545             this.doQuery(this.allQuery, true);
11546         } else {
11547             this.doQuery(this.getRawValue());
11548         }
11549     },
11550     
11551     onSearchFieldClick : function(e)
11552     {
11553         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
11554             return;
11555         }
11556         
11557         this.page = 0;
11558         this.loadNext = false;
11559         this.hasFocus = true;
11560         
11561         if(this.triggerAction == 'all') {
11562             this.doQuery(this.allQuery, true);
11563         } else {
11564             this.doQuery(this.getRawValue());
11565         }
11566     },
11567     
11568     listKeyPress : function(e)
11569     {
11570         //Roo.log('listkeypress');
11571         // scroll to first matching element based on key pres..
11572         if (e.isSpecialKey()) {
11573             return false;
11574         }
11575         var k = String.fromCharCode(e.getKey()).toUpperCase();
11576         //Roo.log(k);
11577         var match  = false;
11578         var csel = this.view.getSelectedNodes();
11579         var cselitem = false;
11580         if (csel.length) {
11581             var ix = this.view.indexOf(csel[0]);
11582             cselitem  = this.store.getAt(ix);
11583             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
11584                 cselitem = false;
11585             }
11586             
11587         }
11588         
11589         this.store.each(function(v) { 
11590             if (cselitem) {
11591                 // start at existing selection.
11592                 if (cselitem.id == v.id) {
11593                     cselitem = false;
11594                 }
11595                 return true;
11596             }
11597                 
11598             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
11599                 match = this.store.indexOf(v);
11600                 return false;
11601             }
11602             return true;
11603         }, this);
11604         
11605         if (match === false) {
11606             return true; // no more action?
11607         }
11608         // scroll to?
11609         this.view.select(match);
11610         var sn = Roo.get(this.view.getSelectedNodes()[0])
11611         //sn.scrollIntoView(sn.dom.parentNode, false);
11612     },
11613     
11614     onViewScroll : function(e, t){
11615         
11616         if(this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
11617             return;
11618         }
11619         
11620         this.hasQuery = true;
11621         
11622         this.loading = this.list.select('.loading', true).first();
11623         
11624         if(this.loading === null){
11625             this.list.createChild({
11626                 tag: 'div',
11627                 cls: 'loading select2-more-results select2-active',
11628                 html: 'Loading more results...'
11629             })
11630             
11631             this.loading = this.list.select('.loading', true).first();
11632             
11633             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
11634             
11635             this.loading.hide();
11636         }
11637         
11638         this.loading.show();
11639         
11640         var _combo = this;
11641         
11642         this.page++;
11643         this.loadNext = true;
11644         
11645         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
11646         
11647         return;
11648     },
11649     
11650     addItem : function(o)
11651     {   
11652         var dv = ''; // display value
11653         
11654         if (this.displayField) {
11655             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11656         } else {
11657             // this is an error condition!!!
11658             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
11659         }
11660         
11661         if(!dv.length){
11662             return;
11663         }
11664         
11665         var choice = this.choices.createChild({
11666             tag: 'li',
11667             cls: 'select2-search-choice',
11668             cn: [
11669                 {
11670                     tag: 'div',
11671                     html: dv
11672                 },
11673                 {
11674                     tag: 'a',
11675                     href: '#',
11676                     cls: 'select2-search-choice-close',
11677                     tabindex: '-1'
11678                 }
11679             ]
11680             
11681         }, this.searchField);
11682         
11683         var close = choice.select('a.select2-search-choice-close', true).first()
11684         
11685         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
11686         
11687         this.item.push(o);
11688         
11689         this.lastData = o;
11690         
11691         this.syncValue();
11692         
11693         this.inputEl().dom.value = '';
11694         
11695     },
11696     
11697     onRemoveItem : function(e, _self, o)
11698     {
11699         e.preventDefault();
11700         var index = this.item.indexOf(o.data) * 1;
11701         
11702         if( index < 0){
11703             Roo.log('not this item?!');
11704             return;
11705         }
11706         
11707         this.item.splice(index, 1);
11708         o.item.remove();
11709         
11710         this.syncValue();
11711         
11712         this.fireEvent('remove', this, e);
11713         
11714     },
11715     
11716     syncValue : function()
11717     {
11718         if(!this.item.length){
11719             this.clearValue();
11720             return;
11721         }
11722             
11723         var value = [];
11724         var _this = this;
11725         Roo.each(this.item, function(i){
11726             if(_this.valueField){
11727                 value.push(i[_this.valueField]);
11728                 return;
11729             }
11730
11731             value.push(i);
11732         });
11733
11734         this.value = value.join(',');
11735
11736         if(this.hiddenField){
11737             this.hiddenField.dom.value = this.value;
11738         }
11739     },
11740     
11741     clearItem : function()
11742     {
11743         if(!this.multiple){
11744             return;
11745         }
11746         
11747         this.item = [];
11748         
11749         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
11750            c.remove();
11751         });
11752         
11753         this.syncValue();
11754     },
11755     
11756     inputEl: function ()
11757     {
11758         if(this.tickable){
11759             return this.searchField;
11760         }
11761         return this.el.select('input.form-control',true).first();
11762     },
11763     
11764     
11765     onTickableFooterButtonClick : function(e, btn, el)
11766     {
11767         e.preventDefault();
11768         
11769         if(btn && btn.name == 'cancel'){
11770             this.tickItems = Roo.apply([], this.item);
11771             this.collapse();
11772             return;
11773         }
11774         
11775         this.clearItem();
11776         
11777         var _this = this;
11778         
11779         Roo.each(this.tickItems, function(o){
11780             _this.addItem(o);
11781         });
11782         
11783         this.collapse();
11784         
11785     }
11786     
11787     
11788
11789     /** 
11790     * @cfg {Boolean} grow 
11791     * @hide 
11792     */
11793     /** 
11794     * @cfg {Number} growMin 
11795     * @hide 
11796     */
11797     /** 
11798     * @cfg {Number} growMax 
11799     * @hide 
11800     */
11801     /**
11802      * @hide
11803      * @method autoSize
11804      */
11805 });
11806 /*
11807  * Based on:
11808  * Ext JS Library 1.1.1
11809  * Copyright(c) 2006-2007, Ext JS, LLC.
11810  *
11811  * Originally Released Under LGPL - original licence link has changed is not relivant.
11812  *
11813  * Fork - LGPL
11814  * <script type="text/javascript">
11815  */
11816
11817 /**
11818  * @class Roo.View
11819  * @extends Roo.util.Observable
11820  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
11821  * This class also supports single and multi selection modes. <br>
11822  * Create a data model bound view:
11823  <pre><code>
11824  var store = new Roo.data.Store(...);
11825
11826  var view = new Roo.View({
11827     el : "my-element",
11828     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
11829  
11830     singleSelect: true,
11831     selectedClass: "ydataview-selected",
11832     store: store
11833  });
11834
11835  // listen for node click?
11836  view.on("click", function(vw, index, node, e){
11837  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
11838  });
11839
11840  // load XML data
11841  dataModel.load("foobar.xml");
11842  </code></pre>
11843  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
11844  * <br><br>
11845  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
11846  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
11847  * 
11848  * Note: old style constructor is still suported (container, template, config)
11849  * 
11850  * @constructor
11851  * Create a new View
11852  * @param {Object} config The config object
11853  * 
11854  */
11855 Roo.View = function(config, depreciated_tpl, depreciated_config){
11856     
11857     this.parent = false;
11858     
11859     if (typeof(depreciated_tpl) == 'undefined') {
11860         // new way.. - universal constructor.
11861         Roo.apply(this, config);
11862         this.el  = Roo.get(this.el);
11863     } else {
11864         // old format..
11865         this.el  = Roo.get(config);
11866         this.tpl = depreciated_tpl;
11867         Roo.apply(this, depreciated_config);
11868     }
11869     this.wrapEl  = this.el.wrap().wrap();
11870     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
11871     
11872     
11873     if(typeof(this.tpl) == "string"){
11874         this.tpl = new Roo.Template(this.tpl);
11875     } else {
11876         // support xtype ctors..
11877         this.tpl = new Roo.factory(this.tpl, Roo);
11878     }
11879     
11880     
11881     this.tpl.compile();
11882     
11883     /** @private */
11884     this.addEvents({
11885         /**
11886          * @event beforeclick
11887          * Fires before a click is processed. Returns false to cancel the default action.
11888          * @param {Roo.View} this
11889          * @param {Number} index The index of the target node
11890          * @param {HTMLElement} node The target node
11891          * @param {Roo.EventObject} e The raw event object
11892          */
11893             "beforeclick" : true,
11894         /**
11895          * @event click
11896          * Fires when a template node is clicked.
11897          * @param {Roo.View} this
11898          * @param {Number} index The index of the target node
11899          * @param {HTMLElement} node The target node
11900          * @param {Roo.EventObject} e The raw event object
11901          */
11902             "click" : true,
11903         /**
11904          * @event dblclick
11905          * Fires when a template node is double clicked.
11906          * @param {Roo.View} this
11907          * @param {Number} index The index of the target node
11908          * @param {HTMLElement} node The target node
11909          * @param {Roo.EventObject} e The raw event object
11910          */
11911             "dblclick" : true,
11912         /**
11913          * @event contextmenu
11914          * Fires when a template node is right clicked.
11915          * @param {Roo.View} this
11916          * @param {Number} index The index of the target node
11917          * @param {HTMLElement} node The target node
11918          * @param {Roo.EventObject} e The raw event object
11919          */
11920             "contextmenu" : true,
11921         /**
11922          * @event selectionchange
11923          * Fires when the selected nodes change.
11924          * @param {Roo.View} this
11925          * @param {Array} selections Array of the selected nodes
11926          */
11927             "selectionchange" : true,
11928     
11929         /**
11930          * @event beforeselect
11931          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
11932          * @param {Roo.View} this
11933          * @param {HTMLElement} node The node to be selected
11934          * @param {Array} selections Array of currently selected nodes
11935          */
11936             "beforeselect" : true,
11937         /**
11938          * @event preparedata
11939          * Fires on every row to render, to allow you to change the data.
11940          * @param {Roo.View} this
11941          * @param {Object} data to be rendered (change this)
11942          */
11943           "preparedata" : true
11944           
11945           
11946         });
11947
11948
11949
11950     this.el.on({
11951         "click": this.onClick,
11952         "dblclick": this.onDblClick,
11953         "contextmenu": this.onContextMenu,
11954         scope:this
11955     });
11956
11957     this.selections = [];
11958     this.nodes = [];
11959     this.cmp = new Roo.CompositeElementLite([]);
11960     if(this.store){
11961         this.store = Roo.factory(this.store, Roo.data);
11962         this.setStore(this.store, true);
11963     }
11964     
11965     if ( this.footer && this.footer.xtype) {
11966            
11967          var fctr = this.wrapEl.appendChild(document.createElement("div"));
11968         
11969         this.footer.dataSource = this.store
11970         this.footer.container = fctr;
11971         this.footer = Roo.factory(this.footer, Roo);
11972         fctr.insertFirst(this.el);
11973         
11974         // this is a bit insane - as the paging toolbar seems to detach the el..
11975 //        dom.parentNode.parentNode.parentNode
11976          // they get detached?
11977     }
11978     
11979     
11980     Roo.View.superclass.constructor.call(this);
11981     
11982     
11983 };
11984
11985 Roo.extend(Roo.View, Roo.util.Observable, {
11986     
11987      /**
11988      * @cfg {Roo.data.Store} store Data store to load data from.
11989      */
11990     store : false,
11991     
11992     /**
11993      * @cfg {String|Roo.Element} el The container element.
11994      */
11995     el : '',
11996     
11997     /**
11998      * @cfg {String|Roo.Template} tpl The template used by this View 
11999      */
12000     tpl : false,
12001     /**
12002      * @cfg {String} dataName the named area of the template to use as the data area
12003      *                          Works with domtemplates roo-name="name"
12004      */
12005     dataName: false,
12006     /**
12007      * @cfg {String} selectedClass The css class to add to selected nodes
12008      */
12009     selectedClass : "x-view-selected",
12010      /**
12011      * @cfg {String} emptyText The empty text to show when nothing is loaded.
12012      */
12013     emptyText : "",
12014     
12015     /**
12016      * @cfg {String} text to display on mask (default Loading)
12017      */
12018     mask : false,
12019     /**
12020      * @cfg {Boolean} multiSelect Allow multiple selection
12021      */
12022     multiSelect : false,
12023     /**
12024      * @cfg {Boolean} singleSelect Allow single selection
12025      */
12026     singleSelect:  false,
12027     
12028     /**
12029      * @cfg {Boolean} toggleSelect - selecting 
12030      */
12031     toggleSelect : false,
12032     
12033     /**
12034      * @cfg {Boolean} tickable - selecting 
12035      */
12036     tickable : false,
12037     
12038     /**
12039      * Returns the element this view is bound to.
12040      * @return {Roo.Element}
12041      */
12042     getEl : function(){
12043         return this.wrapEl;
12044     },
12045     
12046     
12047
12048     /**
12049      * Refreshes the view. - called by datachanged on the store. - do not call directly.
12050      */
12051     refresh : function(){
12052         Roo.log('refresh');
12053         var t = this.tpl;
12054         
12055         // if we are using something like 'domtemplate', then
12056         // the what gets used is:
12057         // t.applySubtemplate(NAME, data, wrapping data..)
12058         // the outer template then get' applied with
12059         //     the store 'extra data'
12060         // and the body get's added to the
12061         //      roo-name="data" node?
12062         //      <span class='roo-tpl-{name}'></span> ?????
12063         
12064         
12065         
12066         this.clearSelections();
12067         this.el.update("");
12068         var html = [];
12069         var records = this.store.getRange();
12070         if(records.length < 1) {
12071             
12072             // is this valid??  = should it render a template??
12073             
12074             this.el.update(this.emptyText);
12075             return;
12076         }
12077         var el = this.el;
12078         if (this.dataName) {
12079             this.el.update(t.apply(this.store.meta)); //????
12080             el = this.el.child('.roo-tpl-' + this.dataName);
12081         }
12082         
12083         for(var i = 0, len = records.length; i < len; i++){
12084             var data = this.prepareData(records[i].data, i, records[i]);
12085             this.fireEvent("preparedata", this, data, i, records[i]);
12086             
12087             var d = Roo.apply({}, data);
12088             
12089             if(this.tickable){
12090                 Roo.apply(d, {'roo-id' : Roo.id()});
12091                 
12092                 var _this = this;
12093             
12094                 Roo.each(this.parent.item, function(item){
12095                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
12096                         return;
12097                     }
12098                     Roo.apply(d, {'roo-data-checked' : 'checked'});
12099                 });
12100             }
12101             
12102             html[html.length] = Roo.util.Format.trim(
12103                 this.dataName ?
12104                     t.applySubtemplate(this.dataName, d, this.store.meta) :
12105                     t.apply(d)
12106             );
12107         }
12108         
12109         
12110         
12111         el.update(html.join(""));
12112         this.nodes = el.dom.childNodes;
12113         this.updateIndexes(0);
12114     },
12115     
12116
12117     /**
12118      * Function to override to reformat the data that is sent to
12119      * the template for each node.
12120      * DEPRICATED - use the preparedata event handler.
12121      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
12122      * a JSON object for an UpdateManager bound view).
12123      */
12124     prepareData : function(data, index, record)
12125     {
12126         this.fireEvent("preparedata", this, data, index, record);
12127         return data;
12128     },
12129
12130     onUpdate : function(ds, record){
12131          Roo.log('on update');   
12132         this.clearSelections();
12133         var index = this.store.indexOf(record);
12134         var n = this.nodes[index];
12135         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
12136         n.parentNode.removeChild(n);
12137         this.updateIndexes(index, index);
12138     },
12139
12140     
12141     
12142 // --------- FIXME     
12143     onAdd : function(ds, records, index)
12144     {
12145         Roo.log(['on Add', ds, records, index] );        
12146         this.clearSelections();
12147         if(this.nodes.length == 0){
12148             this.refresh();
12149             return;
12150         }
12151         var n = this.nodes[index];
12152         for(var i = 0, len = records.length; i < len; i++){
12153             var d = this.prepareData(records[i].data, i, records[i]);
12154             if(n){
12155                 this.tpl.insertBefore(n, d);
12156             }else{
12157                 
12158                 this.tpl.append(this.el, d);
12159             }
12160         }
12161         this.updateIndexes(index);
12162     },
12163
12164     onRemove : function(ds, record, index){
12165         Roo.log('onRemove');
12166         this.clearSelections();
12167         var el = this.dataName  ?
12168             this.el.child('.roo-tpl-' + this.dataName) :
12169             this.el; 
12170         
12171         el.dom.removeChild(this.nodes[index]);
12172         this.updateIndexes(index);
12173     },
12174
12175     /**
12176      * Refresh an individual node.
12177      * @param {Number} index
12178      */
12179     refreshNode : function(index){
12180         this.onUpdate(this.store, this.store.getAt(index));
12181     },
12182
12183     updateIndexes : function(startIndex, endIndex){
12184         var ns = this.nodes;
12185         startIndex = startIndex || 0;
12186         endIndex = endIndex || ns.length - 1;
12187         for(var i = startIndex; i <= endIndex; i++){
12188             ns[i].nodeIndex = i;
12189         }
12190     },
12191
12192     /**
12193      * Changes the data store this view uses and refresh the view.
12194      * @param {Store} store
12195      */
12196     setStore : function(store, initial){
12197         if(!initial && this.store){
12198             this.store.un("datachanged", this.refresh);
12199             this.store.un("add", this.onAdd);
12200             this.store.un("remove", this.onRemove);
12201             this.store.un("update", this.onUpdate);
12202             this.store.un("clear", this.refresh);
12203             this.store.un("beforeload", this.onBeforeLoad);
12204             this.store.un("load", this.onLoad);
12205             this.store.un("loadexception", this.onLoad);
12206         }
12207         if(store){
12208           
12209             store.on("datachanged", this.refresh, this);
12210             store.on("add", this.onAdd, this);
12211             store.on("remove", this.onRemove, this);
12212             store.on("update", this.onUpdate, this);
12213             store.on("clear", this.refresh, this);
12214             store.on("beforeload", this.onBeforeLoad, this);
12215             store.on("load", this.onLoad, this);
12216             store.on("loadexception", this.onLoad, this);
12217         }
12218         
12219         if(store){
12220             this.refresh();
12221         }
12222     },
12223     /**
12224      * onbeforeLoad - masks the loading area.
12225      *
12226      */
12227     onBeforeLoad : function(store,opts)
12228     {
12229          Roo.log('onBeforeLoad');   
12230         if (!opts.add) {
12231             this.el.update("");
12232         }
12233         this.el.mask(this.mask ? this.mask : "Loading" ); 
12234     },
12235     onLoad : function ()
12236     {
12237         this.el.unmask();
12238     },
12239     
12240
12241     /**
12242      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
12243      * @param {HTMLElement} node
12244      * @return {HTMLElement} The template node
12245      */
12246     findItemFromChild : function(node){
12247         var el = this.dataName  ?
12248             this.el.child('.roo-tpl-' + this.dataName,true) :
12249             this.el.dom; 
12250         
12251         if(!node || node.parentNode == el){
12252                     return node;
12253             }
12254             var p = node.parentNode;
12255             while(p && p != el){
12256             if(p.parentNode == el){
12257                 return p;
12258             }
12259             p = p.parentNode;
12260         }
12261             return null;
12262     },
12263
12264     /** @ignore */
12265     onClick : function(e){
12266         var item = this.findItemFromChild(e.getTarget());
12267         if(item){
12268             var index = this.indexOf(item);
12269             if(this.onItemClick(item, index, e) !== false){
12270                 this.fireEvent("click", this, index, item, e);
12271             }
12272         }else{
12273             this.clearSelections();
12274         }
12275     },
12276
12277     /** @ignore */
12278     onContextMenu : function(e){
12279         var item = this.findItemFromChild(e.getTarget());
12280         if(item){
12281             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
12282         }
12283     },
12284
12285     /** @ignore */
12286     onDblClick : function(e){
12287         var item = this.findItemFromChild(e.getTarget());
12288         if(item){
12289             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
12290         }
12291     },
12292
12293     onItemClick : function(item, index, e)
12294     {
12295         if(this.fireEvent("beforeclick", this, index, item, e) === false){
12296             return false;
12297         }
12298         if (this.toggleSelect) {
12299             var m = this.isSelected(item) ? 'unselect' : 'select';
12300             Roo.log(m);
12301             var _t = this;
12302             _t[m](item, true, false);
12303             return true;
12304         }
12305         if(this.multiSelect || this.singleSelect){
12306             if(this.multiSelect && e.shiftKey && this.lastSelection){
12307                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
12308             }else{
12309                 this.select(item, this.multiSelect && e.ctrlKey);
12310                 this.lastSelection = item;
12311             }
12312             
12313             if(!this.tickable){
12314                 e.preventDefault();
12315             }
12316             
12317         }
12318         return true;
12319     },
12320
12321     /**
12322      * Get the number of selected nodes.
12323      * @return {Number}
12324      */
12325     getSelectionCount : function(){
12326         return this.selections.length;
12327     },
12328
12329     /**
12330      * Get the currently selected nodes.
12331      * @return {Array} An array of HTMLElements
12332      */
12333     getSelectedNodes : function(){
12334         return this.selections;
12335     },
12336
12337     /**
12338      * Get the indexes of the selected nodes.
12339      * @return {Array}
12340      */
12341     getSelectedIndexes : function(){
12342         var indexes = [], s = this.selections;
12343         for(var i = 0, len = s.length; i < len; i++){
12344             indexes.push(s[i].nodeIndex);
12345         }
12346         return indexes;
12347     },
12348
12349     /**
12350      * Clear all selections
12351      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
12352      */
12353     clearSelections : function(suppressEvent){
12354         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
12355             this.cmp.elements = this.selections;
12356             this.cmp.removeClass(this.selectedClass);
12357             this.selections = [];
12358             if(!suppressEvent){
12359                 this.fireEvent("selectionchange", this, this.selections);
12360             }
12361         }
12362     },
12363
12364     /**
12365      * Returns true if the passed node is selected
12366      * @param {HTMLElement/Number} node The node or node index
12367      * @return {Boolean}
12368      */
12369     isSelected : function(node){
12370         var s = this.selections;
12371         if(s.length < 1){
12372             return false;
12373         }
12374         node = this.getNode(node);
12375         return s.indexOf(node) !== -1;
12376     },
12377
12378     /**
12379      * Selects nodes.
12380      * @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
12381      * @param {Boolean} keepExisting (optional) true to keep existing selections
12382      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12383      */
12384     select : function(nodeInfo, keepExisting, suppressEvent){
12385         if(nodeInfo instanceof Array){
12386             if(!keepExisting){
12387                 this.clearSelections(true);
12388             }
12389             for(var i = 0, len = nodeInfo.length; i < len; i++){
12390                 this.select(nodeInfo[i], true, true);
12391             }
12392             return;
12393         } 
12394         var node = this.getNode(nodeInfo);
12395         if(!node || this.isSelected(node)){
12396             return; // already selected.
12397         }
12398         if(!keepExisting){
12399             this.clearSelections(true);
12400         }
12401         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
12402             Roo.fly(node).addClass(this.selectedClass);
12403             this.selections.push(node);
12404             if(!suppressEvent){
12405                 this.fireEvent("selectionchange", this, this.selections);
12406             }
12407         }
12408         
12409         
12410     },
12411       /**
12412      * Unselects nodes.
12413      * @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
12414      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
12415      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12416      */
12417     unselect : function(nodeInfo, keepExisting, suppressEvent)
12418     {
12419         if(nodeInfo instanceof Array){
12420             Roo.each(this.selections, function(s) {
12421                 this.unselect(s, nodeInfo);
12422             }, this);
12423             return;
12424         }
12425         var node = this.getNode(nodeInfo);
12426         if(!node || !this.isSelected(node)){
12427             Roo.log("not selected");
12428             return; // not selected.
12429         }
12430         // fireevent???
12431         var ns = [];
12432         Roo.each(this.selections, function(s) {
12433             if (s == node ) {
12434                 Roo.fly(node).removeClass(this.selectedClass);
12435
12436                 return;
12437             }
12438             ns.push(s);
12439         },this);
12440         
12441         this.selections= ns;
12442         this.fireEvent("selectionchange", this, this.selections);
12443     },
12444
12445     /**
12446      * Gets a template node.
12447      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12448      * @return {HTMLElement} The node or null if it wasn't found
12449      */
12450     getNode : function(nodeInfo){
12451         if(typeof nodeInfo == "string"){
12452             return document.getElementById(nodeInfo);
12453         }else if(typeof nodeInfo == "number"){
12454             return this.nodes[nodeInfo];
12455         }
12456         return nodeInfo;
12457     },
12458
12459     /**
12460      * Gets a range template nodes.
12461      * @param {Number} startIndex
12462      * @param {Number} endIndex
12463      * @return {Array} An array of nodes
12464      */
12465     getNodes : function(start, end){
12466         var ns = this.nodes;
12467         start = start || 0;
12468         end = typeof end == "undefined" ? ns.length - 1 : end;
12469         var nodes = [];
12470         if(start <= end){
12471             for(var i = start; i <= end; i++){
12472                 nodes.push(ns[i]);
12473             }
12474         } else{
12475             for(var i = start; i >= end; i--){
12476                 nodes.push(ns[i]);
12477             }
12478         }
12479         return nodes;
12480     },
12481
12482     /**
12483      * Finds the index of the passed node
12484      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12485      * @return {Number} The index of the node or -1
12486      */
12487     indexOf : function(node){
12488         node = this.getNode(node);
12489         if(typeof node.nodeIndex == "number"){
12490             return node.nodeIndex;
12491         }
12492         var ns = this.nodes;
12493         for(var i = 0, len = ns.length; i < len; i++){
12494             if(ns[i] == node){
12495                 return i;
12496             }
12497         }
12498         return -1;
12499     }
12500 });
12501 /*
12502  * - LGPL
12503  *
12504  * based on jquery fullcalendar
12505  * 
12506  */
12507
12508 Roo.bootstrap = Roo.bootstrap || {};
12509 /**
12510  * @class Roo.bootstrap.Calendar
12511  * @extends Roo.bootstrap.Component
12512  * Bootstrap Calendar class
12513  * @cfg {Boolean} loadMask (true|false) default false
12514  * @cfg {Object} header generate the user specific header of the calendar, default false
12515
12516  * @constructor
12517  * Create a new Container
12518  * @param {Object} config The config object
12519  */
12520
12521
12522
12523 Roo.bootstrap.Calendar = function(config){
12524     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
12525      this.addEvents({
12526         /**
12527              * @event select
12528              * Fires when a date is selected
12529              * @param {DatePicker} this
12530              * @param {Date} date The selected date
12531              */
12532         'select': true,
12533         /**
12534              * @event monthchange
12535              * Fires when the displayed month changes 
12536              * @param {DatePicker} this
12537              * @param {Date} date The selected month
12538              */
12539         'monthchange': true,
12540         /**
12541              * @event evententer
12542              * Fires when mouse over an event
12543              * @param {Calendar} this
12544              * @param {event} Event
12545              */
12546         'evententer': true,
12547         /**
12548              * @event eventleave
12549              * Fires when the mouse leaves an
12550              * @param {Calendar} this
12551              * @param {event}
12552              */
12553         'eventleave': true,
12554         /**
12555              * @event eventclick
12556              * Fires when the mouse click an
12557              * @param {Calendar} this
12558              * @param {event}
12559              */
12560         'eventclick': true
12561         
12562     });
12563
12564 };
12565
12566 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
12567     
12568      /**
12569      * @cfg {Number} startDay
12570      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
12571      */
12572     startDay : 0,
12573     
12574     loadMask : false,
12575     
12576     header : false,
12577       
12578     getAutoCreate : function(){
12579         
12580         
12581         var fc_button = function(name, corner, style, content ) {
12582             return Roo.apply({},{
12583                 tag : 'span',
12584                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
12585                          (corner.length ?
12586                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
12587                             ''
12588                         ),
12589                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
12590                 unselectable: 'on'
12591             });
12592         };
12593         
12594         var header = {};
12595         
12596         if(!this.header){
12597             header = {
12598                 tag : 'table',
12599                 cls : 'fc-header',
12600                 style : 'width:100%',
12601                 cn : [
12602                     {
12603                         tag: 'tr',
12604                         cn : [
12605                             {
12606                                 tag : 'td',
12607                                 cls : 'fc-header-left',
12608                                 cn : [
12609                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
12610                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
12611                                     { tag: 'span', cls: 'fc-header-space' },
12612                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
12613
12614
12615                                 ]
12616                             },
12617
12618                             {
12619                                 tag : 'td',
12620                                 cls : 'fc-header-center',
12621                                 cn : [
12622                                     {
12623                                         tag: 'span',
12624                                         cls: 'fc-header-title',
12625                                         cn : {
12626                                             tag: 'H2',
12627                                             html : 'month / year'
12628                                         }
12629                                     }
12630
12631                                 ]
12632                             },
12633                             {
12634                                 tag : 'td',
12635                                 cls : 'fc-header-right',
12636                                 cn : [
12637                               /*      fc_button('month', 'left', '', 'month' ),
12638                                     fc_button('week', '', '', 'week' ),
12639                                     fc_button('day', 'right', '', 'day' )
12640                                 */    
12641
12642                                 ]
12643                             }
12644
12645                         ]
12646                     }
12647                 ]
12648             };
12649         }
12650         
12651         header = this.header;
12652         
12653        
12654         var cal_heads = function() {
12655             var ret = [];
12656             // fixme - handle this.
12657             
12658             for (var i =0; i < Date.dayNames.length; i++) {
12659                 var d = Date.dayNames[i];
12660                 ret.push({
12661                     tag: 'th',
12662                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
12663                     html : d.substring(0,3)
12664                 });
12665                 
12666             }
12667             ret[0].cls += ' fc-first';
12668             ret[6].cls += ' fc-last';
12669             return ret;
12670         };
12671         var cal_cell = function(n) {
12672             return  {
12673                 tag: 'td',
12674                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
12675                 cn : [
12676                     {
12677                         cn : [
12678                             {
12679                                 cls: 'fc-day-number',
12680                                 html: 'D'
12681                             },
12682                             {
12683                                 cls: 'fc-day-content',
12684                              
12685                                 cn : [
12686                                      {
12687                                         style: 'position: relative;' // height: 17px;
12688                                     }
12689                                 ]
12690                             }
12691                             
12692                             
12693                         ]
12694                     }
12695                 ]
12696                 
12697             }
12698         };
12699         var cal_rows = function() {
12700             
12701             var ret = []
12702             for (var r = 0; r < 6; r++) {
12703                 var row= {
12704                     tag : 'tr',
12705                     cls : 'fc-week',
12706                     cn : []
12707                 };
12708                 
12709                 for (var i =0; i < Date.dayNames.length; i++) {
12710                     var d = Date.dayNames[i];
12711                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
12712
12713                 }
12714                 row.cn[0].cls+=' fc-first';
12715                 row.cn[0].cn[0].style = 'min-height:90px';
12716                 row.cn[6].cls+=' fc-last';
12717                 ret.push(row);
12718                 
12719             }
12720             ret[0].cls += ' fc-first';
12721             ret[4].cls += ' fc-prev-last';
12722             ret[5].cls += ' fc-last';
12723             return ret;
12724             
12725         };
12726         
12727         var cal_table = {
12728             tag: 'table',
12729             cls: 'fc-border-separate',
12730             style : 'width:100%',
12731             cellspacing  : 0,
12732             cn : [
12733                 { 
12734                     tag: 'thead',
12735                     cn : [
12736                         { 
12737                             tag: 'tr',
12738                             cls : 'fc-first fc-last',
12739                             cn : cal_heads()
12740                         }
12741                     ]
12742                 },
12743                 { 
12744                     tag: 'tbody',
12745                     cn : cal_rows()
12746                 }
12747                   
12748             ]
12749         };
12750          
12751          var cfg = {
12752             cls : 'fc fc-ltr',
12753             cn : [
12754                 header,
12755                 {
12756                     cls : 'fc-content',
12757                     style : "position: relative;",
12758                     cn : [
12759                         {
12760                             cls : 'fc-view fc-view-month fc-grid',
12761                             style : 'position: relative',
12762                             unselectable : 'on',
12763                             cn : [
12764                                 {
12765                                     cls : 'fc-event-container',
12766                                     style : 'position:absolute;z-index:8;top:0;left:0;'
12767                                 },
12768                                 cal_table
12769                             ]
12770                         }
12771                     ]
12772     
12773                 }
12774            ] 
12775             
12776         };
12777         
12778          
12779         
12780         return cfg;
12781     },
12782     
12783     
12784     initEvents : function()
12785     {
12786         if(!this.store){
12787             throw "can not find store for calendar";
12788         }
12789         
12790         var mark = {
12791             tag: "div",
12792             cls:"x-dlg-mask",
12793             style: "text-align:center",
12794             cn: [
12795                 {
12796                     tag: "div",
12797                     style: "background-color:white;width:50%;margin:250 auto",
12798                     cn: [
12799                         {
12800                             tag: "img",
12801                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
12802                         },
12803                         {
12804                             tag: "span",
12805                             html: "Loading"
12806                         }
12807                         
12808                     ]
12809                 }
12810             ]
12811         }
12812         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
12813         
12814         var size = this.el.select('.fc-content', true).first().getSize();
12815         this.maskEl.setSize(size.width, size.height);
12816         this.maskEl.enableDisplayMode("block");
12817         if(!this.loadMask){
12818             this.maskEl.hide();
12819         }
12820         
12821         this.store = Roo.factory(this.store, Roo.data);
12822         this.store.on('load', this.onLoad, this);
12823         this.store.on('beforeload', this.onBeforeLoad, this);
12824         
12825         this.resize();
12826         
12827         this.cells = this.el.select('.fc-day',true);
12828         //Roo.log(this.cells);
12829         this.textNodes = this.el.query('.fc-day-number');
12830         this.cells.addClassOnOver('fc-state-hover');
12831         
12832         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
12833         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
12834         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
12835         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
12836         
12837         this.on('monthchange', this.onMonthChange, this);
12838         
12839         this.update(new Date().clearTime());
12840     },
12841     
12842     resize : function() {
12843         var sz  = this.el.getSize();
12844         
12845         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
12846         this.el.select('.fc-day-content div',true).setHeight(34);
12847     },
12848     
12849     
12850     // private
12851     showPrevMonth : function(e){
12852         this.update(this.activeDate.add("mo", -1));
12853     },
12854     showToday : function(e){
12855         this.update(new Date().clearTime());
12856     },
12857     // private
12858     showNextMonth : function(e){
12859         this.update(this.activeDate.add("mo", 1));
12860     },
12861
12862     // private
12863     showPrevYear : function(){
12864         this.update(this.activeDate.add("y", -1));
12865     },
12866
12867     // private
12868     showNextYear : function(){
12869         this.update(this.activeDate.add("y", 1));
12870     },
12871
12872     
12873    // private
12874     update : function(date)
12875     {
12876         var vd = this.activeDate;
12877         this.activeDate = date;
12878 //        if(vd && this.el){
12879 //            var t = date.getTime();
12880 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
12881 //                Roo.log('using add remove');
12882 //                
12883 //                this.fireEvent('monthchange', this, date);
12884 //                
12885 //                this.cells.removeClass("fc-state-highlight");
12886 //                this.cells.each(function(c){
12887 //                   if(c.dateValue == t){
12888 //                       c.addClass("fc-state-highlight");
12889 //                       setTimeout(function(){
12890 //                            try{c.dom.firstChild.focus();}catch(e){}
12891 //                       }, 50);
12892 //                       return false;
12893 //                   }
12894 //                   return true;
12895 //                });
12896 //                return;
12897 //            }
12898 //        }
12899         
12900         var days = date.getDaysInMonth();
12901         
12902         var firstOfMonth = date.getFirstDateOfMonth();
12903         var startingPos = firstOfMonth.getDay()-this.startDay;
12904         
12905         if(startingPos < this.startDay){
12906             startingPos += 7;
12907         }
12908         
12909         var pm = date.add(Date.MONTH, -1);
12910         var prevStart = pm.getDaysInMonth()-startingPos;
12911 //        
12912         this.cells = this.el.select('.fc-day',true);
12913         this.textNodes = this.el.query('.fc-day-number');
12914         this.cells.addClassOnOver('fc-state-hover');
12915         
12916         var cells = this.cells.elements;
12917         var textEls = this.textNodes;
12918         
12919         Roo.each(cells, function(cell){
12920             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
12921         });
12922         
12923         days += startingPos;
12924
12925         // convert everything to numbers so it's fast
12926         var day = 86400000;
12927         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
12928         //Roo.log(d);
12929         //Roo.log(pm);
12930         //Roo.log(prevStart);
12931         
12932         var today = new Date().clearTime().getTime();
12933         var sel = date.clearTime().getTime();
12934         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
12935         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
12936         var ddMatch = this.disabledDatesRE;
12937         var ddText = this.disabledDatesText;
12938         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
12939         var ddaysText = this.disabledDaysText;
12940         var format = this.format;
12941         
12942         var setCellClass = function(cal, cell){
12943             cell.row = 0;
12944             cell.events = [];
12945             cell.more = [];
12946             //Roo.log('set Cell Class');
12947             cell.title = "";
12948             var t = d.getTime();
12949             
12950             //Roo.log(d);
12951             
12952             cell.dateValue = t;
12953             if(t == today){
12954                 cell.className += " fc-today";
12955                 cell.className += " fc-state-highlight";
12956                 cell.title = cal.todayText;
12957             }
12958             if(t == sel){
12959                 // disable highlight in other month..
12960                 //cell.className += " fc-state-highlight";
12961                 
12962             }
12963             // disabling
12964             if(t < min) {
12965                 cell.className = " fc-state-disabled";
12966                 cell.title = cal.minText;
12967                 return;
12968             }
12969             if(t > max) {
12970                 cell.className = " fc-state-disabled";
12971                 cell.title = cal.maxText;
12972                 return;
12973             }
12974             if(ddays){
12975                 if(ddays.indexOf(d.getDay()) != -1){
12976                     cell.title = ddaysText;
12977                     cell.className = " fc-state-disabled";
12978                 }
12979             }
12980             if(ddMatch && format){
12981                 var fvalue = d.dateFormat(format);
12982                 if(ddMatch.test(fvalue)){
12983                     cell.title = ddText.replace("%0", fvalue);
12984                     cell.className = " fc-state-disabled";
12985                 }
12986             }
12987             
12988             if (!cell.initialClassName) {
12989                 cell.initialClassName = cell.dom.className;
12990             }
12991             
12992             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
12993         };
12994
12995         var i = 0;
12996         
12997         for(; i < startingPos; i++) {
12998             textEls[i].innerHTML = (++prevStart);
12999             d.setDate(d.getDate()+1);
13000             
13001             cells[i].className = "fc-past fc-other-month";
13002             setCellClass(this, cells[i]);
13003         }
13004         
13005         var intDay = 0;
13006         
13007         for(; i < days; i++){
13008             intDay = i - startingPos + 1;
13009             textEls[i].innerHTML = (intDay);
13010             d.setDate(d.getDate()+1);
13011             
13012             cells[i].className = ''; // "x-date-active";
13013             setCellClass(this, cells[i]);
13014         }
13015         var extraDays = 0;
13016         
13017         for(; i < 42; i++) {
13018             textEls[i].innerHTML = (++extraDays);
13019             d.setDate(d.getDate()+1);
13020             
13021             cells[i].className = "fc-future fc-other-month";
13022             setCellClass(this, cells[i]);
13023         }
13024         
13025         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
13026         
13027         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
13028         
13029         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
13030         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
13031         
13032         if(totalRows != 6){
13033             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
13034             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
13035         }
13036         
13037         this.fireEvent('monthchange', this, date);
13038         
13039         
13040         /*
13041         if(!this.internalRender){
13042             var main = this.el.dom.firstChild;
13043             var w = main.offsetWidth;
13044             this.el.setWidth(w + this.el.getBorderWidth("lr"));
13045             Roo.fly(main).setWidth(w);
13046             this.internalRender = true;
13047             // opera does not respect the auto grow header center column
13048             // then, after it gets a width opera refuses to recalculate
13049             // without a second pass
13050             if(Roo.isOpera && !this.secondPass){
13051                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
13052                 this.secondPass = true;
13053                 this.update.defer(10, this, [date]);
13054             }
13055         }
13056         */
13057         
13058     },
13059     
13060     findCell : function(dt) {
13061         dt = dt.clearTime().getTime();
13062         var ret = false;
13063         this.cells.each(function(c){
13064             //Roo.log("check " +c.dateValue + '?=' + dt);
13065             if(c.dateValue == dt){
13066                 ret = c;
13067                 return false;
13068             }
13069             return true;
13070         });
13071         
13072         return ret;
13073     },
13074     
13075     findCells : function(ev) {
13076         var s = ev.start.clone().clearTime().getTime();
13077        // Roo.log(s);
13078         var e= ev.end.clone().clearTime().getTime();
13079        // Roo.log(e);
13080         var ret = [];
13081         this.cells.each(function(c){
13082              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
13083             
13084             if(c.dateValue > e){
13085                 return ;
13086             }
13087             if(c.dateValue < s){
13088                 return ;
13089             }
13090             ret.push(c);
13091         });
13092         
13093         return ret;    
13094     },
13095     
13096 //    findBestRow: function(cells)
13097 //    {
13098 //        var ret = 0;
13099 //        
13100 //        for (var i =0 ; i < cells.length;i++) {
13101 //            ret  = Math.max(cells[i].rows || 0,ret);
13102 //        }
13103 //        return ret;
13104 //        
13105 //    },
13106     
13107     
13108     addItem : function(ev)
13109     {
13110         // look for vertical location slot in
13111         var cells = this.findCells(ev);
13112         
13113 //        ev.row = this.findBestRow(cells);
13114         
13115         // work out the location.
13116         
13117         var crow = false;
13118         var rows = [];
13119         for(var i =0; i < cells.length; i++) {
13120             
13121             cells[i].row = cells[0].row;
13122             
13123             if(i == 0){
13124                 cells[i].row = cells[i].row + 1;
13125             }
13126             
13127             if (!crow) {
13128                 crow = {
13129                     start : cells[i],
13130                     end :  cells[i]
13131                 };
13132                 continue;
13133             }
13134             if (crow.start.getY() == cells[i].getY()) {
13135                 // on same row.
13136                 crow.end = cells[i];
13137                 continue;
13138             }
13139             // different row.
13140             rows.push(crow);
13141             crow = {
13142                 start: cells[i],
13143                 end : cells[i]
13144             };
13145             
13146         }
13147         
13148         rows.push(crow);
13149         ev.els = [];
13150         ev.rows = rows;
13151         ev.cells = cells;
13152         
13153         cells[0].events.push(ev);
13154         
13155         this.calevents.push(ev);
13156     },
13157     
13158     clearEvents: function() {
13159         
13160         if(!this.calevents){
13161             return;
13162         }
13163         
13164         Roo.each(this.cells.elements, function(c){
13165             c.row = 0;
13166             c.events = [];
13167             c.more = [];
13168         });
13169         
13170         Roo.each(this.calevents, function(e) {
13171             Roo.each(e.els, function(el) {
13172                 el.un('mouseenter' ,this.onEventEnter, this);
13173                 el.un('mouseleave' ,this.onEventLeave, this);
13174                 el.remove();
13175             },this);
13176         },this);
13177         
13178         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
13179             e.remove();
13180         });
13181         
13182     },
13183     
13184     renderEvents: function()
13185     {   
13186         var _this = this;
13187         
13188         this.cells.each(function(c) {
13189             
13190             if(c.row < 5){
13191                 return;
13192             }
13193             
13194             var ev = c.events;
13195             
13196             var r = 4;
13197             if(c.row != c.events.length){
13198                 r = 4 - (4 - (c.row - c.events.length));
13199             }
13200             
13201             c.events = ev.slice(0, r);
13202             c.more = ev.slice(r);
13203             
13204             if(c.more.length && c.more.length == 1){
13205                 c.events.push(c.more.pop());
13206             }
13207             
13208             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
13209             
13210         });
13211             
13212         this.cells.each(function(c) {
13213             
13214             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
13215             
13216             
13217             for (var e = 0; e < c.events.length; e++){
13218                 var ev = c.events[e];
13219                 var rows = ev.rows;
13220                 
13221                 for(var i = 0; i < rows.length; i++) {
13222                 
13223                     // how many rows should it span..
13224
13225                     var  cfg = {
13226                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
13227                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
13228
13229                         unselectable : "on",
13230                         cn : [
13231                             {
13232                                 cls: 'fc-event-inner',
13233                                 cn : [
13234     //                                {
13235     //                                  tag:'span',
13236     //                                  cls: 'fc-event-time',
13237     //                                  html : cells.length > 1 ? '' : ev.time
13238     //                                },
13239                                     {
13240                                       tag:'span',
13241                                       cls: 'fc-event-title',
13242                                       html : String.format('{0}', ev.title)
13243                                     }
13244
13245
13246                                 ]
13247                             },
13248                             {
13249                                 cls: 'ui-resizable-handle ui-resizable-e',
13250                                 html : '&nbsp;&nbsp;&nbsp'
13251                             }
13252
13253                         ]
13254                     };
13255
13256                     if (i == 0) {
13257                         cfg.cls += ' fc-event-start';
13258                     }
13259                     if ((i+1) == rows.length) {
13260                         cfg.cls += ' fc-event-end';
13261                     }
13262
13263                     var ctr = _this.el.select('.fc-event-container',true).first();
13264                     var cg = ctr.createChild(cfg);
13265
13266                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
13267                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
13268
13269                     var r = (c.more.length) ? 1 : 0;
13270                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
13271                     cg.setWidth(ebox.right - sbox.x -2);
13272
13273                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
13274                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
13275                     cg.on('click', _this.onEventClick, _this, ev);
13276
13277                     ev.els.push(cg);
13278                     
13279                 }
13280                 
13281             }
13282             
13283             
13284             if(c.more.length){
13285                 var  cfg = {
13286                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
13287                     style : 'position: absolute',
13288                     unselectable : "on",
13289                     cn : [
13290                         {
13291                             cls: 'fc-event-inner',
13292                             cn : [
13293                                 {
13294                                   tag:'span',
13295                                   cls: 'fc-event-title',
13296                                   html : 'More'
13297                                 }
13298
13299
13300                             ]
13301                         },
13302                         {
13303                             cls: 'ui-resizable-handle ui-resizable-e',
13304                             html : '&nbsp;&nbsp;&nbsp'
13305                         }
13306
13307                     ]
13308                 };
13309
13310                 var ctr = _this.el.select('.fc-event-container',true).first();
13311                 var cg = ctr.createChild(cfg);
13312
13313                 var sbox = c.select('.fc-day-content',true).first().getBox();
13314                 var ebox = c.select('.fc-day-content',true).first().getBox();
13315                 //Roo.log(cg);
13316                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
13317                 cg.setWidth(ebox.right - sbox.x -2);
13318
13319                 cg.on('click', _this.onMoreEventClick, _this, c.more);
13320                 
13321             }
13322             
13323         });
13324         
13325         
13326         
13327     },
13328     
13329     onEventEnter: function (e, el,event,d) {
13330         this.fireEvent('evententer', this, el, event);
13331     },
13332     
13333     onEventLeave: function (e, el,event,d) {
13334         this.fireEvent('eventleave', this, el, event);
13335     },
13336     
13337     onEventClick: function (e, el,event,d) {
13338         this.fireEvent('eventclick', this, el, event);
13339     },
13340     
13341     onMonthChange: function () {
13342         this.store.load();
13343     },
13344     
13345     onMoreEventClick: function(e, el, more)
13346     {
13347         var _this = this;
13348         
13349         this.calpopover.placement = 'right';
13350         this.calpopover.setTitle('More');
13351         
13352         this.calpopover.setContent('');
13353         
13354         var ctr = this.calpopover.el.select('.popover-content', true).first();
13355         
13356         Roo.each(more, function(m){
13357             var cfg = {
13358                 cls : 'fc-event-hori fc-event-draggable',
13359                 html : m.title
13360             }
13361             var cg = ctr.createChild(cfg);
13362             
13363             cg.on('click', _this.onEventClick, _this, m);
13364         });
13365         
13366         this.calpopover.show(el);
13367         
13368         
13369     },
13370     
13371     onLoad: function () 
13372     {   
13373         this.calevents = [];
13374         var cal = this;
13375         
13376         if(this.store.getCount() > 0){
13377             this.store.data.each(function(d){
13378                cal.addItem({
13379                     id : d.data.id,
13380                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
13381                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
13382                     time : d.data.start_time,
13383                     title : d.data.title,
13384                     description : d.data.description,
13385                     venue : d.data.venue
13386                 });
13387             });
13388         }
13389         
13390         this.renderEvents();
13391         
13392         if(this.calevents.length && this.loadMask){
13393             this.maskEl.hide();
13394         }
13395     },
13396     
13397     onBeforeLoad: function()
13398     {
13399         this.clearEvents();
13400         if(this.loadMask){
13401             this.maskEl.show();
13402         }
13403     }
13404 });
13405
13406  
13407  /*
13408  * - LGPL
13409  *
13410  * element
13411  * 
13412  */
13413
13414 /**
13415  * @class Roo.bootstrap.Popover
13416  * @extends Roo.bootstrap.Component
13417  * Bootstrap Popover class
13418  * @cfg {String} html contents of the popover   (or false to use children..)
13419  * @cfg {String} title of popover (or false to hide)
13420  * @cfg {String} placement how it is placed
13421  * @cfg {String} trigger click || hover (or false to trigger manually)
13422  * @cfg {String} over what (parent or false to trigger manually.)
13423  * 
13424  * @constructor
13425  * Create a new Popover
13426  * @param {Object} config The config object
13427  */
13428
13429 Roo.bootstrap.Popover = function(config){
13430     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
13431 };
13432
13433 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
13434     
13435     title: 'Fill in a title',
13436     html: false,
13437     
13438     placement : 'right',
13439     trigger : 'hover', // hover
13440     
13441     over: 'parent',
13442     
13443     can_build_overlaid : false,
13444     
13445     getChildContainer : function()
13446     {
13447         return this.el.select('.popover-content',true).first();
13448     },
13449     
13450     getAutoCreate : function(){
13451          Roo.log('make popover?');
13452         var cfg = {
13453            cls : 'popover roo-dynamic',
13454            style: 'display:block',
13455            cn : [
13456                 {
13457                     cls : 'arrow'
13458                 },
13459                 {
13460                     cls : 'popover-inner',
13461                     cn : [
13462                         {
13463                             tag: 'h3',
13464                             cls: 'popover-title',
13465                             html : this.title
13466                         },
13467                         {
13468                             cls : 'popover-content',
13469                             html : this.html
13470                         }
13471                     ]
13472                     
13473                 }
13474            ]
13475         };
13476         
13477         return cfg;
13478     },
13479     setTitle: function(str)
13480     {
13481         this.el.select('.popover-title',true).first().dom.innerHTML = str;
13482     },
13483     setContent: function(str)
13484     {
13485         this.el.select('.popover-content',true).first().dom.innerHTML = str;
13486     },
13487     // as it get's added to the bottom of the page.
13488     onRender : function(ct, position)
13489     {
13490         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
13491         if(!this.el){
13492             var cfg = Roo.apply({},  this.getAutoCreate());
13493             cfg.id = Roo.id();
13494             
13495             if (this.cls) {
13496                 cfg.cls += ' ' + this.cls;
13497             }
13498             if (this.style) {
13499                 cfg.style = this.style;
13500             }
13501             Roo.log("adding to ")
13502             this.el = Roo.get(document.body).createChild(cfg, position);
13503             Roo.log(this.el);
13504         }
13505         this.initEvents();
13506     },
13507     
13508     initEvents : function()
13509     {
13510         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
13511         this.el.enableDisplayMode('block');
13512         this.el.hide();
13513         if (this.over === false) {
13514             return; 
13515         }
13516         if (this.triggers === false) {
13517             return;
13518         }
13519         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13520         var triggers = this.trigger ? this.trigger.split(' ') : [];
13521         Roo.each(triggers, function(trigger) {
13522         
13523             if (trigger == 'click') {
13524                 on_el.on('click', this.toggle, this);
13525             } else if (trigger != 'manual') {
13526                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
13527                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
13528       
13529                 on_el.on(eventIn  ,this.enter, this);
13530                 on_el.on(eventOut, this.leave, this);
13531             }
13532         }, this);
13533         
13534     },
13535     
13536     
13537     // private
13538     timeout : null,
13539     hoverState : null,
13540     
13541     toggle : function () {
13542         this.hoverState == 'in' ? this.leave() : this.enter();
13543     },
13544     
13545     enter : function () {
13546        
13547     
13548         clearTimeout(this.timeout);
13549     
13550         this.hoverState = 'in'
13551     
13552         if (!this.delay || !this.delay.show) {
13553             this.show();
13554             return 
13555         }
13556         var _t = this;
13557         this.timeout = setTimeout(function () {
13558             if (_t.hoverState == 'in') {
13559                 _t.show();
13560             }
13561         }, this.delay.show)
13562     },
13563     leave : function() {
13564         clearTimeout(this.timeout);
13565     
13566         this.hoverState = 'out'
13567     
13568         if (!this.delay || !this.delay.hide) {
13569             this.hide();
13570             return 
13571         }
13572         var _t = this;
13573         this.timeout = setTimeout(function () {
13574             if (_t.hoverState == 'out') {
13575                 _t.hide();
13576             }
13577         }, this.delay.hide)
13578     },
13579     
13580     show : function (on_el)
13581     {
13582         if (!on_el) {
13583             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13584         }
13585         // set content.
13586         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
13587         if (this.html !== false) {
13588             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
13589         }
13590         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
13591         if (!this.title.length) {
13592             this.el.select('.popover-title',true).hide();
13593         }
13594         
13595         var placement = typeof this.placement == 'function' ?
13596             this.placement.call(this, this.el, on_el) :
13597             this.placement;
13598             
13599         var autoToken = /\s?auto?\s?/i;
13600         var autoPlace = autoToken.test(placement);
13601         if (autoPlace) {
13602             placement = placement.replace(autoToken, '') || 'top';
13603         }
13604         
13605         //this.el.detach()
13606         //this.el.setXY([0,0]);
13607         this.el.show();
13608         this.el.dom.style.display='block';
13609         this.el.addClass(placement);
13610         
13611         //this.el.appendTo(on_el);
13612         
13613         var p = this.getPosition();
13614         var box = this.el.getBox();
13615         
13616         if (autoPlace) {
13617             // fixme..
13618         }
13619         var align = Roo.bootstrap.Popover.alignment[placement]
13620         this.el.alignTo(on_el, align[0],align[1]);
13621         //var arrow = this.el.select('.arrow',true).first();
13622         //arrow.set(align[2], 
13623         
13624         this.el.addClass('in');
13625         this.hoverState = null;
13626         
13627         if (this.el.hasClass('fade')) {
13628             // fade it?
13629         }
13630         
13631     },
13632     hide : function()
13633     {
13634         this.el.setXY([0,0]);
13635         this.el.removeClass('in');
13636         this.el.hide();
13637         
13638     }
13639     
13640 });
13641
13642 Roo.bootstrap.Popover.alignment = {
13643     'left' : ['r-l', [-10,0], 'right'],
13644     'right' : ['l-r', [10,0], 'left'],
13645     'bottom' : ['t-b', [0,10], 'top'],
13646     'top' : [ 'b-t', [0,-10], 'bottom']
13647 };
13648
13649  /*
13650  * - LGPL
13651  *
13652  * Progress
13653  * 
13654  */
13655
13656 /**
13657  * @class Roo.bootstrap.Progress
13658  * @extends Roo.bootstrap.Component
13659  * Bootstrap Progress class
13660  * @cfg {Boolean} striped striped of the progress bar
13661  * @cfg {Boolean} active animated of the progress bar
13662  * 
13663  * 
13664  * @constructor
13665  * Create a new Progress
13666  * @param {Object} config The config object
13667  */
13668
13669 Roo.bootstrap.Progress = function(config){
13670     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
13671 };
13672
13673 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
13674     
13675     striped : false,
13676     active: false,
13677     
13678     getAutoCreate : function(){
13679         var cfg = {
13680             tag: 'div',
13681             cls: 'progress'
13682         };
13683         
13684         
13685         if(this.striped){
13686             cfg.cls += ' progress-striped';
13687         }
13688       
13689         if(this.active){
13690             cfg.cls += ' active';
13691         }
13692         
13693         
13694         return cfg;
13695     }
13696    
13697 });
13698
13699  
13700
13701  /*
13702  * - LGPL
13703  *
13704  * ProgressBar
13705  * 
13706  */
13707
13708 /**
13709  * @class Roo.bootstrap.ProgressBar
13710  * @extends Roo.bootstrap.Component
13711  * Bootstrap ProgressBar class
13712  * @cfg {Number} aria_valuenow aria-value now
13713  * @cfg {Number} aria_valuemin aria-value min
13714  * @cfg {Number} aria_valuemax aria-value max
13715  * @cfg {String} label label for the progress bar
13716  * @cfg {String} panel (success | info | warning | danger )
13717  * @cfg {String} role role of the progress bar
13718  * @cfg {String} sr_only text
13719  * 
13720  * 
13721  * @constructor
13722  * Create a new ProgressBar
13723  * @param {Object} config The config object
13724  */
13725
13726 Roo.bootstrap.ProgressBar = function(config){
13727     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
13728 };
13729
13730 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
13731     
13732     aria_valuenow : 0,
13733     aria_valuemin : 0,
13734     aria_valuemax : 100,
13735     label : false,
13736     panel : false,
13737     role : false,
13738     sr_only: false,
13739     
13740     getAutoCreate : function()
13741     {
13742         
13743         var cfg = {
13744             tag: 'div',
13745             cls: 'progress-bar',
13746             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
13747         };
13748         
13749         if(this.sr_only){
13750             cfg.cn = {
13751                 tag: 'span',
13752                 cls: 'sr-only',
13753                 html: this.sr_only
13754             }
13755         }
13756         
13757         if(this.role){
13758             cfg.role = this.role;
13759         }
13760         
13761         if(this.aria_valuenow){
13762             cfg['aria-valuenow'] = this.aria_valuenow;
13763         }
13764         
13765         if(this.aria_valuemin){
13766             cfg['aria-valuemin'] = this.aria_valuemin;
13767         }
13768         
13769         if(this.aria_valuemax){
13770             cfg['aria-valuemax'] = this.aria_valuemax;
13771         }
13772         
13773         if(this.label && !this.sr_only){
13774             cfg.html = this.label;
13775         }
13776         
13777         if(this.panel){
13778             cfg.cls += ' progress-bar-' + this.panel;
13779         }
13780         
13781         return cfg;
13782     },
13783     
13784     update : function(aria_valuenow)
13785     {
13786         this.aria_valuenow = aria_valuenow;
13787         
13788         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
13789     }
13790    
13791 });
13792
13793  
13794
13795  /*
13796  * - LGPL
13797  *
13798  * column
13799  * 
13800  */
13801
13802 /**
13803  * @class Roo.bootstrap.TabGroup
13804  * @extends Roo.bootstrap.Column
13805  * Bootstrap Column class
13806  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
13807  * @cfg {Boolean} carousel true to make the group behave like a carousel
13808  * 
13809  * @constructor
13810  * Create a new TabGroup
13811  * @param {Object} config The config object
13812  */
13813
13814 Roo.bootstrap.TabGroup = function(config){
13815     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
13816     if (!this.navId) {
13817         this.navId = Roo.id();
13818     }
13819     this.tabs = [];
13820     Roo.bootstrap.TabGroup.register(this);
13821     
13822 };
13823
13824 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
13825     
13826     carousel : false,
13827     transition : false,
13828      
13829     getAutoCreate : function()
13830     {
13831         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
13832         
13833         cfg.cls += ' tab-content';
13834         
13835         if (this.carousel) {
13836             cfg.cls += ' carousel slide';
13837             cfg.cn = [{
13838                cls : 'carousel-inner'
13839             }]
13840         }
13841         
13842         
13843         return cfg;
13844     },
13845     getChildContainer : function()
13846     {
13847         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
13848     },
13849     
13850     /**
13851     * register a Navigation item
13852     * @param {Roo.bootstrap.NavItem} the navitem to add
13853     */
13854     register : function(item)
13855     {
13856         this.tabs.push( item);
13857         item.navId = this.navId; // not really needed..
13858     
13859     },
13860     
13861     getActivePanel : function()
13862     {
13863         var r = false;
13864         Roo.each(this.tabs, function(t) {
13865             if (t.active) {
13866                 r = t;
13867                 return false;
13868             }
13869             return null;
13870         });
13871         return r;
13872         
13873     },
13874     getPanelByName : function(n)
13875     {
13876         var r = false;
13877         Roo.each(this.tabs, function(t) {
13878             if (t.tabId == n) {
13879                 r = t;
13880                 return false;
13881             }
13882             return null;
13883         });
13884         return r;
13885     },
13886     indexOfPanel : function(p)
13887     {
13888         var r = false;
13889         Roo.each(this.tabs, function(t,i) {
13890             if (t.tabId == p.tabId) {
13891                 r = i;
13892                 return false;
13893             }
13894             return null;
13895         });
13896         return r;
13897     },
13898     /**
13899      * show a specific panel
13900      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
13901      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
13902      */
13903     showPanel : function (pan)
13904     {
13905         
13906         if (typeof(pan) == 'number') {
13907             pan = this.tabs[pan];
13908         }
13909         if (typeof(pan) == 'string') {
13910             pan = this.getPanelByName(pan);
13911         }
13912         if (pan.tabId == this.getActivePanel().tabId) {
13913             return true;
13914         }
13915         var cur = this.getActivePanel();
13916         
13917         if (false === cur.fireEvent('beforedeactivate')) {
13918             return false;
13919         }
13920         
13921         if (this.carousel) {
13922             this.transition = true;
13923             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
13924             var lr = dir == 'next' ? 'left' : 'right';
13925             pan.el.addClass(dir); // or prev
13926             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
13927             cur.el.addClass(lr); // or right
13928             pan.el.addClass(lr);
13929             
13930             var _this = this;
13931             cur.el.on('transitionend', function() {
13932                 Roo.log("trans end?");
13933                 
13934                 pan.el.removeClass([lr,dir]);
13935                 pan.setActive(true);
13936                 
13937                 cur.el.removeClass([lr]);
13938                 cur.setActive(false);
13939                 
13940                 _this.transition = false;
13941                 
13942             }, this, { single:  true } );
13943             return true;
13944         }
13945         
13946         cur.setActive(false);
13947         pan.setActive(true);
13948         return true;
13949         
13950     },
13951     showPanelNext : function()
13952     {
13953         var i = this.indexOfPanel(this.getActivePanel());
13954         if (i > this.tabs.length) {
13955             return;
13956         }
13957         this.showPanel(this.tabs[i+1]);
13958     },
13959     showPanelPrev : function()
13960     {
13961         var i = this.indexOfPanel(this.getActivePanel());
13962         if (i  < 1) {
13963             return;
13964         }
13965         this.showPanel(this.tabs[i-1]);
13966     }
13967     
13968     
13969   
13970 });
13971
13972  
13973
13974  
13975  
13976 Roo.apply(Roo.bootstrap.TabGroup, {
13977     
13978     groups: {},
13979      /**
13980     * register a Navigation Group
13981     * @param {Roo.bootstrap.NavGroup} the navgroup to add
13982     */
13983     register : function(navgrp)
13984     {
13985         this.groups[navgrp.navId] = navgrp;
13986         
13987     },
13988     /**
13989     * fetch a Navigation Group based on the navigation ID
13990     * if one does not exist , it will get created.
13991     * @param {string} the navgroup to add
13992     * @returns {Roo.bootstrap.NavGroup} the navgroup 
13993     */
13994     get: function(navId) {
13995         if (typeof(this.groups[navId]) == 'undefined') {
13996             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
13997         }
13998         return this.groups[navId] ;
13999     }
14000     
14001     
14002     
14003 });
14004
14005  /*
14006  * - LGPL
14007  *
14008  * TabPanel
14009  * 
14010  */
14011
14012 /**
14013  * @class Roo.bootstrap.TabPanel
14014  * @extends Roo.bootstrap.Component
14015  * Bootstrap TabPanel class
14016  * @cfg {Boolean} active panel active
14017  * @cfg {String} html panel content
14018  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
14019  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
14020  * 
14021  * 
14022  * @constructor
14023  * Create a new TabPanel
14024  * @param {Object} config The config object
14025  */
14026
14027 Roo.bootstrap.TabPanel = function(config){
14028     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
14029     this.addEvents({
14030         /**
14031              * @event changed
14032              * Fires when the active status changes
14033              * @param {Roo.bootstrap.TabPanel} this
14034              * @param {Boolean} state the new state
14035             
14036          */
14037         'changed': true,
14038         /**
14039              * @event beforedeactivate
14040              * Fires before a tab is de-activated - can be used to do validation on a form.
14041              * @param {Roo.bootstrap.TabPanel} this
14042              * @return {Boolean} false if there is an error
14043             
14044          */
14045         'beforedeactivate': true
14046      });
14047     
14048     this.tabId = this.tabId || Roo.id();
14049   
14050 };
14051
14052 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
14053     
14054     active: false,
14055     html: false,
14056     tabId: false,
14057     navId : false,
14058     
14059     getAutoCreate : function(){
14060         var cfg = {
14061             tag: 'div',
14062             // item is needed for carousel - not sure if it has any effect otherwise
14063             cls: 'tab-pane item',
14064             html: this.html || ''
14065         };
14066         
14067         if(this.active){
14068             cfg.cls += ' active';
14069         }
14070         
14071         if(this.tabId){
14072             cfg.tabId = this.tabId;
14073         }
14074         
14075         
14076         return cfg;
14077     },
14078     
14079     initEvents:  function()
14080     {
14081         Roo.log('-------- init events on tab panel ---------');
14082         
14083         var p = this.parent();
14084         this.navId = this.navId || p.navId;
14085         
14086         if (typeof(this.navId) != 'undefined') {
14087             // not really needed.. but just in case.. parent should be a NavGroup.
14088             var tg = Roo.bootstrap.TabGroup.get(this.navId);
14089             Roo.log(['register', tg, this]);
14090             tg.register(this);
14091         }
14092     },
14093     
14094     
14095     onRender : function(ct, position)
14096     {
14097        // Roo.log("Call onRender: " + this.xtype);
14098         
14099         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
14100         
14101         
14102         
14103         
14104         
14105     },
14106     
14107     setActive: function(state)
14108     {
14109         Roo.log("panel - set active " + this.tabId + "=" + state);
14110         
14111         this.active = state;
14112         if (!state) {
14113             this.el.removeClass('active');
14114             
14115         } else  if (!this.el.hasClass('active')) {
14116             this.el.addClass('active');
14117         }
14118         this.fireEvent('changed', this, state);
14119     }
14120     
14121     
14122 });
14123  
14124
14125  
14126
14127  /*
14128  * - LGPL
14129  *
14130  * DateField
14131  * 
14132  */
14133
14134 /**
14135  * @class Roo.bootstrap.DateField
14136  * @extends Roo.bootstrap.Input
14137  * Bootstrap DateField class
14138  * @cfg {Number} weekStart default 0
14139  * @cfg {Number} weekStart default 0
14140  * @cfg {Number} viewMode default empty, (months|years)
14141  * @cfg {Number} minViewMode default empty, (months|years)
14142  * @cfg {Number} startDate default -Infinity
14143  * @cfg {Number} endDate default Infinity
14144  * @cfg {Boolean} todayHighlight default false
14145  * @cfg {Boolean} todayBtn default false
14146  * @cfg {Boolean} calendarWeeks default false
14147  * @cfg {Object} daysOfWeekDisabled default empty
14148  * 
14149  * @cfg {Boolean} keyboardNavigation default true
14150  * @cfg {String} language default en
14151  * 
14152  * @constructor
14153  * Create a new DateField
14154  * @param {Object} config The config object
14155  */
14156
14157 Roo.bootstrap.DateField = function(config){
14158     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
14159      this.addEvents({
14160             /**
14161              * @event show
14162              * Fires when this field show.
14163              * @param {Roo.bootstrap.DateField} this
14164              * @param {Mixed} date The date value
14165              */
14166             show : true,
14167             /**
14168              * @event show
14169              * Fires when this field hide.
14170              * @param {Roo.bootstrap.DateField} this
14171              * @param {Mixed} date The date value
14172              */
14173             hide : true,
14174             /**
14175              * @event select
14176              * Fires when select a date.
14177              * @param {Roo.bootstrap.DateField} this
14178              * @param {Mixed} date The date value
14179              */
14180             select : true
14181         });
14182 };
14183
14184 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
14185     
14186     /**
14187      * @cfg {String} format
14188      * The default date format string which can be overriden for localization support.  The format must be
14189      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
14190      */
14191     format : "m/d/y",
14192     /**
14193      * @cfg {String} altFormats
14194      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
14195      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
14196      */
14197     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
14198     
14199     weekStart : 0,
14200     
14201     viewMode : '',
14202     
14203     minViewMode : '',
14204     
14205     todayHighlight : false,
14206     
14207     todayBtn: false,
14208     
14209     language: 'en',
14210     
14211     keyboardNavigation: true,
14212     
14213     calendarWeeks: false,
14214     
14215     startDate: -Infinity,
14216     
14217     endDate: Infinity,
14218     
14219     daysOfWeekDisabled: [],
14220     
14221     _events: [],
14222     
14223     UTCDate: function()
14224     {
14225         return new Date(Date.UTC.apply(Date, arguments));
14226     },
14227     
14228     UTCToday: function()
14229     {
14230         var today = new Date();
14231         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
14232     },
14233     
14234     getDate: function() {
14235             var d = this.getUTCDate();
14236             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
14237     },
14238     
14239     getUTCDate: function() {
14240             return this.date;
14241     },
14242     
14243     setDate: function(d) {
14244             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
14245     },
14246     
14247     setUTCDate: function(d) {
14248             this.date = d;
14249             this.setValue(this.formatDate(this.date));
14250     },
14251         
14252     onRender: function(ct, position)
14253     {
14254         
14255         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
14256         
14257         this.language = this.language || 'en';
14258         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
14259         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
14260         
14261         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
14262         this.format = this.format || 'm/d/y';
14263         this.isInline = false;
14264         this.isInput = true;
14265         this.component = this.el.select('.add-on', true).first() || false;
14266         this.component = (this.component && this.component.length === 0) ? false : this.component;
14267         this.hasInput = this.component && this.inputEL().length;
14268         
14269         if (typeof(this.minViewMode === 'string')) {
14270             switch (this.minViewMode) {
14271                 case 'months':
14272                     this.minViewMode = 1;
14273                     break;
14274                 case 'years':
14275                     this.minViewMode = 2;
14276                     break;
14277                 default:
14278                     this.minViewMode = 0;
14279                     break;
14280             }
14281         }
14282         
14283         if (typeof(this.viewMode === 'string')) {
14284             switch (this.viewMode) {
14285                 case 'months':
14286                     this.viewMode = 1;
14287                     break;
14288                 case 'years':
14289                     this.viewMode = 2;
14290                     break;
14291                 default:
14292                     this.viewMode = 0;
14293                     break;
14294             }
14295         }
14296                 
14297         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
14298         
14299         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14300         
14301         this.picker().on('mousedown', this.onMousedown, this);
14302         this.picker().on('click', this.onClick, this);
14303         
14304         this.picker().addClass('datepicker-dropdown');
14305         
14306         this.startViewMode = this.viewMode;
14307         
14308         
14309         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
14310             if(!this.calendarWeeks){
14311                 v.remove();
14312                 return;
14313             };
14314             
14315             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
14316             v.attr('colspan', function(i, val){
14317                 return parseInt(val) + 1;
14318             });
14319         })
14320                         
14321         
14322         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
14323         
14324         this.setStartDate(this.startDate);
14325         this.setEndDate(this.endDate);
14326         
14327         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
14328         
14329         this.fillDow();
14330         this.fillMonths();
14331         this.update();
14332         this.showMode();
14333         
14334         if(this.isInline) {
14335             this.show();
14336         }
14337     },
14338     
14339     picker : function()
14340     {
14341         return this.el.select('.datepicker', true).first();
14342     },
14343     
14344     fillDow: function()
14345     {
14346         var dowCnt = this.weekStart;
14347         
14348         var dow = {
14349             tag: 'tr',
14350             cn: [
14351                 
14352             ]
14353         };
14354         
14355         if(this.calendarWeeks){
14356             dow.cn.push({
14357                 tag: 'th',
14358                 cls: 'cw',
14359                 html: '&nbsp;'
14360             })
14361         }
14362         
14363         while (dowCnt < this.weekStart + 7) {
14364             dow.cn.push({
14365                 tag: 'th',
14366                 cls: 'dow',
14367                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
14368             });
14369         }
14370         
14371         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
14372     },
14373     
14374     fillMonths: function()
14375     {    
14376         var i = 0
14377         var months = this.picker().select('>.datepicker-months td', true).first();
14378         
14379         months.dom.innerHTML = '';
14380         
14381         while (i < 12) {
14382             var month = {
14383                 tag: 'span',
14384                 cls: 'month',
14385                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
14386             }
14387             
14388             months.createChild(month);
14389         }
14390         
14391     },
14392     
14393     update: function()
14394     {
14395         
14396         this.date = (typeof(this.date) === 'undefined') ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
14397         
14398         if (this.date < this.startDate) {
14399             this.viewDate = new Date(this.startDate);
14400         } else if (this.date > this.endDate) {
14401             this.viewDate = new Date(this.endDate);
14402         } else {
14403             this.viewDate = new Date(this.date);
14404         }
14405         
14406         this.fill();
14407     },
14408     
14409     fill: function() 
14410     {
14411         var d = new Date(this.viewDate),
14412                 year = d.getUTCFullYear(),
14413                 month = d.getUTCMonth(),
14414                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
14415                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
14416                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
14417                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
14418                 currentDate = this.date && this.date.valueOf(),
14419                 today = this.UTCToday();
14420         
14421         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
14422         
14423 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
14424         
14425 //        this.picker.select('>tfoot th.today').
14426 //                                              .text(dates[this.language].today)
14427 //                                              .toggle(this.todayBtn !== false);
14428     
14429         this.updateNavArrows();
14430         this.fillMonths();
14431                                                 
14432         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
14433         
14434         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
14435          
14436         prevMonth.setUTCDate(day);
14437         
14438         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
14439         
14440         var nextMonth = new Date(prevMonth);
14441         
14442         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
14443         
14444         nextMonth = nextMonth.valueOf();
14445         
14446         var fillMonths = false;
14447         
14448         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
14449         
14450         while(prevMonth.valueOf() < nextMonth) {
14451             var clsName = '';
14452             
14453             if (prevMonth.getUTCDay() === this.weekStart) {
14454                 if(fillMonths){
14455                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
14456                 }
14457                     
14458                 fillMonths = {
14459                     tag: 'tr',
14460                     cn: []
14461                 };
14462                 
14463                 if(this.calendarWeeks){
14464                     // ISO 8601: First week contains first thursday.
14465                     // ISO also states week starts on Monday, but we can be more abstract here.
14466                     var
14467                     // Start of current week: based on weekstart/current date
14468                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
14469                     // Thursday of this week
14470                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
14471                     // First Thursday of year, year from thursday
14472                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
14473                     // Calendar week: ms between thursdays, div ms per day, div 7 days
14474                     calWeek =  (th - yth) / 864e5 / 7 + 1;
14475                     
14476                     fillMonths.cn.push({
14477                         tag: 'td',
14478                         cls: 'cw',
14479                         html: calWeek
14480                     });
14481                 }
14482             }
14483             
14484             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
14485                 clsName += ' old';
14486             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
14487                 clsName += ' new';
14488             }
14489             if (this.todayHighlight &&
14490                 prevMonth.getUTCFullYear() == today.getFullYear() &&
14491                 prevMonth.getUTCMonth() == today.getMonth() &&
14492                 prevMonth.getUTCDate() == today.getDate()) {
14493                 clsName += ' today';
14494             }
14495             
14496             if (currentDate && prevMonth.valueOf() === currentDate) {
14497                 clsName += ' active';
14498             }
14499             
14500             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
14501                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
14502                     clsName += ' disabled';
14503             }
14504             
14505             fillMonths.cn.push({
14506                 tag: 'td',
14507                 cls: 'day ' + clsName,
14508                 html: prevMonth.getDate()
14509             })
14510             
14511             prevMonth.setDate(prevMonth.getDate()+1);
14512         }
14513           
14514         var currentYear = this.date && this.date.getUTCFullYear();
14515         var currentMonth = this.date && this.date.getUTCMonth();
14516         
14517         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
14518         
14519         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
14520             v.removeClass('active');
14521             
14522             if(currentYear === year && k === currentMonth){
14523                 v.addClass('active');
14524             }
14525             
14526             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
14527                 v.addClass('disabled');
14528             }
14529             
14530         });
14531         
14532         
14533         year = parseInt(year/10, 10) * 10;
14534         
14535         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
14536         
14537         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
14538         
14539         year -= 1;
14540         for (var i = -1; i < 11; i++) {
14541             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
14542                 tag: 'span',
14543                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
14544                 html: year
14545             })
14546             
14547             year += 1;
14548         }
14549     },
14550     
14551     showMode: function(dir) 
14552     {
14553         if (dir) {
14554             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
14555         }
14556         Roo.each(this.picker().select('>div',true).elements, function(v){
14557             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14558             v.hide();
14559         });
14560         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
14561     },
14562     
14563     place: function()
14564     {
14565         if(this.isInline) return;
14566         
14567         this.picker().removeClass(['bottom', 'top']);
14568         
14569         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
14570             /*
14571              * place to the top of element!
14572              *
14573              */
14574             
14575             this.picker().addClass('top');
14576             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
14577             
14578             return;
14579         }
14580         
14581         this.picker().addClass('bottom');
14582         
14583         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
14584     },
14585     
14586     parseDate : function(value)
14587     {
14588         if(!value || value instanceof Date){
14589             return value;
14590         }
14591         var v = Date.parseDate(value, this.format);
14592         if (!v && this.useIso) {
14593             v = Date.parseDate(value, 'Y-m-d');
14594         }
14595         if(!v && this.altFormats){
14596             if(!this.altFormatsArray){
14597                 this.altFormatsArray = this.altFormats.split("|");
14598             }
14599             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
14600                 v = Date.parseDate(value, this.altFormatsArray[i]);
14601             }
14602         }
14603         return v;
14604     },
14605     
14606     formatDate : function(date, fmt)
14607     {
14608         return (!date || !(date instanceof Date)) ?
14609         date : date.dateFormat(fmt || this.format);
14610     },
14611     
14612     onFocus : function()
14613     {
14614         Roo.bootstrap.DateField.superclass.onFocus.call(this);
14615         this.show();
14616     },
14617     
14618     onBlur : function()
14619     {
14620         Roo.bootstrap.DateField.superclass.onBlur.call(this);
14621         
14622         var d = this.inputEl().getValue();
14623         
14624         if(d && d.length){
14625             this.setValue(d);
14626         }
14627                 
14628         this.hide();
14629     },
14630     
14631     show : function()
14632     {
14633         this.picker().show();
14634         this.update();
14635         this.place();
14636         
14637         this.fireEvent('show', this, this.date);
14638     },
14639     
14640     hide : function()
14641     {
14642         if(this.isInline) return;
14643         this.picker().hide();
14644         this.viewMode = this.startViewMode;
14645         this.showMode();
14646         
14647         this.fireEvent('hide', this, this.date);
14648         
14649     },
14650     
14651     onMousedown: function(e)
14652     {
14653         e.stopPropagation();
14654         e.preventDefault();
14655     },
14656     
14657     keyup: function(e)
14658     {
14659         Roo.bootstrap.DateField.superclass.keyup.call(this);
14660         this.update();
14661     },
14662
14663     setValue: function(v)
14664     {
14665         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
14666         
14667         var d = new Date(v);
14668         
14669         if(isNaN(d.getTime())){
14670             return;
14671         }
14672         
14673         this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
14674
14675         this.update();
14676
14677         this.fireEvent('select', this, this.date);
14678         
14679     },
14680     
14681     getValue: function()
14682     {
14683         return this.formatDate(this.date);
14684     },
14685     
14686     fireKey: function(e)
14687     {
14688         if (!this.picker().isVisible()){
14689             if (e.keyCode == 27) // allow escape to hide and re-show picker
14690                 this.show();
14691             return;
14692         }
14693         
14694         var dateChanged = false,
14695         dir, day, month,
14696         newDate, newViewDate;
14697         
14698         switch(e.keyCode){
14699             case 27: // escape
14700                 this.hide();
14701                 e.preventDefault();
14702                 break;
14703             case 37: // left
14704             case 39: // right
14705                 if (!this.keyboardNavigation) break;
14706                 dir = e.keyCode == 37 ? -1 : 1;
14707                 
14708                 if (e.ctrlKey){
14709                     newDate = this.moveYear(this.date, dir);
14710                     newViewDate = this.moveYear(this.viewDate, dir);
14711                 } else if (e.shiftKey){
14712                     newDate = this.moveMonth(this.date, dir);
14713                     newViewDate = this.moveMonth(this.viewDate, dir);
14714                 } else {
14715                     newDate = new Date(this.date);
14716                     newDate.setUTCDate(this.date.getUTCDate() + dir);
14717                     newViewDate = new Date(this.viewDate);
14718                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
14719                 }
14720                 if (this.dateWithinRange(newDate)){
14721                     this.date = newDate;
14722                     this.viewDate = newViewDate;
14723                     this.setValue(this.formatDate(this.date));
14724 //                    this.update();
14725                     e.preventDefault();
14726                     dateChanged = true;
14727                 }
14728                 break;
14729             case 38: // up
14730             case 40: // down
14731                 if (!this.keyboardNavigation) break;
14732                 dir = e.keyCode == 38 ? -1 : 1;
14733                 if (e.ctrlKey){
14734                     newDate = this.moveYear(this.date, dir);
14735                     newViewDate = this.moveYear(this.viewDate, dir);
14736                 } else if (e.shiftKey){
14737                     newDate = this.moveMonth(this.date, dir);
14738                     newViewDate = this.moveMonth(this.viewDate, dir);
14739                 } else {
14740                     newDate = new Date(this.date);
14741                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
14742                     newViewDate = new Date(this.viewDate);
14743                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
14744                 }
14745                 if (this.dateWithinRange(newDate)){
14746                     this.date = newDate;
14747                     this.viewDate = newViewDate;
14748                     this.setValue(this.formatDate(this.date));
14749 //                    this.update();
14750                     e.preventDefault();
14751                     dateChanged = true;
14752                 }
14753                 break;
14754             case 13: // enter
14755                 this.setValue(this.formatDate(this.date));
14756                 this.hide();
14757                 e.preventDefault();
14758                 break;
14759             case 9: // tab
14760                 this.setValue(this.formatDate(this.date));
14761                 this.hide();
14762                 break;
14763             case 16: // shift
14764             case 17: // ctrl
14765             case 18: // alt
14766                 break;
14767             default :
14768                 this.hide();
14769                 
14770         }
14771     },
14772     
14773     
14774     onClick: function(e) 
14775     {
14776         e.stopPropagation();
14777         e.preventDefault();
14778         
14779         var target = e.getTarget();
14780         
14781         if(target.nodeName.toLowerCase() === 'i'){
14782             target = Roo.get(target).dom.parentNode;
14783         }
14784         
14785         var nodeName = target.nodeName;
14786         var className = target.className;
14787         var html = target.innerHTML;
14788         
14789         switch(nodeName.toLowerCase()) {
14790             case 'th':
14791                 switch(className) {
14792                     case 'switch':
14793                         this.showMode(1);
14794                         break;
14795                     case 'prev':
14796                     case 'next':
14797                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
14798                         switch(this.viewMode){
14799                                 case 0:
14800                                         this.viewDate = this.moveMonth(this.viewDate, dir);
14801                                         break;
14802                                 case 1:
14803                                 case 2:
14804                                         this.viewDate = this.moveYear(this.viewDate, dir);
14805                                         break;
14806                         }
14807                         this.fill();
14808                         break;
14809                     case 'today':
14810                         var date = new Date();
14811                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
14812 //                        this.fill()
14813                         this.setValue(this.formatDate(this.date));
14814                         
14815                         this.hide();
14816                         break;
14817                 }
14818                 break;
14819             case 'span':
14820                 if (className.indexOf('disabled') === -1) {
14821                     this.viewDate.setUTCDate(1);
14822                     if (className.indexOf('month') !== -1) {
14823                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
14824                     } else {
14825                         var year = parseInt(html, 10) || 0;
14826                         this.viewDate.setUTCFullYear(year);
14827                         
14828                     }
14829                     this.showMode(-1);
14830                     this.fill();
14831                 }
14832                 break;
14833                 
14834             case 'td':
14835                 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
14836                     var day = parseInt(html, 10) || 1;
14837                     var year = this.viewDate.getUTCFullYear(),
14838                         month = this.viewDate.getUTCMonth();
14839
14840                     if (className.indexOf('old') !== -1) {
14841                         if(month === 0 ){
14842                             month = 11;
14843                             year -= 1;
14844                         }else{
14845                             month -= 1;
14846                         }
14847                     } else if (className.indexOf('new') !== -1) {
14848                         if (month == 11) {
14849                             month = 0;
14850                             year += 1;
14851                         } else {
14852                             month += 1;
14853                         }
14854                     }
14855                     this.date = this.UTCDate(year, month, day,0,0,0,0);
14856                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
14857 //                    this.fill();
14858                     this.setValue(this.formatDate(this.date));
14859                     this.hide();
14860                 }
14861                 break;
14862         }
14863     },
14864     
14865     setStartDate: function(startDate)
14866     {
14867         this.startDate = startDate || -Infinity;
14868         if (this.startDate !== -Infinity) {
14869             this.startDate = this.parseDate(this.startDate);
14870         }
14871         this.update();
14872         this.updateNavArrows();
14873     },
14874
14875     setEndDate: function(endDate)
14876     {
14877         this.endDate = endDate || Infinity;
14878         if (this.endDate !== Infinity) {
14879             this.endDate = this.parseDate(this.endDate);
14880         }
14881         this.update();
14882         this.updateNavArrows();
14883     },
14884     
14885     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
14886     {
14887         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
14888         if (typeof(this.daysOfWeekDisabled) !== 'object') {
14889             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
14890         }
14891         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
14892             return parseInt(d, 10);
14893         });
14894         this.update();
14895         this.updateNavArrows();
14896     },
14897     
14898     updateNavArrows: function() 
14899     {
14900         var d = new Date(this.viewDate),
14901         year = d.getUTCFullYear(),
14902         month = d.getUTCMonth();
14903         
14904         Roo.each(this.picker().select('.prev', true).elements, function(v){
14905             v.show();
14906             switch (this.viewMode) {
14907                 case 0:
14908
14909                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
14910                         v.hide();
14911                     }
14912                     break;
14913                 case 1:
14914                 case 2:
14915                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
14916                         v.hide();
14917                     }
14918                     break;
14919             }
14920         });
14921         
14922         Roo.each(this.picker().select('.next', true).elements, function(v){
14923             v.show();
14924             switch (this.viewMode) {
14925                 case 0:
14926
14927                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
14928                         v.hide();
14929                     }
14930                     break;
14931                 case 1:
14932                 case 2:
14933                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
14934                         v.hide();
14935                     }
14936                     break;
14937             }
14938         })
14939     },
14940     
14941     moveMonth: function(date, dir)
14942     {
14943         if (!dir) return date;
14944         var new_date = new Date(date.valueOf()),
14945         day = new_date.getUTCDate(),
14946         month = new_date.getUTCMonth(),
14947         mag = Math.abs(dir),
14948         new_month, test;
14949         dir = dir > 0 ? 1 : -1;
14950         if (mag == 1){
14951             test = dir == -1
14952             // If going back one month, make sure month is not current month
14953             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
14954             ? function(){
14955                 return new_date.getUTCMonth() == month;
14956             }
14957             // If going forward one month, make sure month is as expected
14958             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
14959             : function(){
14960                 return new_date.getUTCMonth() != new_month;
14961             };
14962             new_month = month + dir;
14963             new_date.setUTCMonth(new_month);
14964             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
14965             if (new_month < 0 || new_month > 11)
14966                 new_month = (new_month + 12) % 12;
14967         } else {
14968             // For magnitudes >1, move one month at a time...
14969             for (var i=0; i<mag; i++)
14970                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
14971                 new_date = this.moveMonth(new_date, dir);
14972             // ...then reset the day, keeping it in the new month
14973             new_month = new_date.getUTCMonth();
14974             new_date.setUTCDate(day);
14975             test = function(){
14976                 return new_month != new_date.getUTCMonth();
14977             };
14978         }
14979         // Common date-resetting loop -- if date is beyond end of month, make it
14980         // end of month
14981         while (test()){
14982             new_date.setUTCDate(--day);
14983             new_date.setUTCMonth(new_month);
14984         }
14985         return new_date;
14986     },
14987
14988     moveYear: function(date, dir)
14989     {
14990         return this.moveMonth(date, dir*12);
14991     },
14992
14993     dateWithinRange: function(date)
14994     {
14995         return date >= this.startDate && date <= this.endDate;
14996     },
14997
14998     
14999     remove: function() 
15000     {
15001         this.picker().remove();
15002     }
15003    
15004 });
15005
15006 Roo.apply(Roo.bootstrap.DateField,  {
15007     
15008     head : {
15009         tag: 'thead',
15010         cn: [
15011         {
15012             tag: 'tr',
15013             cn: [
15014             {
15015                 tag: 'th',
15016                 cls: 'prev',
15017                 html: '<i class="fa fa-arrow-left"/>'
15018             },
15019             {
15020                 tag: 'th',
15021                 cls: 'switch',
15022                 colspan: '5'
15023             },
15024             {
15025                 tag: 'th',
15026                 cls: 'next',
15027                 html: '<i class="fa fa-arrow-right"/>'
15028             }
15029
15030             ]
15031         }
15032         ]
15033     },
15034     
15035     content : {
15036         tag: 'tbody',
15037         cn: [
15038         {
15039             tag: 'tr',
15040             cn: [
15041             {
15042                 tag: 'td',
15043                 colspan: '7'
15044             }
15045             ]
15046         }
15047         ]
15048     },
15049     
15050     footer : {
15051         tag: 'tfoot',
15052         cn: [
15053         {
15054             tag: 'tr',
15055             cn: [
15056             {
15057                 tag: 'th',
15058                 colspan: '7',
15059                 cls: 'today'
15060             }
15061                     
15062             ]
15063         }
15064         ]
15065     },
15066     
15067     dates:{
15068         en: {
15069             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
15070             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
15071             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
15072             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
15073             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
15074             today: "Today"
15075         }
15076     },
15077     
15078     modes: [
15079     {
15080         clsName: 'days',
15081         navFnc: 'Month',
15082         navStep: 1
15083     },
15084     {
15085         clsName: 'months',
15086         navFnc: 'FullYear',
15087         navStep: 1
15088     },
15089     {
15090         clsName: 'years',
15091         navFnc: 'FullYear',
15092         navStep: 10
15093     }]
15094 });
15095
15096 Roo.apply(Roo.bootstrap.DateField,  {
15097   
15098     template : {
15099         tag: 'div',
15100         cls: 'datepicker dropdown-menu',
15101         cn: [
15102         {
15103             tag: 'div',
15104             cls: 'datepicker-days',
15105             cn: [
15106             {
15107                 tag: 'table',
15108                 cls: 'table-condensed',
15109                 cn:[
15110                 Roo.bootstrap.DateField.head,
15111                 {
15112                     tag: 'tbody'
15113                 },
15114                 Roo.bootstrap.DateField.footer
15115                 ]
15116             }
15117             ]
15118         },
15119         {
15120             tag: 'div',
15121             cls: 'datepicker-months',
15122             cn: [
15123             {
15124                 tag: 'table',
15125                 cls: 'table-condensed',
15126                 cn:[
15127                 Roo.bootstrap.DateField.head,
15128                 Roo.bootstrap.DateField.content,
15129                 Roo.bootstrap.DateField.footer
15130                 ]
15131             }
15132             ]
15133         },
15134         {
15135             tag: 'div',
15136             cls: 'datepicker-years',
15137             cn: [
15138             {
15139                 tag: 'table',
15140                 cls: 'table-condensed',
15141                 cn:[
15142                 Roo.bootstrap.DateField.head,
15143                 Roo.bootstrap.DateField.content,
15144                 Roo.bootstrap.DateField.footer
15145                 ]
15146             }
15147             ]
15148         }
15149         ]
15150     }
15151 });
15152
15153  
15154
15155  /*
15156  * - LGPL
15157  *
15158  * TimeField
15159  * 
15160  */
15161
15162 /**
15163  * @class Roo.bootstrap.TimeField
15164  * @extends Roo.bootstrap.Input
15165  * Bootstrap DateField class
15166  * 
15167  * 
15168  * @constructor
15169  * Create a new TimeField
15170  * @param {Object} config The config object
15171  */
15172
15173 Roo.bootstrap.TimeField = function(config){
15174     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
15175     this.addEvents({
15176             /**
15177              * @event show
15178              * Fires when this field show.
15179              * @param {Roo.bootstrap.DateField} this
15180              * @param {Mixed} date The date value
15181              */
15182             show : true,
15183             /**
15184              * @event show
15185              * Fires when this field hide.
15186              * @param {Roo.bootstrap.DateField} this
15187              * @param {Mixed} date The date value
15188              */
15189             hide : true,
15190             /**
15191              * @event select
15192              * Fires when select a date.
15193              * @param {Roo.bootstrap.DateField} this
15194              * @param {Mixed} date The date value
15195              */
15196             select : true
15197         });
15198 };
15199
15200 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
15201     
15202     /**
15203      * @cfg {String} format
15204      * The default time format string which can be overriden for localization support.  The format must be
15205      * valid according to {@link Date#parseDate} (defaults to 'H:i').
15206      */
15207     format : "H:i",
15208        
15209     onRender: function(ct, position)
15210     {
15211         
15212         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
15213                 
15214         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
15215         
15216         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15217         
15218         this.pop = this.picker().select('>.datepicker-time',true).first();
15219         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block' 
15220         
15221         this.picker().on('mousedown', this.onMousedown, this);
15222         this.picker().on('click', this.onClick, this);
15223         
15224         this.picker().addClass('datepicker-dropdown');
15225     
15226         this.fillTime();
15227         this.update();
15228             
15229         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
15230         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
15231         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
15232         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
15233         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
15234         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
15235
15236     },
15237     
15238     fireKey: function(e){
15239         if (!this.picker().isVisible()){
15240             if (e.keyCode == 27) // allow escape to hide and re-show picker
15241                 this.show();
15242             return;
15243         }
15244
15245         e.preventDefault();
15246         
15247         switch(e.keyCode){
15248             case 27: // escape
15249                 this.hide();
15250                 break;
15251             case 37: // left
15252             case 39: // right
15253                 this.onTogglePeriod();
15254                 break;
15255             case 38: // up
15256                 this.onIncrementMinutes();
15257                 break;
15258             case 40: // down
15259                 this.onDecrementMinutes();
15260                 break;
15261             case 13: // enter
15262             case 9: // tab
15263                 this.setTime();
15264                 break;
15265         }
15266     },
15267     
15268     onClick: function(e) {
15269         e.stopPropagation();
15270         e.preventDefault();
15271     },
15272     
15273     picker : function()
15274     {
15275         return this.el.select('.datepicker', true).first();
15276     },
15277     
15278     fillTime: function()
15279     {    
15280         var time = this.pop.select('tbody', true).first();
15281         
15282         time.dom.innerHTML = '';
15283         
15284         time.createChild({
15285             tag: 'tr',
15286             cn: [
15287                 {
15288                     tag: 'td',
15289                     cn: [
15290                         {
15291                             tag: 'a',
15292                             href: '#',
15293                             cls: 'btn',
15294                             cn: [
15295                                 {
15296                                     tag: 'span',
15297                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
15298                                 }
15299                             ]
15300                         } 
15301                     ]
15302                 },
15303                 {
15304                     tag: 'td',
15305                     cls: 'separator'
15306                 },
15307                 {
15308                     tag: 'td',
15309                     cn: [
15310                         {
15311                             tag: 'a',
15312                             href: '#',
15313                             cls: 'btn',
15314                             cn: [
15315                                 {
15316                                     tag: 'span',
15317                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
15318                                 }
15319                             ]
15320                         }
15321                     ]
15322                 },
15323                 {
15324                     tag: 'td',
15325                     cls: 'separator'
15326                 }
15327             ]
15328         });
15329         
15330         time.createChild({
15331             tag: 'tr',
15332             cn: [
15333                 {
15334                     tag: 'td',
15335                     cn: [
15336                         {
15337                             tag: 'span',
15338                             cls: 'timepicker-hour',
15339                             html: '00'
15340                         }  
15341                     ]
15342                 },
15343                 {
15344                     tag: 'td',
15345                     cls: 'separator',
15346                     html: ':'
15347                 },
15348                 {
15349                     tag: 'td',
15350                     cn: [
15351                         {
15352                             tag: 'span',
15353                             cls: 'timepicker-minute',
15354                             html: '00'
15355                         }  
15356                     ]
15357                 },
15358                 {
15359                     tag: 'td',
15360                     cls: 'separator'
15361                 },
15362                 {
15363                     tag: 'td',
15364                     cn: [
15365                         {
15366                             tag: 'button',
15367                             type: 'button',
15368                             cls: 'btn btn-primary period',
15369                             html: 'AM'
15370                             
15371                         }
15372                     ]
15373                 }
15374             ]
15375         });
15376         
15377         time.createChild({
15378             tag: 'tr',
15379             cn: [
15380                 {
15381                     tag: 'td',
15382                     cn: [
15383                         {
15384                             tag: 'a',
15385                             href: '#',
15386                             cls: 'btn',
15387                             cn: [
15388                                 {
15389                                     tag: 'span',
15390                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
15391                                 }
15392                             ]
15393                         }
15394                     ]
15395                 },
15396                 {
15397                     tag: 'td',
15398                     cls: 'separator'
15399                 },
15400                 {
15401                     tag: 'td',
15402                     cn: [
15403                         {
15404                             tag: 'a',
15405                             href: '#',
15406                             cls: 'btn',
15407                             cn: [
15408                                 {
15409                                     tag: 'span',
15410                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
15411                                 }
15412                             ]
15413                         }
15414                     ]
15415                 },
15416                 {
15417                     tag: 'td',
15418                     cls: 'separator'
15419                 }
15420             ]
15421         });
15422         
15423     },
15424     
15425     update: function()
15426     {
15427         
15428         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
15429         
15430         this.fill();
15431     },
15432     
15433     fill: function() 
15434     {
15435         var hours = this.time.getHours();
15436         var minutes = this.time.getMinutes();
15437         var period = 'AM';
15438         
15439         if(hours > 11){
15440             period = 'PM';
15441         }
15442         
15443         if(hours == 0){
15444             hours = 12;
15445         }
15446         
15447         
15448         if(hours > 12){
15449             hours = hours - 12;
15450         }
15451         
15452         if(hours < 10){
15453             hours = '0' + hours;
15454         }
15455         
15456         if(minutes < 10){
15457             minutes = '0' + minutes;
15458         }
15459         
15460         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
15461         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
15462         this.pop.select('button', true).first().dom.innerHTML = period;
15463         
15464     },
15465     
15466     place: function()
15467     {   
15468         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
15469         
15470         var cls = ['bottom'];
15471         
15472         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
15473             cls.pop();
15474             cls.push('top');
15475         }
15476         
15477         cls.push('right');
15478         
15479         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
15480             cls.pop();
15481             cls.push('left');
15482         }
15483         
15484         this.picker().addClass(cls.join('-'));
15485         
15486         var _this = this;
15487         
15488         Roo.each(cls, function(c){
15489             if(c == 'bottom'){
15490                 _this.picker().setTop(_this.inputEl().getHeight());
15491                 return;
15492             }
15493             if(c == 'top'){
15494                 _this.picker().setTop(0 - _this.picker().getHeight());
15495                 return;
15496             }
15497             
15498             if(c == 'left'){
15499                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
15500                 return;
15501             }
15502             if(c == 'right'){
15503                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
15504                 return;
15505             }
15506         });
15507         
15508     },
15509   
15510     onFocus : function()
15511     {
15512         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
15513         this.show();
15514     },
15515     
15516     onBlur : function()
15517     {
15518         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
15519         this.hide();
15520     },
15521     
15522     show : function()
15523     {
15524         this.picker().show();
15525         this.pop.show();
15526         this.update();
15527         this.place();
15528         
15529         this.fireEvent('show', this, this.date);
15530     },
15531     
15532     hide : function()
15533     {
15534         this.picker().hide();
15535         this.pop.hide();
15536         
15537         this.fireEvent('hide', this, this.date);
15538     },
15539     
15540     setTime : function()
15541     {
15542         this.hide();
15543         this.setValue(this.time.format(this.format));
15544         
15545         this.fireEvent('select', this, this.date);
15546         
15547         
15548     },
15549     
15550     onMousedown: function(e){
15551         e.stopPropagation();
15552         e.preventDefault();
15553     },
15554     
15555     onIncrementHours: function()
15556     {
15557         Roo.log('onIncrementHours');
15558         this.time = this.time.add(Date.HOUR, 1);
15559         this.update();
15560         
15561     },
15562     
15563     onDecrementHours: function()
15564     {
15565         Roo.log('onDecrementHours');
15566         this.time = this.time.add(Date.HOUR, -1);
15567         this.update();
15568     },
15569     
15570     onIncrementMinutes: function()
15571     {
15572         Roo.log('onIncrementMinutes');
15573         this.time = this.time.add(Date.MINUTE, 1);
15574         this.update();
15575     },
15576     
15577     onDecrementMinutes: function()
15578     {
15579         Roo.log('onDecrementMinutes');
15580         this.time = this.time.add(Date.MINUTE, -1);
15581         this.update();
15582     },
15583     
15584     onTogglePeriod: function()
15585     {
15586         Roo.log('onTogglePeriod');
15587         this.time = this.time.add(Date.HOUR, 12);
15588         this.update();
15589     }
15590     
15591    
15592 });
15593
15594 Roo.apply(Roo.bootstrap.TimeField,  {
15595     
15596     content : {
15597         tag: 'tbody',
15598         cn: [
15599             {
15600                 tag: 'tr',
15601                 cn: [
15602                 {
15603                     tag: 'td',
15604                     colspan: '7'
15605                 }
15606                 ]
15607             }
15608         ]
15609     },
15610     
15611     footer : {
15612         tag: 'tfoot',
15613         cn: [
15614             {
15615                 tag: 'tr',
15616                 cn: [
15617                 {
15618                     tag: 'th',
15619                     colspan: '7',
15620                     cls: '',
15621                     cn: [
15622                         {
15623                             tag: 'button',
15624                             cls: 'btn btn-info ok',
15625                             html: 'OK'
15626                         }
15627                     ]
15628                 }
15629
15630                 ]
15631             }
15632         ]
15633     }
15634 });
15635
15636 Roo.apply(Roo.bootstrap.TimeField,  {
15637   
15638     template : {
15639         tag: 'div',
15640         cls: 'datepicker dropdown-menu',
15641         cn: [
15642             {
15643                 tag: 'div',
15644                 cls: 'datepicker-time',
15645                 cn: [
15646                 {
15647                     tag: 'table',
15648                     cls: 'table-condensed',
15649                     cn:[
15650                     Roo.bootstrap.TimeField.content,
15651                     Roo.bootstrap.TimeField.footer
15652                     ]
15653                 }
15654                 ]
15655             }
15656         ]
15657     }
15658 });
15659
15660  
15661
15662  /*
15663  * - LGPL
15664  *
15665  * CheckBox
15666  * 
15667  */
15668
15669 /**
15670  * @class Roo.bootstrap.CheckBox
15671  * @extends Roo.bootstrap.Input
15672  * Bootstrap CheckBox class
15673  * 
15674  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
15675  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
15676  * @cfg {String} boxLabel The text that appears beside the checkbox
15677  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
15678  * @cfg {Boolean} checked initnal the element
15679  * 
15680  * 
15681  * @constructor
15682  * Create a new CheckBox
15683  * @param {Object} config The config object
15684  */
15685
15686 Roo.bootstrap.CheckBox = function(config){
15687     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
15688    
15689         this.addEvents({
15690             /**
15691             * @event check
15692             * Fires when the element is checked or unchecked.
15693             * @param {Roo.bootstrap.CheckBox} this This input
15694             * @param {Boolean} checked The new checked value
15695             */
15696            check : true
15697         });
15698 };
15699
15700 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
15701     
15702     inputType: 'checkbox',
15703     inputValue: 1,
15704     valueOff: 0,
15705     boxLabel: false,
15706     checked: false,
15707     weight : false,
15708     
15709     getAutoCreate : function()
15710     {
15711         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
15712         
15713         var id = Roo.id();
15714         
15715         var cfg = {};
15716         
15717         cfg.cls = 'form-group checkbox' //input-group
15718         
15719         
15720         
15721         
15722         var input =  {
15723             tag: 'input',
15724             id : id,
15725             type : this.inputType,
15726             value : (!this.checked) ? this.valueOff : this.inputValue,
15727             cls : 'roo-checkbox', //'form-box',
15728             placeholder : this.placeholder || ''
15729             
15730         };
15731         
15732         if (this.weight) { // Validity check?
15733             cfg.cls += " checkbox-" + this.weight;
15734         }
15735         
15736         if (this.disabled) {
15737             input.disabled=true;
15738         }
15739         
15740         if(this.checked){
15741             input.checked = this.checked;
15742         }
15743         
15744         if (this.name) {
15745             input.name = this.name;
15746         }
15747         
15748         if (this.size) {
15749             input.cls += ' input-' + this.size;
15750         }
15751         
15752         var settings=this;
15753         ['xs','sm','md','lg'].map(function(size){
15754             if (settings[size]) {
15755                 cfg.cls += ' col-' + size + '-' + settings[size];
15756             }
15757         });
15758         
15759        
15760         
15761         var inputblock = input;
15762         
15763         
15764         
15765         
15766         if (this.before || this.after) {
15767             
15768             inputblock = {
15769                 cls : 'input-group',
15770                 cn :  [] 
15771             };
15772             if (this.before) {
15773                 inputblock.cn.push({
15774                     tag :'span',
15775                     cls : 'input-group-addon',
15776                     html : this.before
15777                 });
15778             }
15779             inputblock.cn.push(input);
15780             if (this.after) {
15781                 inputblock.cn.push({
15782                     tag :'span',
15783                     cls : 'input-group-addon',
15784                     html : this.after
15785                 });
15786             }
15787             
15788         };
15789         
15790         if (align ==='left' && this.fieldLabel.length) {
15791                 Roo.log("left and has label");
15792                 cfg.cn = [
15793                     
15794                     {
15795                         tag: 'label',
15796                         'for' :  id,
15797                         cls : 'control-label col-md-' + this.labelWidth,
15798                         html : this.fieldLabel
15799                         
15800                     },
15801                     {
15802                         cls : "col-md-" + (12 - this.labelWidth), 
15803                         cn: [
15804                             inputblock
15805                         ]
15806                     }
15807                     
15808                 ];
15809         } else if ( this.fieldLabel.length) {
15810                 Roo.log(" label");
15811                 cfg.cn = [
15812                    
15813                     {
15814                         tag: this.boxLabel ? 'span' : 'label',
15815                         'for': id,
15816                         cls: 'control-label box-input-label',
15817                         //cls : 'input-group-addon',
15818                         html : this.fieldLabel
15819                         
15820                     },
15821                     
15822                     inputblock
15823                     
15824                 ];
15825
15826         } else {
15827             
15828                 Roo.log(" no label && no align");
15829                 cfg.cn = [  inputblock ] ;
15830                 
15831                 
15832         };
15833          if(this.boxLabel){
15834             cfg.cn.push( {
15835                 tag: 'label',
15836                 'for': id,
15837                 cls: 'box-label',
15838                 html: this.boxLabel
15839                 
15840             });
15841         }
15842         
15843         
15844        
15845         return cfg;
15846         
15847     },
15848     
15849     /**
15850      * return the real input element.
15851      */
15852     inputEl: function ()
15853     {
15854         return this.el.select('input.roo-checkbox',true).first();
15855     },
15856     
15857     label: function()
15858     {
15859         return this.el.select('label.control-label',true).first();
15860     },
15861     
15862     initEvents : function()
15863     {
15864 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
15865         
15866         this.inputEl().on('click', this.onClick,  this);
15867         
15868     },
15869     
15870     onClick : function()
15871     {   
15872         this.setChecked(!this.checked);
15873     },
15874     
15875     setChecked : function(state,suppressEvent)
15876     {
15877         this.checked = state;
15878         
15879         this.inputEl().dom.checked = state;
15880         
15881         if(suppressEvent !== true){
15882             this.fireEvent('check', this, state);
15883         }
15884         
15885         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
15886         
15887     },
15888     
15889     setValue : function(v,suppressEvent)
15890     {
15891         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
15892     }
15893     
15894 });
15895
15896  
15897 /*
15898  * - LGPL
15899  *
15900  * Radio
15901  * 
15902  */
15903
15904 /**
15905  * @class Roo.bootstrap.Radio
15906  * @extends Roo.bootstrap.CheckBox
15907  * Bootstrap Radio class
15908
15909  * @constructor
15910  * Create a new Radio
15911  * @param {Object} config The config object
15912  */
15913
15914 Roo.bootstrap.Radio = function(config){
15915     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
15916    
15917 };
15918
15919 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
15920     
15921     inputType: 'radio',
15922     inputValue: '',
15923     valueOff: '',
15924     
15925     getAutoCreate : function()
15926     {
15927         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
15928         
15929         var id = Roo.id();
15930         
15931         var cfg = {};
15932         
15933         cfg.cls = 'form-group radio' //input-group
15934         
15935         var input =  {
15936             tag: 'input',
15937             id : id,
15938             type : this.inputType,
15939             value : (!this.checked) ? this.valueOff : this.inputValue,
15940             cls : 'roo-radio',
15941             placeholder : this.placeholder || ''
15942             
15943         };
15944           if (this.weight) { // Validity check?
15945             cfg.cls += " radio-" + this.weight;
15946         }
15947         if (this.disabled) {
15948             input.disabled=true;
15949         }
15950         
15951         if(this.checked){
15952             input.checked = this.checked;
15953         }
15954         
15955         if (this.name) {
15956             input.name = this.name;
15957         }
15958         
15959         if (this.size) {
15960             input.cls += ' input-' + this.size;
15961         }
15962         
15963         var settings=this;
15964         ['xs','sm','md','lg'].map(function(size){
15965             if (settings[size]) {
15966                 cfg.cls += ' col-' + size + '-' + settings[size];
15967             }
15968         });
15969         
15970         var inputblock = input;
15971         
15972         if (this.before || this.after) {
15973             
15974             inputblock = {
15975                 cls : 'input-group',
15976                 cn :  [] 
15977             };
15978             if (this.before) {
15979                 inputblock.cn.push({
15980                     tag :'span',
15981                     cls : 'input-group-addon',
15982                     html : this.before
15983                 });
15984             }
15985             inputblock.cn.push(input);
15986             if (this.after) {
15987                 inputblock.cn.push({
15988                     tag :'span',
15989                     cls : 'input-group-addon',
15990                     html : this.after
15991                 });
15992             }
15993             
15994         };
15995         
15996         if (align ==='left' && this.fieldLabel.length) {
15997                 Roo.log("left and has label");
15998                 cfg.cn = [
15999                     
16000                     {
16001                         tag: 'label',
16002                         'for' :  id,
16003                         cls : 'control-label col-md-' + this.labelWidth,
16004                         html : this.fieldLabel
16005                         
16006                     },
16007                     {
16008                         cls : "col-md-" + (12 - this.labelWidth), 
16009                         cn: [
16010                             inputblock
16011                         ]
16012                     }
16013                     
16014                 ];
16015         } else if ( this.fieldLabel.length) {
16016                 Roo.log(" label");
16017                  cfg.cn = [
16018                    
16019                     {
16020                         tag: 'label',
16021                         'for': id,
16022                         cls: 'control-label box-input-label',
16023                         //cls : 'input-group-addon',
16024                         html : this.fieldLabel
16025                         
16026                     },
16027                     
16028                     inputblock
16029                     
16030                 ];
16031
16032         } else {
16033             
16034                    Roo.log(" no label && no align");
16035                 cfg.cn = [
16036                     
16037                         inputblock
16038                     
16039                 ];
16040                 
16041                 
16042         };
16043         
16044         if(this.boxLabel){
16045             cfg.cn.push({
16046                 tag: 'label',
16047                 'for': id,
16048                 cls: 'box-label',
16049                 html: this.boxLabel
16050             })
16051         }
16052         
16053         return cfg;
16054         
16055     },
16056     inputEl: function ()
16057     {
16058         return this.el.select('input.roo-radio',true).first();
16059     },
16060     onClick : function()
16061     {   
16062         this.setChecked(true);
16063     },
16064     
16065     setChecked : function(state,suppressEvent)
16066     {
16067         if(state){
16068             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
16069                 v.dom.checked = false;
16070             });
16071         }
16072         
16073         this.checked = state;
16074         this.inputEl().dom.checked = state;
16075         
16076         if(suppressEvent !== true){
16077             this.fireEvent('check', this, state);
16078         }
16079         
16080         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
16081         
16082     },
16083     
16084     getGroupValue : function()
16085     {
16086         var value = ''
16087         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
16088             if(v.dom.checked == true){
16089                 value = v.dom.value;
16090             }
16091         });
16092         
16093         return value;
16094     },
16095     
16096     /**
16097      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
16098      * @return {Mixed} value The field value
16099      */
16100     getValue : function(){
16101         return this.getGroupValue();
16102     }
16103     
16104 });
16105
16106  
16107 //<script type="text/javascript">
16108
16109 /*
16110  * Based  Ext JS Library 1.1.1
16111  * Copyright(c) 2006-2007, Ext JS, LLC.
16112  * LGPL
16113  *
16114  */
16115  
16116 /**
16117  * @class Roo.HtmlEditorCore
16118  * @extends Roo.Component
16119  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
16120  *
16121  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
16122  */
16123
16124 Roo.HtmlEditorCore = function(config){
16125     
16126     
16127     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
16128     this.addEvents({
16129         /**
16130          * @event initialize
16131          * Fires when the editor is fully initialized (including the iframe)
16132          * @param {Roo.HtmlEditorCore} this
16133          */
16134         initialize: true,
16135         /**
16136          * @event activate
16137          * Fires when the editor is first receives the focus. Any insertion must wait
16138          * until after this event.
16139          * @param {Roo.HtmlEditorCore} this
16140          */
16141         activate: true,
16142          /**
16143          * @event beforesync
16144          * Fires before the textarea is updated with content from the editor iframe. Return false
16145          * to cancel the sync.
16146          * @param {Roo.HtmlEditorCore} this
16147          * @param {String} html
16148          */
16149         beforesync: true,
16150          /**
16151          * @event beforepush
16152          * Fires before the iframe editor is updated with content from the textarea. Return false
16153          * to cancel the push.
16154          * @param {Roo.HtmlEditorCore} this
16155          * @param {String} html
16156          */
16157         beforepush: true,
16158          /**
16159          * @event sync
16160          * Fires when the textarea is updated with content from the editor iframe.
16161          * @param {Roo.HtmlEditorCore} this
16162          * @param {String} html
16163          */
16164         sync: true,
16165          /**
16166          * @event push
16167          * Fires when the iframe editor is updated with content from the textarea.
16168          * @param {Roo.HtmlEditorCore} this
16169          * @param {String} html
16170          */
16171         push: true,
16172         
16173         /**
16174          * @event editorevent
16175          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
16176          * @param {Roo.HtmlEditorCore} this
16177          */
16178         editorevent: true
16179     });
16180      
16181 };
16182
16183
16184 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
16185
16186
16187      /**
16188      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
16189      */
16190     
16191     owner : false,
16192     
16193      /**
16194      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
16195      *                        Roo.resizable.
16196      */
16197     resizable : false,
16198      /**
16199      * @cfg {Number} height (in pixels)
16200      */   
16201     height: 300,
16202    /**
16203      * @cfg {Number} width (in pixels)
16204      */   
16205     width: 500,
16206     
16207     /**
16208      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
16209      * 
16210      */
16211     stylesheets: false,
16212     
16213     // id of frame..
16214     frameId: false,
16215     
16216     // private properties
16217     validationEvent : false,
16218     deferHeight: true,
16219     initialized : false,
16220     activated : false,
16221     sourceEditMode : false,
16222     onFocus : Roo.emptyFn,
16223     iframePad:3,
16224     hideMode:'offsets',
16225     
16226     clearUp: true,
16227     
16228      
16229     
16230
16231     /**
16232      * Protected method that will not generally be called directly. It
16233      * is called when the editor initializes the iframe with HTML contents. Override this method if you
16234      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
16235      */
16236     getDocMarkup : function(){
16237         // body styles..
16238         var st = '';
16239         Roo.log(this.stylesheets);
16240         
16241         // inherit styels from page...?? 
16242         if (this.stylesheets === false) {
16243             
16244             Roo.get(document.head).select('style').each(function(node) {
16245                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16246             });
16247             
16248             Roo.get(document.head).select('link').each(function(node) { 
16249                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16250             });
16251             
16252         } else if (!this.stylesheets.length) {
16253                 // simple..
16254                 st = '<style type="text/css">' +
16255                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16256                    '</style>';
16257         } else {
16258             Roo.each(this.stylesheets, function(s) {
16259                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
16260             });
16261             
16262         }
16263         
16264         st +=  '<style type="text/css">' +
16265             'IMG { cursor: pointer } ' +
16266         '</style>';
16267
16268         
16269         return '<html><head>' + st  +
16270             //<style type="text/css">' +
16271             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16272             //'</style>' +
16273             ' </head><body class="roo-htmleditor-body"></body></html>';
16274     },
16275
16276     // private
16277     onRender : function(ct, position)
16278     {
16279         var _t = this;
16280         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
16281         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
16282         
16283         
16284         this.el.dom.style.border = '0 none';
16285         this.el.dom.setAttribute('tabIndex', -1);
16286         this.el.addClass('x-hidden hide');
16287         
16288         
16289         
16290         if(Roo.isIE){ // fix IE 1px bogus margin
16291             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
16292         }
16293        
16294         
16295         this.frameId = Roo.id();
16296         
16297          
16298         
16299         var iframe = this.owner.wrap.createChild({
16300             tag: 'iframe',
16301             cls: 'form-control', // bootstrap..
16302             id: this.frameId,
16303             name: this.frameId,
16304             frameBorder : 'no',
16305             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
16306         }, this.el
16307         );
16308         
16309         
16310         this.iframe = iframe.dom;
16311
16312          this.assignDocWin();
16313         
16314         this.doc.designMode = 'on';
16315        
16316         this.doc.open();
16317         this.doc.write(this.getDocMarkup());
16318         this.doc.close();
16319
16320         
16321         var task = { // must defer to wait for browser to be ready
16322             run : function(){
16323                 //console.log("run task?" + this.doc.readyState);
16324                 this.assignDocWin();
16325                 if(this.doc.body || this.doc.readyState == 'complete'){
16326                     try {
16327                         this.doc.designMode="on";
16328                     } catch (e) {
16329                         return;
16330                     }
16331                     Roo.TaskMgr.stop(task);
16332                     this.initEditor.defer(10, this);
16333                 }
16334             },
16335             interval : 10,
16336             duration: 10000,
16337             scope: this
16338         };
16339         Roo.TaskMgr.start(task);
16340
16341         
16342          
16343     },
16344
16345     // private
16346     onResize : function(w, h)
16347     {
16348          Roo.log('resize: ' +w + ',' + h );
16349         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
16350         if(!this.iframe){
16351             return;
16352         }
16353         if(typeof w == 'number'){
16354             
16355             this.iframe.style.width = w + 'px';
16356         }
16357         if(typeof h == 'number'){
16358             
16359             this.iframe.style.height = h + 'px';
16360             if(this.doc){
16361                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
16362             }
16363         }
16364         
16365     },
16366
16367     /**
16368      * Toggles the editor between standard and source edit mode.
16369      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
16370      */
16371     toggleSourceEdit : function(sourceEditMode){
16372         
16373         this.sourceEditMode = sourceEditMode === true;
16374         
16375         if(this.sourceEditMode){
16376  
16377             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
16378             
16379         }else{
16380             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
16381             //this.iframe.className = '';
16382             this.deferFocus();
16383         }
16384         //this.setSize(this.owner.wrap.getSize());
16385         //this.fireEvent('editmodechange', this, this.sourceEditMode);
16386     },
16387
16388     
16389   
16390
16391     /**
16392      * Protected method that will not generally be called directly. If you need/want
16393      * custom HTML cleanup, this is the method you should override.
16394      * @param {String} html The HTML to be cleaned
16395      * return {String} The cleaned HTML
16396      */
16397     cleanHtml : function(html){
16398         html = String(html);
16399         if(html.length > 5){
16400             if(Roo.isSafari){ // strip safari nonsense
16401                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
16402             }
16403         }
16404         if(html == '&nbsp;'){
16405             html = '';
16406         }
16407         return html;
16408     },
16409
16410     /**
16411      * HTML Editor -> Textarea
16412      * Protected method that will not generally be called directly. Syncs the contents
16413      * of the editor iframe with the textarea.
16414      */
16415     syncValue : function(){
16416         if(this.initialized){
16417             var bd = (this.doc.body || this.doc.documentElement);
16418             //this.cleanUpPaste(); -- this is done else where and causes havoc..
16419             var html = bd.innerHTML;
16420             if(Roo.isSafari){
16421                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
16422                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
16423                 if(m && m[1]){
16424                     html = '<div style="'+m[0]+'">' + html + '</div>';
16425                 }
16426             }
16427             html = this.cleanHtml(html);
16428             // fix up the special chars.. normaly like back quotes in word...
16429             // however we do not want to do this with chinese..
16430             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
16431                 var cc = b.charCodeAt();
16432                 if (
16433                     (cc >= 0x4E00 && cc < 0xA000 ) ||
16434                     (cc >= 0x3400 && cc < 0x4E00 ) ||
16435                     (cc >= 0xf900 && cc < 0xfb00 )
16436                 ) {
16437                         return b;
16438                 }
16439                 return "&#"+cc+";" 
16440             });
16441             if(this.owner.fireEvent('beforesync', this, html) !== false){
16442                 this.el.dom.value = html;
16443                 this.owner.fireEvent('sync', this, html);
16444             }
16445         }
16446     },
16447
16448     /**
16449      * Protected method that will not generally be called directly. Pushes the value of the textarea
16450      * into the iframe editor.
16451      */
16452     pushValue : function(){
16453         if(this.initialized){
16454             var v = this.el.dom.value.trim();
16455             
16456 //            if(v.length < 1){
16457 //                v = '&#160;';
16458 //            }
16459             
16460             if(this.owner.fireEvent('beforepush', this, v) !== false){
16461                 var d = (this.doc.body || this.doc.documentElement);
16462                 d.innerHTML = v;
16463                 this.cleanUpPaste();
16464                 this.el.dom.value = d.innerHTML;
16465                 this.owner.fireEvent('push', this, v);
16466             }
16467         }
16468     },
16469
16470     // private
16471     deferFocus : function(){
16472         this.focus.defer(10, this);
16473     },
16474
16475     // doc'ed in Field
16476     focus : function(){
16477         if(this.win && !this.sourceEditMode){
16478             this.win.focus();
16479         }else{
16480             this.el.focus();
16481         }
16482     },
16483     
16484     assignDocWin: function()
16485     {
16486         var iframe = this.iframe;
16487         
16488          if(Roo.isIE){
16489             this.doc = iframe.contentWindow.document;
16490             this.win = iframe.contentWindow;
16491         } else {
16492             if (!Roo.get(this.frameId)) {
16493                 return;
16494             }
16495             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
16496             this.win = Roo.get(this.frameId).dom.contentWindow;
16497         }
16498     },
16499     
16500     // private
16501     initEditor : function(){
16502         //console.log("INIT EDITOR");
16503         this.assignDocWin();
16504         
16505         
16506         
16507         this.doc.designMode="on";
16508         this.doc.open();
16509         this.doc.write(this.getDocMarkup());
16510         this.doc.close();
16511         
16512         var dbody = (this.doc.body || this.doc.documentElement);
16513         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
16514         // this copies styles from the containing element into thsi one..
16515         // not sure why we need all of this..
16516         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
16517         
16518         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
16519         //ss['background-attachment'] = 'fixed'; // w3c
16520         dbody.bgProperties = 'fixed'; // ie
16521         //Roo.DomHelper.applyStyles(dbody, ss);
16522         Roo.EventManager.on(this.doc, {
16523             //'mousedown': this.onEditorEvent,
16524             'mouseup': this.onEditorEvent,
16525             'dblclick': this.onEditorEvent,
16526             'click': this.onEditorEvent,
16527             'keyup': this.onEditorEvent,
16528             buffer:100,
16529             scope: this
16530         });
16531         if(Roo.isGecko){
16532             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
16533         }
16534         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
16535             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
16536         }
16537         this.initialized = true;
16538
16539         this.owner.fireEvent('initialize', this);
16540         this.pushValue();
16541     },
16542
16543     // private
16544     onDestroy : function(){
16545         
16546         
16547         
16548         if(this.rendered){
16549             
16550             //for (var i =0; i < this.toolbars.length;i++) {
16551             //    // fixme - ask toolbars for heights?
16552             //    this.toolbars[i].onDestroy();
16553            // }
16554             
16555             //this.wrap.dom.innerHTML = '';
16556             //this.wrap.remove();
16557         }
16558     },
16559
16560     // private
16561     onFirstFocus : function(){
16562         
16563         this.assignDocWin();
16564         
16565         
16566         this.activated = true;
16567          
16568     
16569         if(Roo.isGecko){ // prevent silly gecko errors
16570             this.win.focus();
16571             var s = this.win.getSelection();
16572             if(!s.focusNode || s.focusNode.nodeType != 3){
16573                 var r = s.getRangeAt(0);
16574                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
16575                 r.collapse(true);
16576                 this.deferFocus();
16577             }
16578             try{
16579                 this.execCmd('useCSS', true);
16580                 this.execCmd('styleWithCSS', false);
16581             }catch(e){}
16582         }
16583         this.owner.fireEvent('activate', this);
16584     },
16585
16586     // private
16587     adjustFont: function(btn){
16588         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
16589         //if(Roo.isSafari){ // safari
16590         //    adjust *= 2;
16591        // }
16592         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
16593         if(Roo.isSafari){ // safari
16594             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
16595             v =  (v < 10) ? 10 : v;
16596             v =  (v > 48) ? 48 : v;
16597             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
16598             
16599         }
16600         
16601         
16602         v = Math.max(1, v+adjust);
16603         
16604         this.execCmd('FontSize', v  );
16605     },
16606
16607     onEditorEvent : function(e){
16608         this.owner.fireEvent('editorevent', this, e);
16609       //  this.updateToolbar();
16610         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
16611     },
16612
16613     insertTag : function(tg)
16614     {
16615         // could be a bit smarter... -> wrap the current selected tRoo..
16616         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
16617             
16618             range = this.createRange(this.getSelection());
16619             var wrappingNode = this.doc.createElement(tg.toLowerCase());
16620             wrappingNode.appendChild(range.extractContents());
16621             range.insertNode(wrappingNode);
16622
16623             return;
16624             
16625             
16626             
16627         }
16628         this.execCmd("formatblock",   tg);
16629         
16630     },
16631     
16632     insertText : function(txt)
16633     {
16634         
16635         
16636         var range = this.createRange();
16637         range.deleteContents();
16638                //alert(Sender.getAttribute('label'));
16639                
16640         range.insertNode(this.doc.createTextNode(txt));
16641     } ,
16642     
16643      
16644
16645     /**
16646      * Executes a Midas editor command on the editor document and performs necessary focus and
16647      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
16648      * @param {String} cmd The Midas command
16649      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16650      */
16651     relayCmd : function(cmd, value){
16652         this.win.focus();
16653         this.execCmd(cmd, value);
16654         this.owner.fireEvent('editorevent', this);
16655         //this.updateToolbar();
16656         this.owner.deferFocus();
16657     },
16658
16659     /**
16660      * Executes a Midas editor command directly on the editor document.
16661      * For visual commands, you should use {@link #relayCmd} instead.
16662      * <b>This should only be called after the editor is initialized.</b>
16663      * @param {String} cmd The Midas command
16664      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16665      */
16666     execCmd : function(cmd, value){
16667         this.doc.execCommand(cmd, false, value === undefined ? null : value);
16668         this.syncValue();
16669     },
16670  
16671  
16672    
16673     /**
16674      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
16675      * to insert tRoo.
16676      * @param {String} text | dom node.. 
16677      */
16678     insertAtCursor : function(text)
16679     {
16680         
16681         
16682         
16683         if(!this.activated){
16684             return;
16685         }
16686         /*
16687         if(Roo.isIE){
16688             this.win.focus();
16689             var r = this.doc.selection.createRange();
16690             if(r){
16691                 r.collapse(true);
16692                 r.pasteHTML(text);
16693                 this.syncValue();
16694                 this.deferFocus();
16695             
16696             }
16697             return;
16698         }
16699         */
16700         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
16701             this.win.focus();
16702             
16703             
16704             // from jquery ui (MIT licenced)
16705             var range, node;
16706             var win = this.win;
16707             
16708             if (win.getSelection && win.getSelection().getRangeAt) {
16709                 range = win.getSelection().getRangeAt(0);
16710                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
16711                 range.insertNode(node);
16712             } else if (win.document.selection && win.document.selection.createRange) {
16713                 // no firefox support
16714                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
16715                 win.document.selection.createRange().pasteHTML(txt);
16716             } else {
16717                 // no firefox support
16718                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
16719                 this.execCmd('InsertHTML', txt);
16720             } 
16721             
16722             this.syncValue();
16723             
16724             this.deferFocus();
16725         }
16726     },
16727  // private
16728     mozKeyPress : function(e){
16729         if(e.ctrlKey){
16730             var c = e.getCharCode(), cmd;
16731           
16732             if(c > 0){
16733                 c = String.fromCharCode(c).toLowerCase();
16734                 switch(c){
16735                     case 'b':
16736                         cmd = 'bold';
16737                         break;
16738                     case 'i':
16739                         cmd = 'italic';
16740                         break;
16741                     
16742                     case 'u':
16743                         cmd = 'underline';
16744                         break;
16745                     
16746                     case 'v':
16747                         this.cleanUpPaste.defer(100, this);
16748                         return;
16749                         
16750                 }
16751                 if(cmd){
16752                     this.win.focus();
16753                     this.execCmd(cmd);
16754                     this.deferFocus();
16755                     e.preventDefault();
16756                 }
16757                 
16758             }
16759         }
16760     },
16761
16762     // private
16763     fixKeys : function(){ // load time branching for fastest keydown performance
16764         if(Roo.isIE){
16765             return function(e){
16766                 var k = e.getKey(), r;
16767                 if(k == e.TAB){
16768                     e.stopEvent();
16769                     r = this.doc.selection.createRange();
16770                     if(r){
16771                         r.collapse(true);
16772                         r.pasteHTML('&#160;&#160;&#160;&#160;');
16773                         this.deferFocus();
16774                     }
16775                     return;
16776                 }
16777                 
16778                 if(k == e.ENTER){
16779                     r = this.doc.selection.createRange();
16780                     if(r){
16781                         var target = r.parentElement();
16782                         if(!target || target.tagName.toLowerCase() != 'li'){
16783                             e.stopEvent();
16784                             r.pasteHTML('<br />');
16785                             r.collapse(false);
16786                             r.select();
16787                         }
16788                     }
16789                 }
16790                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
16791                     this.cleanUpPaste.defer(100, this);
16792                     return;
16793                 }
16794                 
16795                 
16796             };
16797         }else if(Roo.isOpera){
16798             return function(e){
16799                 var k = e.getKey();
16800                 if(k == e.TAB){
16801                     e.stopEvent();
16802                     this.win.focus();
16803                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
16804                     this.deferFocus();
16805                 }
16806                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
16807                     this.cleanUpPaste.defer(100, this);
16808                     return;
16809                 }
16810                 
16811             };
16812         }else if(Roo.isSafari){
16813             return function(e){
16814                 var k = e.getKey();
16815                 
16816                 if(k == e.TAB){
16817                     e.stopEvent();
16818                     this.execCmd('InsertText','\t');
16819                     this.deferFocus();
16820                     return;
16821                 }
16822                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
16823                     this.cleanUpPaste.defer(100, this);
16824                     return;
16825                 }
16826                 
16827              };
16828         }
16829     }(),
16830     
16831     getAllAncestors: function()
16832     {
16833         var p = this.getSelectedNode();
16834         var a = [];
16835         if (!p) {
16836             a.push(p); // push blank onto stack..
16837             p = this.getParentElement();
16838         }
16839         
16840         
16841         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
16842             a.push(p);
16843             p = p.parentNode;
16844         }
16845         a.push(this.doc.body);
16846         return a;
16847     },
16848     lastSel : false,
16849     lastSelNode : false,
16850     
16851     
16852     getSelection : function() 
16853     {
16854         this.assignDocWin();
16855         return Roo.isIE ? this.doc.selection : this.win.getSelection();
16856     },
16857     
16858     getSelectedNode: function() 
16859     {
16860         // this may only work on Gecko!!!
16861         
16862         // should we cache this!!!!
16863         
16864         
16865         
16866          
16867         var range = this.createRange(this.getSelection()).cloneRange();
16868         
16869         if (Roo.isIE) {
16870             var parent = range.parentElement();
16871             while (true) {
16872                 var testRange = range.duplicate();
16873                 testRange.moveToElementText(parent);
16874                 if (testRange.inRange(range)) {
16875                     break;
16876                 }
16877                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
16878                     break;
16879                 }
16880                 parent = parent.parentElement;
16881             }
16882             return parent;
16883         }
16884         
16885         // is ancestor a text element.
16886         var ac =  range.commonAncestorContainer;
16887         if (ac.nodeType == 3) {
16888             ac = ac.parentNode;
16889         }
16890         
16891         var ar = ac.childNodes;
16892          
16893         var nodes = [];
16894         var other_nodes = [];
16895         var has_other_nodes = false;
16896         for (var i=0;i<ar.length;i++) {
16897             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
16898                 continue;
16899             }
16900             // fullly contained node.
16901             
16902             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
16903                 nodes.push(ar[i]);
16904                 continue;
16905             }
16906             
16907             // probably selected..
16908             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
16909                 other_nodes.push(ar[i]);
16910                 continue;
16911             }
16912             // outer..
16913             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
16914                 continue;
16915             }
16916             
16917             
16918             has_other_nodes = true;
16919         }
16920         if (!nodes.length && other_nodes.length) {
16921             nodes= other_nodes;
16922         }
16923         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
16924             return false;
16925         }
16926         
16927         return nodes[0];
16928     },
16929     createRange: function(sel)
16930     {
16931         // this has strange effects when using with 
16932         // top toolbar - not sure if it's a great idea.
16933         //this.editor.contentWindow.focus();
16934         if (typeof sel != "undefined") {
16935             try {
16936                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
16937             } catch(e) {
16938                 return this.doc.createRange();
16939             }
16940         } else {
16941             return this.doc.createRange();
16942         }
16943     },
16944     getParentElement: function()
16945     {
16946         
16947         this.assignDocWin();
16948         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
16949         
16950         var range = this.createRange(sel);
16951          
16952         try {
16953             var p = range.commonAncestorContainer;
16954             while (p.nodeType == 3) { // text node
16955                 p = p.parentNode;
16956             }
16957             return p;
16958         } catch (e) {
16959             return null;
16960         }
16961     
16962     },
16963     /***
16964      *
16965      * Range intersection.. the hard stuff...
16966      *  '-1' = before
16967      *  '0' = hits..
16968      *  '1' = after.
16969      *         [ -- selected range --- ]
16970      *   [fail]                        [fail]
16971      *
16972      *    basically..
16973      *      if end is before start or  hits it. fail.
16974      *      if start is after end or hits it fail.
16975      *
16976      *   if either hits (but other is outside. - then it's not 
16977      *   
16978      *    
16979      **/
16980     
16981     
16982     // @see http://www.thismuchiknow.co.uk/?p=64.
16983     rangeIntersectsNode : function(range, node)
16984     {
16985         var nodeRange = node.ownerDocument.createRange();
16986         try {
16987             nodeRange.selectNode(node);
16988         } catch (e) {
16989             nodeRange.selectNodeContents(node);
16990         }
16991     
16992         var rangeStartRange = range.cloneRange();
16993         rangeStartRange.collapse(true);
16994     
16995         var rangeEndRange = range.cloneRange();
16996         rangeEndRange.collapse(false);
16997     
16998         var nodeStartRange = nodeRange.cloneRange();
16999         nodeStartRange.collapse(true);
17000     
17001         var nodeEndRange = nodeRange.cloneRange();
17002         nodeEndRange.collapse(false);
17003     
17004         return rangeStartRange.compareBoundaryPoints(
17005                  Range.START_TO_START, nodeEndRange) == -1 &&
17006                rangeEndRange.compareBoundaryPoints(
17007                  Range.START_TO_START, nodeStartRange) == 1;
17008         
17009          
17010     },
17011     rangeCompareNode : function(range, node)
17012     {
17013         var nodeRange = node.ownerDocument.createRange();
17014         try {
17015             nodeRange.selectNode(node);
17016         } catch (e) {
17017             nodeRange.selectNodeContents(node);
17018         }
17019         
17020         
17021         range.collapse(true);
17022     
17023         nodeRange.collapse(true);
17024      
17025         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
17026         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
17027          
17028         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
17029         
17030         var nodeIsBefore   =  ss == 1;
17031         var nodeIsAfter    = ee == -1;
17032         
17033         if (nodeIsBefore && nodeIsAfter)
17034             return 0; // outer
17035         if (!nodeIsBefore && nodeIsAfter)
17036             return 1; //right trailed.
17037         
17038         if (nodeIsBefore && !nodeIsAfter)
17039             return 2;  // left trailed.
17040         // fully contined.
17041         return 3;
17042     },
17043
17044     // private? - in a new class?
17045     cleanUpPaste :  function()
17046     {
17047         // cleans up the whole document..
17048         Roo.log('cleanuppaste');
17049         
17050         this.cleanUpChildren(this.doc.body);
17051         var clean = this.cleanWordChars(this.doc.body.innerHTML);
17052         if (clean != this.doc.body.innerHTML) {
17053             this.doc.body.innerHTML = clean;
17054         }
17055         
17056     },
17057     
17058     cleanWordChars : function(input) {// change the chars to hex code
17059         var he = Roo.HtmlEditorCore;
17060         
17061         var output = input;
17062         Roo.each(he.swapCodes, function(sw) { 
17063             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
17064             
17065             output = output.replace(swapper, sw[1]);
17066         });
17067         
17068         return output;
17069     },
17070     
17071     
17072     cleanUpChildren : function (n)
17073     {
17074         if (!n.childNodes.length) {
17075             return;
17076         }
17077         for (var i = n.childNodes.length-1; i > -1 ; i--) {
17078            this.cleanUpChild(n.childNodes[i]);
17079         }
17080     },
17081     
17082     
17083         
17084     
17085     cleanUpChild : function (node)
17086     {
17087         var ed = this;
17088         //console.log(node);
17089         if (node.nodeName == "#text") {
17090             // clean up silly Windows -- stuff?
17091             return; 
17092         }
17093         if (node.nodeName == "#comment") {
17094             node.parentNode.removeChild(node);
17095             // clean up silly Windows -- stuff?
17096             return; 
17097         }
17098         
17099         if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1 && this.clearUp) {
17100             // remove node.
17101             node.parentNode.removeChild(node);
17102             return;
17103             
17104         }
17105         
17106         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
17107         
17108         // remove <a name=....> as rendering on yahoo mailer is borked with this.
17109         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
17110         
17111         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
17112         //    remove_keep_children = true;
17113         //}
17114         
17115         if (remove_keep_children) {
17116             this.cleanUpChildren(node);
17117             // inserts everything just before this node...
17118             while (node.childNodes.length) {
17119                 var cn = node.childNodes[0];
17120                 node.removeChild(cn);
17121                 node.parentNode.insertBefore(cn, node);
17122             }
17123             node.parentNode.removeChild(node);
17124             return;
17125         }
17126         
17127         if (!node.attributes || !node.attributes.length) {
17128             this.cleanUpChildren(node);
17129             return;
17130         }
17131         
17132         function cleanAttr(n,v)
17133         {
17134             
17135             if (v.match(/^\./) || v.match(/^\//)) {
17136                 return;
17137             }
17138             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
17139                 return;
17140             }
17141             if (v.match(/^#/)) {
17142                 return;
17143             }
17144 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
17145             node.removeAttribute(n);
17146             
17147         }
17148         
17149         function cleanStyle(n,v)
17150         {
17151             if (v.match(/expression/)) { //XSS?? should we even bother..
17152                 node.removeAttribute(n);
17153                 return;
17154             }
17155             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
17156             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
17157             
17158             
17159             var parts = v.split(/;/);
17160             var clean = [];
17161             
17162             Roo.each(parts, function(p) {
17163                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
17164                 if (!p.length) {
17165                     return true;
17166                 }
17167                 var l = p.split(':').shift().replace(/\s+/g,'');
17168                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
17169                 
17170                 if ( cblack.indexOf(l) > -1) {
17171 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17172                     //node.removeAttribute(n);
17173                     return true;
17174                 }
17175                 //Roo.log()
17176                 // only allow 'c whitelisted system attributes'
17177                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
17178 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17179                     //node.removeAttribute(n);
17180                     return true;
17181                 }
17182                 
17183                 
17184                  
17185                 
17186                 clean.push(p);
17187                 return true;
17188             });
17189             if (clean.length) { 
17190                 node.setAttribute(n, clean.join(';'));
17191             } else {
17192                 node.removeAttribute(n);
17193             }
17194             
17195         }
17196         
17197         
17198         for (var i = node.attributes.length-1; i > -1 ; i--) {
17199             var a = node.attributes[i];
17200             //console.log(a);
17201             
17202             if (a.name.toLowerCase().substr(0,2)=='on')  {
17203                 node.removeAttribute(a.name);
17204                 continue;
17205             }
17206             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
17207                 node.removeAttribute(a.name);
17208                 continue;
17209             }
17210             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
17211                 cleanAttr(a.name,a.value); // fixme..
17212                 continue;
17213             }
17214             if (a.name == 'style') {
17215                 cleanStyle(a.name,a.value);
17216                 continue;
17217             }
17218             /// clean up MS crap..
17219             // tecnically this should be a list of valid class'es..
17220             
17221             
17222             if (a.name == 'class') {
17223                 if (a.value.match(/^Mso/)) {
17224                     node.className = '';
17225                 }
17226                 
17227                 if (a.value.match(/body/)) {
17228                     node.className = '';
17229                 }
17230                 continue;
17231             }
17232             
17233             // style cleanup!?
17234             // class cleanup?
17235             
17236         }
17237         
17238         
17239         this.cleanUpChildren(node);
17240         
17241         
17242     },
17243     /**
17244      * Clean up MS wordisms...
17245      */
17246     cleanWord : function(node)
17247     {
17248         var _t = this;
17249         var cleanWordChildren = function()
17250         {
17251             if (!node.childNodes.length) {
17252                 return;
17253             }
17254             for (var i = node.childNodes.length-1; i > -1 ; i--) {
17255                _t.cleanWord(node.childNodes[i]);
17256             }
17257         }
17258         
17259         
17260         if (!node) {
17261             this.cleanWord(this.doc.body);
17262             return;
17263         }
17264         if (node.nodeName == "#text") {
17265             // clean up silly Windows -- stuff?
17266             return; 
17267         }
17268         if (node.nodeName == "#comment") {
17269             node.parentNode.removeChild(node);
17270             // clean up silly Windows -- stuff?
17271             return; 
17272         }
17273         
17274         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
17275             node.parentNode.removeChild(node);
17276             return;
17277         }
17278         
17279         // remove - but keep children..
17280         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
17281             while (node.childNodes.length) {
17282                 var cn = node.childNodes[0];
17283                 node.removeChild(cn);
17284                 node.parentNode.insertBefore(cn, node);
17285             }
17286             node.parentNode.removeChild(node);
17287             cleanWordChildren();
17288             return;
17289         }
17290         // clean styles
17291         if (node.className.length) {
17292             
17293             var cn = node.className.split(/\W+/);
17294             var cna = [];
17295             Roo.each(cn, function(cls) {
17296                 if (cls.match(/Mso[a-zA-Z]+/)) {
17297                     return;
17298                 }
17299                 cna.push(cls);
17300             });
17301             node.className = cna.length ? cna.join(' ') : '';
17302             if (!cna.length) {
17303                 node.removeAttribute("class");
17304             }
17305         }
17306         
17307         if (node.hasAttribute("lang")) {
17308             node.removeAttribute("lang");
17309         }
17310         
17311         if (node.hasAttribute("style")) {
17312             
17313             var styles = node.getAttribute("style").split(";");
17314             var nstyle = [];
17315             Roo.each(styles, function(s) {
17316                 if (!s.match(/:/)) {
17317                     return;
17318                 }
17319                 var kv = s.split(":");
17320                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
17321                     return;
17322                 }
17323                 // what ever is left... we allow.
17324                 nstyle.push(s);
17325             });
17326             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
17327             if (!nstyle.length) {
17328                 node.removeAttribute('style');
17329             }
17330         }
17331         
17332         cleanWordChildren();
17333         
17334         
17335     },
17336     domToHTML : function(currentElement, depth, nopadtext) {
17337         
17338             depth = depth || 0;
17339             nopadtext = nopadtext || false;
17340         
17341             if (!currentElement) {
17342                 return this.domToHTML(this.doc.body);
17343             }
17344             
17345             //Roo.log(currentElement);
17346             var j;
17347             var allText = false;
17348             var nodeName = currentElement.nodeName;
17349             var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
17350             
17351             if  (nodeName == '#text') {
17352                 return currentElement.nodeValue;
17353             }
17354             
17355             
17356             var ret = '';
17357             if (nodeName != 'BODY') {
17358                  
17359                 var i = 0;
17360                 // Prints the node tagName, such as <A>, <IMG>, etc
17361                 if (tagName) {
17362                     var attr = [];
17363                     for(i = 0; i < currentElement.attributes.length;i++) {
17364                         // quoting?
17365                         var aname = currentElement.attributes.item(i).name;
17366                         if (!currentElement.attributes.item(i).value.length) {
17367                             continue;
17368                         }
17369                         attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
17370                     }
17371                     
17372                     ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
17373                 } 
17374                 else {
17375                     
17376                     // eack
17377                 }
17378             } else {
17379                 tagName = false;
17380             }
17381             if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
17382                 return ret;
17383             }
17384             if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
17385                 nopadtext = true;
17386             }
17387             
17388             
17389             // Traverse the tree
17390             i = 0;
17391             var currentElementChild = currentElement.childNodes.item(i);
17392             var allText = true;
17393             var innerHTML  = '';
17394             lastnode = '';
17395             while (currentElementChild) {
17396                 // Formatting code (indent the tree so it looks nice on the screen)
17397                 var nopad = nopadtext;
17398                 if (lastnode == 'SPAN') {
17399                     nopad  = true;
17400                 }
17401                 // text
17402                 if  (currentElementChild.nodeName == '#text') {
17403                     var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
17404                     if (!nopad && toadd.length > 80) {
17405                         innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
17406                     }
17407                     innerHTML  += toadd;
17408                     
17409                     i++;
17410                     currentElementChild = currentElement.childNodes.item(i);
17411                     lastNode = '';
17412                     continue;
17413                 }
17414                 allText = false;
17415                 
17416                 innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
17417                     
17418                 // Recursively traverse the tree structure of the child node
17419                 innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
17420                 lastnode = currentElementChild.nodeName;
17421                 i++;
17422                 currentElementChild=currentElement.childNodes.item(i);
17423             }
17424             
17425             ret += innerHTML;
17426             
17427             if (!allText) {
17428                     // The remaining code is mostly for formatting the tree
17429                 ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
17430             }
17431             
17432             
17433             if (tagName) {
17434                 ret+= "</"+tagName+">";
17435             }
17436             return ret;
17437             
17438         }
17439     
17440     // hide stuff that is not compatible
17441     /**
17442      * @event blur
17443      * @hide
17444      */
17445     /**
17446      * @event change
17447      * @hide
17448      */
17449     /**
17450      * @event focus
17451      * @hide
17452      */
17453     /**
17454      * @event specialkey
17455      * @hide
17456      */
17457     /**
17458      * @cfg {String} fieldClass @hide
17459      */
17460     /**
17461      * @cfg {String} focusClass @hide
17462      */
17463     /**
17464      * @cfg {String} autoCreate @hide
17465      */
17466     /**
17467      * @cfg {String} inputType @hide
17468      */
17469     /**
17470      * @cfg {String} invalidClass @hide
17471      */
17472     /**
17473      * @cfg {String} invalidText @hide
17474      */
17475     /**
17476      * @cfg {String} msgFx @hide
17477      */
17478     /**
17479      * @cfg {String} validateOnBlur @hide
17480      */
17481 });
17482
17483 Roo.HtmlEditorCore.white = [
17484         'area', 'br', 'img', 'input', 'hr', 'wbr',
17485         
17486        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
17487        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
17488        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
17489        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
17490        'table',   'ul',         'xmp', 
17491        
17492        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
17493       'thead',   'tr', 
17494      
17495       'dir', 'menu', 'ol', 'ul', 'dl',
17496        
17497       'embed',  'object'
17498 ];
17499
17500
17501 Roo.HtmlEditorCore.black = [
17502     //    'embed',  'object', // enable - backend responsiblity to clean thiese
17503         'applet', // 
17504         'base',   'basefont', 'bgsound', 'blink',  'body', 
17505         'frame',  'frameset', 'head',    'html',   'ilayer', 
17506         'iframe', 'layer',  'link',     'meta',    'object',   
17507         'script', 'style' ,'title',  'xml' // clean later..
17508 ];
17509 Roo.HtmlEditorCore.clean = [
17510     'script', 'style', 'title', 'xml'
17511 ];
17512 Roo.HtmlEditorCore.remove = [
17513     'font'
17514 ];
17515 // attributes..
17516
17517 Roo.HtmlEditorCore.ablack = [
17518     'on'
17519 ];
17520     
17521 Roo.HtmlEditorCore.aclean = [ 
17522     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
17523 ];
17524
17525 // protocols..
17526 Roo.HtmlEditorCore.pwhite= [
17527         'http',  'https',  'mailto'
17528 ];
17529
17530 // white listed style attributes.
17531 Roo.HtmlEditorCore.cwhite= [
17532       //  'text-align', /// default is to allow most things..
17533       
17534          
17535 //        'font-size'//??
17536 ];
17537
17538 // black listed style attributes.
17539 Roo.HtmlEditorCore.cblack= [
17540       //  'font-size' -- this can be set by the project 
17541 ];
17542
17543
17544 Roo.HtmlEditorCore.swapCodes   =[ 
17545     [    8211, "--" ], 
17546     [    8212, "--" ], 
17547     [    8216,  "'" ],  
17548     [    8217, "'" ],  
17549     [    8220, '"' ],  
17550     [    8221, '"' ],  
17551     [    8226, "*" ],  
17552     [    8230, "..." ]
17553 ]; 
17554
17555     /*
17556  * - LGPL
17557  *
17558  * HtmlEditor
17559  * 
17560  */
17561
17562 /**
17563  * @class Roo.bootstrap.HtmlEditor
17564  * @extends Roo.bootstrap.TextArea
17565  * Bootstrap HtmlEditor class
17566
17567  * @constructor
17568  * Create a new HtmlEditor
17569  * @param {Object} config The config object
17570  */
17571
17572 Roo.bootstrap.HtmlEditor = function(config){
17573     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
17574     if (!this.toolbars) {
17575         this.toolbars = [];
17576     }
17577     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
17578     this.addEvents({
17579             /**
17580              * @event initialize
17581              * Fires when the editor is fully initialized (including the iframe)
17582              * @param {HtmlEditor} this
17583              */
17584             initialize: true,
17585             /**
17586              * @event activate
17587              * Fires when the editor is first receives the focus. Any insertion must wait
17588              * until after this event.
17589              * @param {HtmlEditor} this
17590              */
17591             activate: true,
17592              /**
17593              * @event beforesync
17594              * Fires before the textarea is updated with content from the editor iframe. Return false
17595              * to cancel the sync.
17596              * @param {HtmlEditor} this
17597              * @param {String} html
17598              */
17599             beforesync: true,
17600              /**
17601              * @event beforepush
17602              * Fires before the iframe editor is updated with content from the textarea. Return false
17603              * to cancel the push.
17604              * @param {HtmlEditor} this
17605              * @param {String} html
17606              */
17607             beforepush: true,
17608              /**
17609              * @event sync
17610              * Fires when the textarea is updated with content from the editor iframe.
17611              * @param {HtmlEditor} this
17612              * @param {String} html
17613              */
17614             sync: true,
17615              /**
17616              * @event push
17617              * Fires when the iframe editor is updated with content from the textarea.
17618              * @param {HtmlEditor} this
17619              * @param {String} html
17620              */
17621             push: true,
17622              /**
17623              * @event editmodechange
17624              * Fires when the editor switches edit modes
17625              * @param {HtmlEditor} this
17626              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
17627              */
17628             editmodechange: true,
17629             /**
17630              * @event editorevent
17631              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
17632              * @param {HtmlEditor} this
17633              */
17634             editorevent: true,
17635             /**
17636              * @event firstfocus
17637              * Fires when on first focus - needed by toolbars..
17638              * @param {HtmlEditor} this
17639              */
17640             firstfocus: true,
17641             /**
17642              * @event autosave
17643              * Auto save the htmlEditor value as a file into Events
17644              * @param {HtmlEditor} this
17645              */
17646             autosave: true,
17647             /**
17648              * @event savedpreview
17649              * preview the saved version of htmlEditor
17650              * @param {HtmlEditor} this
17651              */
17652             savedpreview: true
17653         });
17654 };
17655
17656
17657 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
17658     
17659     
17660       /**
17661      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
17662      */
17663     toolbars : false,
17664    
17665      /**
17666      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
17667      *                        Roo.resizable.
17668      */
17669     resizable : false,
17670      /**
17671      * @cfg {Number} height (in pixels)
17672      */   
17673     height: 300,
17674    /**
17675      * @cfg {Number} width (in pixels)
17676      */   
17677     width: false,
17678     
17679     /**
17680      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
17681      * 
17682      */
17683     stylesheets: false,
17684     
17685     // id of frame..
17686     frameId: false,
17687     
17688     // private properties
17689     validationEvent : false,
17690     deferHeight: true,
17691     initialized : false,
17692     activated : false,
17693     
17694     onFocus : Roo.emptyFn,
17695     iframePad:3,
17696     hideMode:'offsets',
17697     
17698     
17699     tbContainer : false,
17700     
17701     toolbarContainer :function() {
17702         return this.wrap.select('.x-html-editor-tb',true).first();
17703     },
17704
17705     /**
17706      * Protected method that will not generally be called directly. It
17707      * is called when the editor creates its toolbar. Override this method if you need to
17708      * add custom toolbar buttons.
17709      * @param {HtmlEditor} editor
17710      */
17711     createToolbar : function(){
17712         
17713         Roo.log("create toolbars");
17714         
17715         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
17716         this.toolbars[0].render(this.toolbarContainer());
17717         
17718         return;
17719         
17720 //        if (!editor.toolbars || !editor.toolbars.length) {
17721 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
17722 //        }
17723 //        
17724 //        for (var i =0 ; i < editor.toolbars.length;i++) {
17725 //            editor.toolbars[i] = Roo.factory(
17726 //                    typeof(editor.toolbars[i]) == 'string' ?
17727 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
17728 //                Roo.bootstrap.HtmlEditor);
17729 //            editor.toolbars[i].init(editor);
17730 //        }
17731     },
17732
17733      
17734     // private
17735     onRender : function(ct, position)
17736     {
17737        // Roo.log("Call onRender: " + this.xtype);
17738         var _t = this;
17739         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
17740       
17741         this.wrap = this.inputEl().wrap({
17742             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
17743         });
17744         
17745         this.editorcore.onRender(ct, position);
17746          
17747         if (this.resizable) {
17748             this.resizeEl = new Roo.Resizable(this.wrap, {
17749                 pinned : true,
17750                 wrap: true,
17751                 dynamic : true,
17752                 minHeight : this.height,
17753                 height: this.height,
17754                 handles : this.resizable,
17755                 width: this.width,
17756                 listeners : {
17757                     resize : function(r, w, h) {
17758                         _t.onResize(w,h); // -something
17759                     }
17760                 }
17761             });
17762             
17763         }
17764         this.createToolbar(this);
17765        
17766         
17767         if(!this.width && this.resizable){
17768             this.setSize(this.wrap.getSize());
17769         }
17770         if (this.resizeEl) {
17771             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
17772             // should trigger onReize..
17773         }
17774         
17775     },
17776
17777     // private
17778     onResize : function(w, h)
17779     {
17780         Roo.log('resize: ' +w + ',' + h );
17781         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
17782         var ew = false;
17783         var eh = false;
17784         
17785         if(this.inputEl() ){
17786             if(typeof w == 'number'){
17787                 var aw = w - this.wrap.getFrameWidth('lr');
17788                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
17789                 ew = aw;
17790             }
17791             if(typeof h == 'number'){
17792                  var tbh = -11;  // fixme it needs to tool bar size!
17793                 for (var i =0; i < this.toolbars.length;i++) {
17794                     // fixme - ask toolbars for heights?
17795                     tbh += this.toolbars[i].el.getHeight();
17796                     //if (this.toolbars[i].footer) {
17797                     //    tbh += this.toolbars[i].footer.el.getHeight();
17798                     //}
17799                 }
17800               
17801                 
17802                 
17803                 
17804                 
17805                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
17806                 ah -= 5; // knock a few pixes off for look..
17807                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
17808                 var eh = ah;
17809             }
17810         }
17811         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
17812         this.editorcore.onResize(ew,eh);
17813         
17814     },
17815
17816     /**
17817      * Toggles the editor between standard and source edit mode.
17818      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
17819      */
17820     toggleSourceEdit : function(sourceEditMode)
17821     {
17822         this.editorcore.toggleSourceEdit(sourceEditMode);
17823         
17824         if(this.editorcore.sourceEditMode){
17825             Roo.log('editor - showing textarea');
17826             
17827 //            Roo.log('in');
17828 //            Roo.log(this.syncValue());
17829             this.syncValue();
17830             this.inputEl().removeClass(['hide', 'x-hidden']);
17831             this.inputEl().dom.removeAttribute('tabIndex');
17832             this.inputEl().focus();
17833         }else{
17834             Roo.log('editor - hiding textarea');
17835 //            Roo.log('out')
17836 //            Roo.log(this.pushValue()); 
17837             this.pushValue();
17838             
17839             this.inputEl().addClass(['hide', 'x-hidden']);
17840             this.inputEl().dom.setAttribute('tabIndex', -1);
17841             //this.deferFocus();
17842         }
17843          
17844         if(this.resizable){
17845             this.setSize(this.wrap.getSize());
17846         }
17847         
17848         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
17849     },
17850  
17851     // private (for BoxComponent)
17852     adjustSize : Roo.BoxComponent.prototype.adjustSize,
17853
17854     // private (for BoxComponent)
17855     getResizeEl : function(){
17856         return this.wrap;
17857     },
17858
17859     // private (for BoxComponent)
17860     getPositionEl : function(){
17861         return this.wrap;
17862     },
17863
17864     // private
17865     initEvents : function(){
17866         this.originalValue = this.getValue();
17867     },
17868
17869 //    /**
17870 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
17871 //     * @method
17872 //     */
17873 //    markInvalid : Roo.emptyFn,
17874 //    /**
17875 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
17876 //     * @method
17877 //     */
17878 //    clearInvalid : Roo.emptyFn,
17879
17880     setValue : function(v){
17881         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
17882         this.editorcore.pushValue();
17883     },
17884
17885      
17886     // private
17887     deferFocus : function(){
17888         this.focus.defer(10, this);
17889     },
17890
17891     // doc'ed in Field
17892     focus : function(){
17893         this.editorcore.focus();
17894         
17895     },
17896       
17897
17898     // private
17899     onDestroy : function(){
17900         
17901         
17902         
17903         if(this.rendered){
17904             
17905             for (var i =0; i < this.toolbars.length;i++) {
17906                 // fixme - ask toolbars for heights?
17907                 this.toolbars[i].onDestroy();
17908             }
17909             
17910             this.wrap.dom.innerHTML = '';
17911             this.wrap.remove();
17912         }
17913     },
17914
17915     // private
17916     onFirstFocus : function(){
17917         //Roo.log("onFirstFocus");
17918         this.editorcore.onFirstFocus();
17919          for (var i =0; i < this.toolbars.length;i++) {
17920             this.toolbars[i].onFirstFocus();
17921         }
17922         
17923     },
17924     
17925     // private
17926     syncValue : function()
17927     {   
17928         this.editorcore.syncValue();
17929     },
17930     
17931     pushValue : function()
17932     {   
17933         this.editorcore.pushValue();
17934     }
17935      
17936     
17937     // hide stuff that is not compatible
17938     /**
17939      * @event blur
17940      * @hide
17941      */
17942     /**
17943      * @event change
17944      * @hide
17945      */
17946     /**
17947      * @event focus
17948      * @hide
17949      */
17950     /**
17951      * @event specialkey
17952      * @hide
17953      */
17954     /**
17955      * @cfg {String} fieldClass @hide
17956      */
17957     /**
17958      * @cfg {String} focusClass @hide
17959      */
17960     /**
17961      * @cfg {String} autoCreate @hide
17962      */
17963     /**
17964      * @cfg {String} inputType @hide
17965      */
17966     /**
17967      * @cfg {String} invalidClass @hide
17968      */
17969     /**
17970      * @cfg {String} invalidText @hide
17971      */
17972     /**
17973      * @cfg {String} msgFx @hide
17974      */
17975     /**
17976      * @cfg {String} validateOnBlur @hide
17977      */
17978 });
17979  
17980     
17981    
17982    
17983    
17984       
17985 Roo.namespace('Roo.bootstrap.htmleditor');
17986 /**
17987  * @class Roo.bootstrap.HtmlEditorToolbar1
17988  * Basic Toolbar
17989  * 
17990  * Usage:
17991  *
17992  new Roo.bootstrap.HtmlEditor({
17993     ....
17994     toolbars : [
17995         new Roo.bootstrap.HtmlEditorToolbar1({
17996             disable : { fonts: 1 , format: 1, ..., ... , ...],
17997             btns : [ .... ]
17998         })
17999     }
18000      
18001  * 
18002  * @cfg {Object} disable List of elements to disable..
18003  * @cfg {Array} btns List of additional buttons.
18004  * 
18005  * 
18006  * NEEDS Extra CSS? 
18007  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
18008  */
18009  
18010 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
18011 {
18012     
18013     Roo.apply(this, config);
18014     
18015     // default disabled, based on 'good practice'..
18016     this.disable = this.disable || {};
18017     Roo.applyIf(this.disable, {
18018         fontSize : true,
18019         colors : true,
18020         specialElements : true
18021     });
18022     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
18023     
18024     this.editor = config.editor;
18025     this.editorcore = config.editor.editorcore;
18026     
18027     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
18028     
18029     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
18030     // dont call parent... till later.
18031 }
18032 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
18033      
18034     bar : true,
18035     
18036     editor : false,
18037     editorcore : false,
18038     
18039     
18040     formats : [
18041         "p" ,  
18042         "h1","h2","h3","h4","h5","h6", 
18043         "pre", "code", 
18044         "abbr", "acronym", "address", "cite", "samp", "var",
18045         'div','span'
18046     ],
18047     
18048     onRender : function(ct, position)
18049     {
18050        // Roo.log("Call onRender: " + this.xtype);
18051         
18052        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
18053        Roo.log(this.el);
18054        this.el.dom.style.marginBottom = '0';
18055        var _this = this;
18056        var editorcore = this.editorcore;
18057        var editor= this.editor;
18058        
18059        var children = [];
18060        var btn = function(id,cmd , toggle, handler){
18061        
18062             var  event = toggle ? 'toggle' : 'click';
18063        
18064             var a = {
18065                 size : 'sm',
18066                 xtype: 'Button',
18067                 xns: Roo.bootstrap,
18068                 glyphicon : id,
18069                 cmd : id || cmd,
18070                 enableToggle:toggle !== false,
18071                 //html : 'submit'
18072                 pressed : toggle ? false : null,
18073                 listeners : {}
18074             }
18075             a.listeners[toggle ? 'toggle' : 'click'] = function() {
18076                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
18077             }
18078             children.push(a);
18079             return a;
18080        }
18081         
18082         var style = {
18083                 xtype: 'Button',
18084                 size : 'sm',
18085                 xns: Roo.bootstrap,
18086                 glyphicon : 'font',
18087                 //html : 'submit'
18088                 menu : {
18089                     xtype: 'Menu',
18090                     xns: Roo.bootstrap,
18091                     items:  []
18092                 }
18093         };
18094         Roo.each(this.formats, function(f) {
18095             style.menu.items.push({
18096                 xtype :'MenuItem',
18097                 xns: Roo.bootstrap,
18098                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
18099                 tagname : f,
18100                 listeners : {
18101                     click : function()
18102                     {
18103                         editorcore.insertTag(this.tagname);
18104                         editor.focus();
18105                     }
18106                 }
18107                 
18108             });
18109         });
18110          children.push(style);   
18111             
18112             
18113         btn('bold',false,true);
18114         btn('italic',false,true);
18115         btn('align-left', 'justifyleft',true);
18116         btn('align-center', 'justifycenter',true);
18117         btn('align-right' , 'justifyright',true);
18118         btn('link', false, false, function(btn) {
18119             //Roo.log("create link?");
18120             var url = prompt(this.createLinkText, this.defaultLinkValue);
18121             if(url && url != 'http:/'+'/'){
18122                 this.editorcore.relayCmd('createlink', url);
18123             }
18124         }),
18125         btn('list','insertunorderedlist',true);
18126         btn('pencil', false,true, function(btn){
18127                 Roo.log(this);
18128                 
18129                 this.toggleSourceEdit(btn.pressed);
18130         });
18131         /*
18132         var cog = {
18133                 xtype: 'Button',
18134                 size : 'sm',
18135                 xns: Roo.bootstrap,
18136                 glyphicon : 'cog',
18137                 //html : 'submit'
18138                 menu : {
18139                     xtype: 'Menu',
18140                     xns: Roo.bootstrap,
18141                     items:  []
18142                 }
18143         };
18144         
18145         cog.menu.items.push({
18146             xtype :'MenuItem',
18147             xns: Roo.bootstrap,
18148             html : Clean styles,
18149             tagname : f,
18150             listeners : {
18151                 click : function()
18152                 {
18153                     editorcore.insertTag(this.tagname);
18154                     editor.focus();
18155                 }
18156             }
18157             
18158         });
18159        */
18160         
18161          
18162        this.xtype = 'NavSimplebar';
18163         
18164         for(var i=0;i< children.length;i++) {
18165             
18166             this.buttons.add(this.addxtypeChild(children[i]));
18167             
18168         }
18169         
18170         editor.on('editorevent', this.updateToolbar, this);
18171     },
18172     onBtnClick : function(id)
18173     {
18174        this.editorcore.relayCmd(id);
18175        this.editorcore.focus();
18176     },
18177     
18178     /**
18179      * Protected method that will not generally be called directly. It triggers
18180      * a toolbar update by reading the markup state of the current selection in the editor.
18181      */
18182     updateToolbar: function(){
18183
18184         if(!this.editorcore.activated){
18185             this.editor.onFirstFocus(); // is this neeed?
18186             return;
18187         }
18188
18189         var btns = this.buttons; 
18190         var doc = this.editorcore.doc;
18191         btns.get('bold').setActive(doc.queryCommandState('bold'));
18192         btns.get('italic').setActive(doc.queryCommandState('italic'));
18193         //btns.get('underline').setActive(doc.queryCommandState('underline'));
18194         
18195         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
18196         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
18197         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
18198         
18199         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
18200         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
18201          /*
18202         
18203         var ans = this.editorcore.getAllAncestors();
18204         if (this.formatCombo) {
18205             
18206             
18207             var store = this.formatCombo.store;
18208             this.formatCombo.setValue("");
18209             for (var i =0; i < ans.length;i++) {
18210                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
18211                     // select it..
18212                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
18213                     break;
18214                 }
18215             }
18216         }
18217         
18218         
18219         
18220         // hides menus... - so this cant be on a menu...
18221         Roo.bootstrap.MenuMgr.hideAll();
18222         */
18223         Roo.bootstrap.MenuMgr.hideAll();
18224         //this.editorsyncValue();
18225     },
18226     onFirstFocus: function() {
18227         this.buttons.each(function(item){
18228            item.enable();
18229         });
18230     },
18231     toggleSourceEdit : function(sourceEditMode){
18232         
18233           
18234         if(sourceEditMode){
18235             Roo.log("disabling buttons");
18236            this.buttons.each( function(item){
18237                 if(item.cmd != 'pencil'){
18238                     item.disable();
18239                 }
18240             });
18241           
18242         }else{
18243             Roo.log("enabling buttons");
18244             if(this.editorcore.initialized){
18245                 this.buttons.each( function(item){
18246                     item.enable();
18247                 });
18248             }
18249             
18250         }
18251         Roo.log("calling toggole on editor");
18252         // tell the editor that it's been pressed..
18253         this.editor.toggleSourceEdit(sourceEditMode);
18254        
18255     }
18256 });
18257
18258
18259
18260
18261
18262 /**
18263  * @class Roo.bootstrap.Table.AbstractSelectionModel
18264  * @extends Roo.util.Observable
18265  * Abstract base class for grid SelectionModels.  It provides the interface that should be
18266  * implemented by descendant classes.  This class should not be directly instantiated.
18267  * @constructor
18268  */
18269 Roo.bootstrap.Table.AbstractSelectionModel = function(){
18270     this.locked = false;
18271     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
18272 };
18273
18274
18275 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
18276     /** @ignore Called by the grid automatically. Do not call directly. */
18277     init : function(grid){
18278         this.grid = grid;
18279         this.initEvents();
18280     },
18281
18282     /**
18283      * Locks the selections.
18284      */
18285     lock : function(){
18286         this.locked = true;
18287     },
18288
18289     /**
18290      * Unlocks the selections.
18291      */
18292     unlock : function(){
18293         this.locked = false;
18294     },
18295
18296     /**
18297      * Returns true if the selections are locked.
18298      * @return {Boolean}
18299      */
18300     isLocked : function(){
18301         return this.locked;
18302     }
18303 });
18304 /**
18305  * @extends Roo.bootstrap.Table.AbstractSelectionModel
18306  * @class Roo.bootstrap.Table.RowSelectionModel
18307  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
18308  * It supports multiple selections and keyboard selection/navigation. 
18309  * @constructor
18310  * @param {Object} config
18311  */
18312
18313 Roo.bootstrap.Table.RowSelectionModel = function(config){
18314     Roo.apply(this, config);
18315     this.selections = new Roo.util.MixedCollection(false, function(o){
18316         return o.id;
18317     });
18318
18319     this.last = false;
18320     this.lastActive = false;
18321
18322     this.addEvents({
18323         /**
18324              * @event selectionchange
18325              * Fires when the selection changes
18326              * @param {SelectionModel} this
18327              */
18328             "selectionchange" : true,
18329         /**
18330              * @event afterselectionchange
18331              * Fires after the selection changes (eg. by key press or clicking)
18332              * @param {SelectionModel} this
18333              */
18334             "afterselectionchange" : true,
18335         /**
18336              * @event beforerowselect
18337              * Fires when a row is selected being selected, return false to cancel.
18338              * @param {SelectionModel} this
18339              * @param {Number} rowIndex The selected index
18340              * @param {Boolean} keepExisting False if other selections will be cleared
18341              */
18342             "beforerowselect" : true,
18343         /**
18344              * @event rowselect
18345              * Fires when a row is selected.
18346              * @param {SelectionModel} this
18347              * @param {Number} rowIndex The selected index
18348              * @param {Roo.data.Record} r The record
18349              */
18350             "rowselect" : true,
18351         /**
18352              * @event rowdeselect
18353              * Fires when a row is deselected.
18354              * @param {SelectionModel} this
18355              * @param {Number} rowIndex The selected index
18356              */
18357         "rowdeselect" : true
18358     });
18359     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
18360     this.locked = false;
18361 };
18362
18363 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
18364     /**
18365      * @cfg {Boolean} singleSelect
18366      * True to allow selection of only one row at a time (defaults to false)
18367      */
18368     singleSelect : false,
18369
18370     // private
18371     initEvents : function(){
18372
18373         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
18374             this.grid.on("mousedown", this.handleMouseDown, this);
18375         }else{ // allow click to work like normal
18376             this.grid.on("rowclick", this.handleDragableRowClick, this);
18377         }
18378
18379         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
18380             "up" : function(e){
18381                 if(!e.shiftKey){
18382                     this.selectPrevious(e.shiftKey);
18383                 }else if(this.last !== false && this.lastActive !== false){
18384                     var last = this.last;
18385                     this.selectRange(this.last,  this.lastActive-1);
18386                     this.grid.getView().focusRow(this.lastActive);
18387                     if(last !== false){
18388                         this.last = last;
18389                     }
18390                 }else{
18391                     this.selectFirstRow();
18392                 }
18393                 this.fireEvent("afterselectionchange", this);
18394             },
18395             "down" : function(e){
18396                 if(!e.shiftKey){
18397                     this.selectNext(e.shiftKey);
18398                 }else if(this.last !== false && this.lastActive !== false){
18399                     var last = this.last;
18400                     this.selectRange(this.last,  this.lastActive+1);
18401                     this.grid.getView().focusRow(this.lastActive);
18402                     if(last !== false){
18403                         this.last = last;
18404                     }
18405                 }else{
18406                     this.selectFirstRow();
18407                 }
18408                 this.fireEvent("afterselectionchange", this);
18409             },
18410             scope: this
18411         });
18412
18413         var view = this.grid.view;
18414         view.on("refresh", this.onRefresh, this);
18415         view.on("rowupdated", this.onRowUpdated, this);
18416         view.on("rowremoved", this.onRemove, this);
18417     },
18418
18419     // private
18420     onRefresh : function(){
18421         var ds = this.grid.dataSource, i, v = this.grid.view;
18422         var s = this.selections;
18423         s.each(function(r){
18424             if((i = ds.indexOfId(r.id)) != -1){
18425                 v.onRowSelect(i);
18426             }else{
18427                 s.remove(r);
18428             }
18429         });
18430     },
18431
18432     // private
18433     onRemove : function(v, index, r){
18434         this.selections.remove(r);
18435     },
18436
18437     // private
18438     onRowUpdated : function(v, index, r){
18439         if(this.isSelected(r)){
18440             v.onRowSelect(index);
18441         }
18442     },
18443
18444     /**
18445      * Select records.
18446      * @param {Array} records The records to select
18447      * @param {Boolean} keepExisting (optional) True to keep existing selections
18448      */
18449     selectRecords : function(records, keepExisting){
18450         if(!keepExisting){
18451             this.clearSelections();
18452         }
18453         var ds = this.grid.dataSource;
18454         for(var i = 0, len = records.length; i < len; i++){
18455             this.selectRow(ds.indexOf(records[i]), true);
18456         }
18457     },
18458
18459     /**
18460      * Gets the number of selected rows.
18461      * @return {Number}
18462      */
18463     getCount : function(){
18464         return this.selections.length;
18465     },
18466
18467     /**
18468      * Selects the first row in the grid.
18469      */
18470     selectFirstRow : function(){
18471         this.selectRow(0);
18472     },
18473
18474     /**
18475      * Select the last row.
18476      * @param {Boolean} keepExisting (optional) True to keep existing selections
18477      */
18478     selectLastRow : function(keepExisting){
18479         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
18480     },
18481
18482     /**
18483      * Selects the row immediately following the last selected row.
18484      * @param {Boolean} keepExisting (optional) True to keep existing selections
18485      */
18486     selectNext : function(keepExisting){
18487         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
18488             this.selectRow(this.last+1, keepExisting);
18489             this.grid.getView().focusRow(this.last);
18490         }
18491     },
18492
18493     /**
18494      * Selects the row that precedes the last selected row.
18495      * @param {Boolean} keepExisting (optional) True to keep existing selections
18496      */
18497     selectPrevious : function(keepExisting){
18498         if(this.last){
18499             this.selectRow(this.last-1, keepExisting);
18500             this.grid.getView().focusRow(this.last);
18501         }
18502     },
18503
18504     /**
18505      * Returns the selected records
18506      * @return {Array} Array of selected records
18507      */
18508     getSelections : function(){
18509         return [].concat(this.selections.items);
18510     },
18511
18512     /**
18513      * Returns the first selected record.
18514      * @return {Record}
18515      */
18516     getSelected : function(){
18517         return this.selections.itemAt(0);
18518     },
18519
18520
18521     /**
18522      * Clears all selections.
18523      */
18524     clearSelections : function(fast){
18525         if(this.locked) return;
18526         if(fast !== true){
18527             var ds = this.grid.dataSource;
18528             var s = this.selections;
18529             s.each(function(r){
18530                 this.deselectRow(ds.indexOfId(r.id));
18531             }, this);
18532             s.clear();
18533         }else{
18534             this.selections.clear();
18535         }
18536         this.last = false;
18537     },
18538
18539
18540     /**
18541      * Selects all rows.
18542      */
18543     selectAll : function(){
18544         if(this.locked) return;
18545         this.selections.clear();
18546         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
18547             this.selectRow(i, true);
18548         }
18549     },
18550
18551     /**
18552      * Returns True if there is a selection.
18553      * @return {Boolean}
18554      */
18555     hasSelection : function(){
18556         return this.selections.length > 0;
18557     },
18558
18559     /**
18560      * Returns True if the specified row is selected.
18561      * @param {Number/Record} record The record or index of the record to check
18562      * @return {Boolean}
18563      */
18564     isSelected : function(index){
18565         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
18566         return (r && this.selections.key(r.id) ? true : false);
18567     },
18568
18569     /**
18570      * Returns True if the specified record id is selected.
18571      * @param {String} id The id of record to check
18572      * @return {Boolean}
18573      */
18574     isIdSelected : function(id){
18575         return (this.selections.key(id) ? true : false);
18576     },
18577
18578     // private
18579     handleMouseDown : function(e, t){
18580         var view = this.grid.getView(), rowIndex;
18581         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
18582             return;
18583         };
18584         if(e.shiftKey && this.last !== false){
18585             var last = this.last;
18586             this.selectRange(last, rowIndex, e.ctrlKey);
18587             this.last = last; // reset the last
18588             view.focusRow(rowIndex);
18589         }else{
18590             var isSelected = this.isSelected(rowIndex);
18591             if(e.button !== 0 && isSelected){
18592                 view.focusRow(rowIndex);
18593             }else if(e.ctrlKey && isSelected){
18594                 this.deselectRow(rowIndex);
18595             }else if(!isSelected){
18596                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
18597                 view.focusRow(rowIndex);
18598             }
18599         }
18600         this.fireEvent("afterselectionchange", this);
18601     },
18602     // private
18603     handleDragableRowClick :  function(grid, rowIndex, e) 
18604     {
18605         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
18606             this.selectRow(rowIndex, false);
18607             grid.view.focusRow(rowIndex);
18608              this.fireEvent("afterselectionchange", this);
18609         }
18610     },
18611     
18612     /**
18613      * Selects multiple rows.
18614      * @param {Array} rows Array of the indexes of the row to select
18615      * @param {Boolean} keepExisting (optional) True to keep existing selections
18616      */
18617     selectRows : function(rows, keepExisting){
18618         if(!keepExisting){
18619             this.clearSelections();
18620         }
18621         for(var i = 0, len = rows.length; i < len; i++){
18622             this.selectRow(rows[i], true);
18623         }
18624     },
18625
18626     /**
18627      * Selects a range of rows. All rows in between startRow and endRow are also selected.
18628      * @param {Number} startRow The index of the first row in the range
18629      * @param {Number} endRow The index of the last row in the range
18630      * @param {Boolean} keepExisting (optional) True to retain existing selections
18631      */
18632     selectRange : function(startRow, endRow, keepExisting){
18633         if(this.locked) return;
18634         if(!keepExisting){
18635             this.clearSelections();
18636         }
18637         if(startRow <= endRow){
18638             for(var i = startRow; i <= endRow; i++){
18639                 this.selectRow(i, true);
18640             }
18641         }else{
18642             for(var i = startRow; i >= endRow; i--){
18643                 this.selectRow(i, true);
18644             }
18645         }
18646     },
18647
18648     /**
18649      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
18650      * @param {Number} startRow The index of the first row in the range
18651      * @param {Number} endRow The index of the last row in the range
18652      */
18653     deselectRange : function(startRow, endRow, preventViewNotify){
18654         if(this.locked) return;
18655         for(var i = startRow; i <= endRow; i++){
18656             this.deselectRow(i, preventViewNotify);
18657         }
18658     },
18659
18660     /**
18661      * Selects a row.
18662      * @param {Number} row The index of the row to select
18663      * @param {Boolean} keepExisting (optional) True to keep existing selections
18664      */
18665     selectRow : function(index, keepExisting, preventViewNotify){
18666         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
18667         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
18668             if(!keepExisting || this.singleSelect){
18669                 this.clearSelections();
18670             }
18671             var r = this.grid.dataSource.getAt(index);
18672             this.selections.add(r);
18673             this.last = this.lastActive = index;
18674             if(!preventViewNotify){
18675                 this.grid.getView().onRowSelect(index);
18676             }
18677             this.fireEvent("rowselect", this, index, r);
18678             this.fireEvent("selectionchange", this);
18679         }
18680     },
18681
18682     /**
18683      * Deselects a row.
18684      * @param {Number} row The index of the row to deselect
18685      */
18686     deselectRow : function(index, preventViewNotify){
18687         if(this.locked) return;
18688         if(this.last == index){
18689             this.last = false;
18690         }
18691         if(this.lastActive == index){
18692             this.lastActive = false;
18693         }
18694         var r = this.grid.dataSource.getAt(index);
18695         this.selections.remove(r);
18696         if(!preventViewNotify){
18697             this.grid.getView().onRowDeselect(index);
18698         }
18699         this.fireEvent("rowdeselect", this, index);
18700         this.fireEvent("selectionchange", this);
18701     },
18702
18703     // private
18704     restoreLast : function(){
18705         if(this._last){
18706             this.last = this._last;
18707         }
18708     },
18709
18710     // private
18711     acceptsNav : function(row, col, cm){
18712         return !cm.isHidden(col) && cm.isCellEditable(col, row);
18713     },
18714
18715     // private
18716     onEditorKey : function(field, e){
18717         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
18718         if(k == e.TAB){
18719             e.stopEvent();
18720             ed.completeEdit();
18721             if(e.shiftKey){
18722                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
18723             }else{
18724                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
18725             }
18726         }else if(k == e.ENTER && !e.ctrlKey){
18727             e.stopEvent();
18728             ed.completeEdit();
18729             if(e.shiftKey){
18730                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
18731             }else{
18732                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
18733             }
18734         }else if(k == e.ESC){
18735             ed.cancelEdit();
18736         }
18737         if(newCell){
18738             g.startEditing(newCell[0], newCell[1]);
18739         }
18740     }
18741 });/*
18742  * Based on:
18743  * Ext JS Library 1.1.1
18744  * Copyright(c) 2006-2007, Ext JS, LLC.
18745  *
18746  * Originally Released Under LGPL - original licence link has changed is not relivant.
18747  *
18748  * Fork - LGPL
18749  * <script type="text/javascript">
18750  */
18751  
18752 /**
18753  * @class Roo.bootstrap.PagingToolbar
18754  * @extends Roo.Row
18755  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
18756  * @constructor
18757  * Create a new PagingToolbar
18758  * @param {Object} config The config object
18759  */
18760 Roo.bootstrap.PagingToolbar = function(config)
18761 {
18762     // old args format still supported... - xtype is prefered..
18763         // created from xtype...
18764     var ds = config.dataSource;
18765     this.toolbarItems = [];
18766     if (config.items) {
18767         this.toolbarItems = config.items;
18768 //        config.items = [];
18769     }
18770     
18771     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
18772     this.ds = ds;
18773     this.cursor = 0;
18774     if (ds) { 
18775         this.bind(ds);
18776     }
18777     
18778     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
18779     
18780 };
18781
18782 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
18783     /**
18784      * @cfg {Roo.data.Store} dataSource
18785      * The underlying data store providing the paged data
18786      */
18787     /**
18788      * @cfg {String/HTMLElement/Element} container
18789      * container The id or element that will contain the toolbar
18790      */
18791     /**
18792      * @cfg {Boolean} displayInfo
18793      * True to display the displayMsg (defaults to false)
18794      */
18795     /**
18796      * @cfg {Number} pageSize
18797      * The number of records to display per page (defaults to 20)
18798      */
18799     pageSize: 20,
18800     /**
18801      * @cfg {String} displayMsg
18802      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
18803      */
18804     displayMsg : 'Displaying {0} - {1} of {2}',
18805     /**
18806      * @cfg {String} emptyMsg
18807      * The message to display when no records are found (defaults to "No data to display")
18808      */
18809     emptyMsg : 'No data to display',
18810     /**
18811      * Customizable piece of the default paging text (defaults to "Page")
18812      * @type String
18813      */
18814     beforePageText : "Page",
18815     /**
18816      * Customizable piece of the default paging text (defaults to "of %0")
18817      * @type String
18818      */
18819     afterPageText : "of {0}",
18820     /**
18821      * Customizable piece of the default paging text (defaults to "First Page")
18822      * @type String
18823      */
18824     firstText : "First Page",
18825     /**
18826      * Customizable piece of the default paging text (defaults to "Previous Page")
18827      * @type String
18828      */
18829     prevText : "Previous Page",
18830     /**
18831      * Customizable piece of the default paging text (defaults to "Next Page")
18832      * @type String
18833      */
18834     nextText : "Next Page",
18835     /**
18836      * Customizable piece of the default paging text (defaults to "Last Page")
18837      * @type String
18838      */
18839     lastText : "Last Page",
18840     /**
18841      * Customizable piece of the default paging text (defaults to "Refresh")
18842      * @type String
18843      */
18844     refreshText : "Refresh",
18845
18846     buttons : false,
18847     // private
18848     onRender : function(ct, position) 
18849     {
18850         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
18851         this.navgroup.parentId = this.id;
18852         this.navgroup.onRender(this.el, null);
18853         // add the buttons to the navgroup
18854         
18855         if(this.displayInfo){
18856             Roo.log(this.el.select('ul.navbar-nav',true).first());
18857             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
18858             this.displayEl = this.el.select('.x-paging-info', true).first();
18859 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
18860 //            this.displayEl = navel.el.select('span',true).first();
18861         }
18862         
18863         var _this = this;
18864         
18865         if(this.buttons){
18866             Roo.each(_this.buttons, function(e){
18867                Roo.factory(e).onRender(_this.el, null);
18868             });
18869         }
18870             
18871         Roo.each(_this.toolbarItems, function(e) {
18872             _this.navgroup.addItem(e);
18873         });
18874         
18875         this.first = this.navgroup.addItem({
18876             tooltip: this.firstText,
18877             cls: "prev",
18878             icon : 'fa fa-backward',
18879             disabled: true,
18880             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
18881         });
18882         
18883         this.prev =  this.navgroup.addItem({
18884             tooltip: this.prevText,
18885             cls: "prev",
18886             icon : 'fa fa-step-backward',
18887             disabled: true,
18888             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
18889         });
18890     //this.addSeparator();
18891         
18892         
18893         var field = this.navgroup.addItem( {
18894             tagtype : 'span',
18895             cls : 'x-paging-position',
18896             
18897             html : this.beforePageText  +
18898                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
18899                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
18900          } ); //?? escaped?
18901         
18902         this.field = field.el.select('input', true).first();
18903         this.field.on("keydown", this.onPagingKeydown, this);
18904         this.field.on("focus", function(){this.dom.select();});
18905     
18906     
18907         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
18908         //this.field.setHeight(18);
18909         //this.addSeparator();
18910         this.next = this.navgroup.addItem({
18911             tooltip: this.nextText,
18912             cls: "next",
18913             html : ' <i class="fa fa-step-forward">',
18914             disabled: true,
18915             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
18916         });
18917         this.last = this.navgroup.addItem({
18918             tooltip: this.lastText,
18919             icon : 'fa fa-forward',
18920             cls: "next",
18921             disabled: true,
18922             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
18923         });
18924     //this.addSeparator();
18925         this.loading = this.navgroup.addItem({
18926             tooltip: this.refreshText,
18927             icon: 'fa fa-refresh',
18928             
18929             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
18930         });
18931
18932     },
18933
18934     // private
18935     updateInfo : function(){
18936         if(this.displayEl){
18937             var count = this.ds.getCount();
18938             var msg = count == 0 ?
18939                 this.emptyMsg :
18940                 String.format(
18941                     this.displayMsg,
18942                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
18943                 );
18944             this.displayEl.update(msg);
18945         }
18946     },
18947
18948     // private
18949     onLoad : function(ds, r, o){
18950        this.cursor = o.params ? o.params.start : 0;
18951        var d = this.getPageData(),
18952             ap = d.activePage,
18953             ps = d.pages;
18954         
18955        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
18956        this.field.dom.value = ap;
18957        this.first.setDisabled(ap == 1);
18958        this.prev.setDisabled(ap == 1);
18959        this.next.setDisabled(ap == ps);
18960        this.last.setDisabled(ap == ps);
18961        this.loading.enable();
18962        this.updateInfo();
18963     },
18964
18965     // private
18966     getPageData : function(){
18967         var total = this.ds.getTotalCount();
18968         return {
18969             total : total,
18970             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
18971             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
18972         };
18973     },
18974
18975     // private
18976     onLoadError : function(){
18977         this.loading.enable();
18978     },
18979
18980     // private
18981     onPagingKeydown : function(e){
18982         var k = e.getKey();
18983         var d = this.getPageData();
18984         if(k == e.RETURN){
18985             var v = this.field.dom.value, pageNum;
18986             if(!v || isNaN(pageNum = parseInt(v, 10))){
18987                 this.field.dom.value = d.activePage;
18988                 return;
18989             }
18990             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
18991             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
18992             e.stopEvent();
18993         }
18994         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))
18995         {
18996           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
18997           this.field.dom.value = pageNum;
18998           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
18999           e.stopEvent();
19000         }
19001         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
19002         {
19003           var v = this.field.dom.value, pageNum; 
19004           var increment = (e.shiftKey) ? 10 : 1;
19005           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
19006             increment *= -1;
19007           if(!v || isNaN(pageNum = parseInt(v, 10))) {
19008             this.field.dom.value = d.activePage;
19009             return;
19010           }
19011           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
19012           {
19013             this.field.dom.value = parseInt(v, 10) + increment;
19014             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
19015             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
19016           }
19017           e.stopEvent();
19018         }
19019     },
19020
19021     // private
19022     beforeLoad : function(){
19023         if(this.loading){
19024             this.loading.disable();
19025         }
19026     },
19027
19028     // private
19029     onClick : function(which){
19030         var ds = this.ds;
19031         if (!ds) {
19032             return;
19033         }
19034         switch(which){
19035             case "first":
19036                 ds.load({params:{start: 0, limit: this.pageSize}});
19037             break;
19038             case "prev":
19039                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
19040             break;
19041             case "next":
19042                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
19043             break;
19044             case "last":
19045                 var total = ds.getTotalCount();
19046                 var extra = total % this.pageSize;
19047                 var lastStart = extra ? (total - extra) : total-this.pageSize;
19048                 ds.load({params:{start: lastStart, limit: this.pageSize}});
19049             break;
19050             case "refresh":
19051                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
19052             break;
19053         }
19054     },
19055
19056     /**
19057      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
19058      * @param {Roo.data.Store} store The data store to unbind
19059      */
19060     unbind : function(ds){
19061         ds.un("beforeload", this.beforeLoad, this);
19062         ds.un("load", this.onLoad, this);
19063         ds.un("loadexception", this.onLoadError, this);
19064         ds.un("remove", this.updateInfo, this);
19065         ds.un("add", this.updateInfo, this);
19066         this.ds = undefined;
19067     },
19068
19069     /**
19070      * Binds the paging toolbar to the specified {@link Roo.data.Store}
19071      * @param {Roo.data.Store} store The data store to bind
19072      */
19073     bind : function(ds){
19074         ds.on("beforeload", this.beforeLoad, this);
19075         ds.on("load", this.onLoad, this);
19076         ds.on("loadexception", this.onLoadError, this);
19077         ds.on("remove", this.updateInfo, this);
19078         ds.on("add", this.updateInfo, this);
19079         this.ds = ds;
19080     }
19081 });/*
19082  * - LGPL
19083  *
19084  * element
19085  * 
19086  */
19087
19088 /**
19089  * @class Roo.bootstrap.MessageBar
19090  * @extends Roo.bootstrap.Component
19091  * Bootstrap MessageBar class
19092  * @cfg {String} html contents of the MessageBar
19093  * @cfg {String} weight (info | success | warning | danger) default info
19094  * @cfg {String} beforeClass insert the bar before the given class
19095  * @cfg {Boolean} closable (true | false) default false
19096  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
19097  * 
19098  * @constructor
19099  * Create a new Element
19100  * @param {Object} config The config object
19101  */
19102
19103 Roo.bootstrap.MessageBar = function(config){
19104     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
19105 };
19106
19107 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
19108     
19109     html: '',
19110     weight: 'info',
19111     closable: false,
19112     fixed: false,
19113     beforeClass: 'bootstrap-sticky-wrap',
19114     
19115     getAutoCreate : function(){
19116         
19117         var cfg = {
19118             tag: 'div',
19119             cls: 'alert alert-dismissable alert-' + this.weight,
19120             cn: [
19121                 {
19122                     tag: 'span',
19123                     cls: 'message',
19124                     html: this.html || ''
19125                 }
19126             ]
19127         }
19128         
19129         if(this.fixed){
19130             cfg.cls += ' alert-messages-fixed';
19131         }
19132         
19133         if(this.closable){
19134             cfg.cn.push({
19135                 tag: 'button',
19136                 cls: 'close',
19137                 html: 'x'
19138             });
19139         }
19140         
19141         return cfg;
19142     },
19143     
19144     onRender : function(ct, position)
19145     {
19146         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19147         
19148         if(!this.el){
19149             var cfg = Roo.apply({},  this.getAutoCreate());
19150             cfg.id = Roo.id();
19151             
19152             if (this.cls) {
19153                 cfg.cls += ' ' + this.cls;
19154             }
19155             if (this.style) {
19156                 cfg.style = this.style;
19157             }
19158             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
19159             
19160             this.el.setVisibilityMode(Roo.Element.DISPLAY);
19161         }
19162         
19163         this.el.select('>button.close').on('click', this.hide, this);
19164         
19165     },
19166     
19167     show : function()
19168     {
19169         if (!this.rendered) {
19170             this.render();
19171         }
19172         
19173         this.el.show();
19174         
19175         this.fireEvent('show', this);
19176         
19177     },
19178     
19179     hide : function()
19180     {
19181         if (!this.rendered) {
19182             this.render();
19183         }
19184         
19185         this.el.hide();
19186         
19187         this.fireEvent('hide', this);
19188     },
19189     
19190     update : function()
19191     {
19192 //        var e = this.el.dom.firstChild;
19193 //        
19194 //        if(this.closable){
19195 //            e = e.nextSibling;
19196 //        }
19197 //        
19198 //        e.data = this.html || '';
19199
19200         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
19201     }
19202    
19203 });
19204
19205  
19206
19207      /*
19208  * - LGPL
19209  *
19210  * Graph
19211  * 
19212  */
19213
19214
19215 /**
19216  * @class Roo.bootstrap.Graph
19217  * @extends Roo.bootstrap.Component
19218  * Bootstrap Graph class
19219 > Prameters
19220  -sm {number} sm 4
19221  -md {number} md 5
19222  @cfg {String} graphtype  bar | vbar | pie
19223  @cfg {number} g_x coodinator | centre x (pie)
19224  @cfg {number} g_y coodinator | centre y (pie)
19225  @cfg {number} g_r radius (pie)
19226  @cfg {number} g_height height of the chart (respected by all elements in the set)
19227  @cfg {number} g_width width of the chart (respected by all elements in the set)
19228  @cfg {Object} title The title of the chart
19229     
19230  -{Array}  values
19231  -opts (object) options for the chart 
19232      o {
19233      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
19234      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
19235      o vgutter (number)
19236      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.
19237      o stacked (boolean) whether or not to tread values as in a stacked bar chart
19238      o to
19239      o stretch (boolean)
19240      o }
19241  -opts (object) options for the pie
19242      o{
19243      o cut
19244      o startAngle (number)
19245      o endAngle (number)
19246      } 
19247  *
19248  * @constructor
19249  * Create a new Input
19250  * @param {Object} config The config object
19251  */
19252
19253 Roo.bootstrap.Graph = function(config){
19254     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
19255     
19256     this.addEvents({
19257         // img events
19258         /**
19259          * @event click
19260          * The img click event for the img.
19261          * @param {Roo.EventObject} e
19262          */
19263         "click" : true
19264     });
19265 };
19266
19267 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
19268     
19269     sm: 4,
19270     md: 5,
19271     graphtype: 'bar',
19272     g_height: 250,
19273     g_width: 400,
19274     g_x: 50,
19275     g_y: 50,
19276     g_r: 30,
19277     opts:{
19278         //g_colors: this.colors,
19279         g_type: 'soft',
19280         g_gutter: '20%'
19281
19282     },
19283     title : false,
19284
19285     getAutoCreate : function(){
19286         
19287         var cfg = {
19288             tag: 'div',
19289             html : null
19290         }
19291         
19292         
19293         return  cfg;
19294     },
19295
19296     onRender : function(ct,position){
19297         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
19298         this.raphael = Raphael(this.el.dom);
19299         
19300                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19301                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19302                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19303                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
19304                 /*
19305                 r.text(160, 10, "Single Series Chart").attr(txtattr);
19306                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
19307                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
19308                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
19309                 
19310                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
19311                 r.barchart(330, 10, 300, 220, data1);
19312                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
19313                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
19314                 */
19315                 
19316                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19317                 // r.barchart(30, 30, 560, 250,  xdata, {
19318                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
19319                 //     axis : "0 0 1 1",
19320                 //     axisxlabels :  xdata
19321                 //     //yvalues : cols,
19322                    
19323                 // });
19324 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19325 //        
19326 //        this.load(null,xdata,{
19327 //                axis : "0 0 1 1",
19328 //                axisxlabels :  xdata
19329 //                });
19330
19331     },
19332
19333     load : function(graphtype,xdata,opts){
19334         this.raphael.clear();
19335         if(!graphtype) {
19336             graphtype = this.graphtype;
19337         }
19338         if(!opts){
19339             opts = this.opts;
19340         }
19341         var r = this.raphael,
19342             fin = function () {
19343                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
19344             },
19345             fout = function () {
19346                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
19347             },
19348             pfin = function() {
19349                 this.sector.stop();
19350                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
19351
19352                 if (this.label) {
19353                     this.label[0].stop();
19354                     this.label[0].attr({ r: 7.5 });
19355                     this.label[1].attr({ "font-weight": 800 });
19356                 }
19357             },
19358             pfout = function() {
19359                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
19360
19361                 if (this.label) {
19362                     this.label[0].animate({ r: 5 }, 500, "bounce");
19363                     this.label[1].attr({ "font-weight": 400 });
19364                 }
19365             };
19366
19367         switch(graphtype){
19368             case 'bar':
19369                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
19370                 break;
19371             case 'hbar':
19372                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
19373                 break;
19374             case 'pie':
19375 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
19376 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
19377 //            
19378                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
19379                 
19380                 break;
19381
19382         }
19383         
19384         if(this.title){
19385             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
19386         }
19387         
19388     },
19389     
19390     setTitle: function(o)
19391     {
19392         this.title = o;
19393     },
19394     
19395     initEvents: function() {
19396         
19397         if(!this.href){
19398             this.el.on('click', this.onClick, this);
19399         }
19400     },
19401     
19402     onClick : function(e)
19403     {
19404         Roo.log('img onclick');
19405         this.fireEvent('click', this, e);
19406     }
19407    
19408 });
19409
19410  
19411 /*
19412  * - LGPL
19413  *
19414  * numberBox
19415  * 
19416  */
19417 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19418
19419 /**
19420  * @class Roo.bootstrap.dash.NumberBox
19421  * @extends Roo.bootstrap.Component
19422  * Bootstrap NumberBox class
19423  * @cfg {String} bgcolor Box background color, such as (aqua | green | yellow | red etc..) Default aqua
19424  * @cfg {String} headline Box headline
19425  * @cfg {String} content Box content
19426  * @cfg {String} icon Box icon
19427  * @cfg {String} footer Footer text
19428  * @cfg {String} fhref Footer href
19429  * 
19430  * @constructor
19431  * Create a new NumberBox
19432  * @param {Object} config The config object
19433  */
19434
19435
19436 Roo.bootstrap.dash.NumberBox = function(config){
19437     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
19438     
19439 };
19440
19441 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
19442     
19443     bgcolor : 'aqua',
19444     headline : '',
19445     content : '',
19446     icon : '',
19447     footer : '',
19448     fhref : '',
19449     ficon : '',
19450     
19451     getAutoCreate : function(){
19452         
19453         var cfg = {
19454             tag : 'div',
19455             cls : 'small-box bg-' + this.bgcolor,
19456             cn : [
19457                 {
19458                     tag : 'div',
19459                     cls : 'inner',
19460                     cn :[
19461                         {
19462                             tag : 'h3',
19463                             cls : 'roo-headline',
19464                             html : this.headline
19465                         },
19466                         {
19467                             tag : 'p',
19468                             cls : 'roo-content',
19469                             html : this.content
19470                         }
19471                     ]
19472                 }
19473             ]
19474         }
19475         
19476         if(this.icon){
19477             cfg.cn.push({
19478                 tag : 'div',
19479                 cls : 'icon',
19480                 cn :[
19481                     {
19482                         tag : 'i',
19483                         cls : 'ion ' + this.icon
19484                     }
19485                 ]
19486             });
19487         }
19488         
19489         if(this.footer){
19490             var footer = {
19491                 tag : 'a',
19492                 cls : 'small-box-footer',
19493                 href : this.fhref || '#',
19494                 html : this.footer
19495             };
19496             
19497             cfg.cn.push(footer);
19498             
19499         }
19500         
19501         return  cfg;
19502     },
19503
19504     onRender : function(ct,position){
19505         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
19506
19507
19508        
19509                 
19510     },
19511
19512     setHeadline: function (value)
19513     {
19514         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
19515     },
19516     
19517     setFooter: function (value, href)
19518     {
19519         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
19520         
19521         if(href){
19522             this.el.select('a.small-box-footer',true).first().attr('href', href);
19523         }
19524         
19525     },
19526
19527     setContent: function (value)
19528     {
19529         this.el.select('.roo-content',true).first().dom.innerHTML = value;
19530     },
19531
19532     initEvents: function() 
19533     {   
19534         
19535     }
19536     
19537 });
19538
19539  
19540 /*
19541  * - LGPL
19542  *
19543  * TabBox
19544  * 
19545  */
19546 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19547
19548 /**
19549  * @class Roo.bootstrap.dash.TabBox
19550  * @extends Roo.bootstrap.Component
19551  * Bootstrap TabBox class
19552  * @cfg {String} title Title of the TabBox
19553  * @cfg {String} icon Icon of the TabBox
19554  * @cfg {Boolean} showtabs (true|false) show the tabs default true
19555  * 
19556  * @constructor
19557  * Create a new TabBox
19558  * @param {Object} config The config object
19559  */
19560
19561
19562 Roo.bootstrap.dash.TabBox = function(config){
19563     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
19564     this.addEvents({
19565         // raw events
19566         /**
19567          * @event addpane
19568          * When a pane is added
19569          * @param {Roo.bootstrap.dash.TabPane} pane
19570          */
19571         "addpane" : true
19572          
19573     });
19574 };
19575
19576 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
19577
19578     title : '',
19579     icon : false,
19580     showtabs : true,
19581     
19582     getChildContainer : function()
19583     {
19584         return this.el.select('.tab-content', true).first();
19585     },
19586     
19587     getAutoCreate : function(){
19588         
19589         var header = {
19590             tag: 'li',
19591             cls: 'pull-left header',
19592             html: this.title,
19593             cn : []
19594         };
19595         
19596         if(this.icon){
19597             header.cn.push({
19598                 tag: 'i',
19599                 cls: 'fa ' + this.icon
19600             });
19601         }
19602         
19603         
19604         var cfg = {
19605             tag: 'div',
19606             cls: 'nav-tabs-custom',
19607             cn: [
19608                 {
19609                     tag: 'ul',
19610                     cls: 'nav nav-tabs pull-right',
19611                     cn: [
19612                         header
19613                     ]
19614                 },
19615                 {
19616                     tag: 'div',
19617                     cls: 'tab-content no-padding',
19618                     cn: []
19619                 }
19620             ]
19621         }
19622
19623         return  cfg;
19624     },
19625     initEvents : function()
19626     {
19627         //Roo.log('add add pane handler');
19628         this.on('addpane', this.onAddPane, this);
19629     },
19630      /**
19631      * Updates the box title
19632      * @param {String} html to set the title to.
19633      */
19634     setTitle : function(value)
19635     {
19636         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
19637     },
19638     onAddPane : function(pane)
19639     {
19640         //Roo.log('addpane');
19641         //Roo.log(pane);
19642         // tabs are rendere left to right..
19643         if(!this.showtabs){
19644             return;
19645         }
19646         
19647         var ctr = this.el.select('.nav-tabs', true).first();
19648          
19649          
19650         var existing = ctr.select('.nav-tab',true);
19651         var qty = existing.getCount();;
19652         
19653         
19654         var tab = ctr.createChild({
19655             tag : 'li',
19656             cls : 'nav-tab' + (qty ? '' : ' active'),
19657             cn : [
19658                 {
19659                     tag : 'a',
19660                     href:'#',
19661                     html : pane.title
19662                 }
19663             ]
19664         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
19665         pane.tab = tab;
19666         
19667         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
19668         if (!qty) {
19669             pane.el.addClass('active');
19670         }
19671         
19672                 
19673     },
19674     onTabClick : function(ev,un,ob,pane)
19675     {
19676         //Roo.log('tab - prev default');
19677         ev.preventDefault();
19678         
19679         
19680         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
19681         pane.tab.addClass('active');
19682         //Roo.log(pane.title);
19683         this.getChildContainer().select('.tab-pane',true).removeClass('active');
19684         // technically we should have a deactivate event.. but maybe add later.
19685         // and it should not de-activate the selected tab...
19686         
19687         pane.el.addClass('active');
19688         pane.fireEvent('activate');
19689         
19690         
19691     }
19692     
19693     
19694 });
19695
19696  
19697 /*
19698  * - LGPL
19699  *
19700  * Tab pane
19701  * 
19702  */
19703 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19704 /**
19705  * @class Roo.bootstrap.TabPane
19706  * @extends Roo.bootstrap.Component
19707  * Bootstrap TabPane class
19708  * @cfg {Boolean} active (false | true) Default false
19709  * @cfg {String} title title of panel
19710
19711  * 
19712  * @constructor
19713  * Create a new TabPane
19714  * @param {Object} config The config object
19715  */
19716
19717 Roo.bootstrap.dash.TabPane = function(config){
19718     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
19719     
19720 };
19721
19722 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
19723     
19724     active : false,
19725     title : '',
19726     
19727     // the tabBox that this is attached to.
19728     tab : false,
19729      
19730     getAutoCreate : function() 
19731     {
19732         var cfg = {
19733             tag: 'div',
19734             cls: 'tab-pane'
19735         }
19736         
19737         if(this.active){
19738             cfg.cls += ' active';
19739         }
19740         
19741         return cfg;
19742     },
19743     initEvents  : function()
19744     {
19745         //Roo.log('trigger add pane handler');
19746         this.parent().fireEvent('addpane', this)
19747     },
19748     
19749      /**
19750      * Updates the tab title 
19751      * @param {String} html to set the title to.
19752      */
19753     setTitle: function(str)
19754     {
19755         if (!this.tab) {
19756             return;
19757         }
19758         this.title = str;
19759         this.tab.select('a'.true).first().dom.innerHTML = str;
19760         
19761     }
19762     
19763     
19764     
19765 });
19766
19767  
19768
19769
19770  /*
19771  * - LGPL
19772  *
19773  * menu
19774  * 
19775  */
19776 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
19777
19778 /**
19779  * @class Roo.bootstrap.menu.Menu
19780  * @extends Roo.bootstrap.Component
19781  * Bootstrap Menu class - container for Menu
19782  * @cfg {String} html Text of the menu
19783  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
19784  * @cfg {String} icon Font awesome icon
19785  * @cfg {String} pos Menu align to (top | bottom) default bottom
19786  * 
19787  * 
19788  * @constructor
19789  * Create a new Menu
19790  * @param {Object} config The config object
19791  */
19792
19793
19794 Roo.bootstrap.menu.Menu = function(config){
19795     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
19796     
19797     this.addEvents({
19798         /**
19799          * @event beforeshow
19800          * Fires before this menu is displayed
19801          * @param {Roo.bootstrap.menu.Menu} this
19802          */
19803         beforeshow : true,
19804         /**
19805          * @event beforehide
19806          * Fires before this menu is hidden
19807          * @param {Roo.bootstrap.menu.Menu} this
19808          */
19809         beforehide : true,
19810         /**
19811          * @event show
19812          * Fires after this menu is displayed
19813          * @param {Roo.bootstrap.menu.Menu} this
19814          */
19815         show : true,
19816         /**
19817          * @event hide
19818          * Fires after this menu is hidden
19819          * @param {Roo.bootstrap.menu.Menu} this
19820          */
19821         hide : true,
19822         /**
19823          * @event click
19824          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19825          * @param {Roo.bootstrap.menu.Menu} this
19826          * @param {Roo.EventObject} e
19827          */
19828         click : true
19829     });
19830     
19831 };
19832
19833 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
19834     
19835     submenu : false,
19836     html : '',
19837     weight : 'default',
19838     icon : false,
19839     pos : 'bottom',
19840     
19841     
19842     getChildContainer : function() {
19843         if(this.isSubMenu){
19844             return this.el;
19845         }
19846         
19847         return this.el.select('ul.dropdown-menu', true).first();  
19848     },
19849     
19850     getAutoCreate : function()
19851     {
19852         var text = [
19853             {
19854                 tag : 'span',
19855                 cls : 'roo-menu-text',
19856                 html : this.html
19857             }
19858         ];
19859         
19860         if(this.icon){
19861             text.unshift({
19862                 tag : 'i',
19863                 cls : 'fa ' + this.icon
19864             })
19865         }
19866         
19867         
19868         var cfg = {
19869             tag : 'div',
19870             cls : 'btn-group',
19871             cn : [
19872                 {
19873                     tag : 'button',
19874                     cls : 'dropdown-button btn btn-' + this.weight,
19875                     cn : text
19876                 },
19877                 {
19878                     tag : 'button',
19879                     cls : 'dropdown-toggle btn btn-' + this.weight,
19880                     cn : [
19881                         {
19882                             tag : 'span',
19883                             cls : 'caret'
19884                         }
19885                     ]
19886                 },
19887                 {
19888                     tag : 'ul',
19889                     cls : 'dropdown-menu'
19890                 }
19891             ]
19892             
19893         };
19894         
19895         if(this.pos == 'top'){
19896             cfg.cls += ' dropup';
19897         }
19898         
19899         if(this.isSubMenu){
19900             cfg = {
19901                 tag : 'ul',
19902                 cls : 'dropdown-menu'
19903             }
19904         }
19905         
19906         return cfg;
19907     },
19908     
19909     onRender : function(ct, position)
19910     {
19911         this.isSubMenu = ct.hasClass('dropdown-submenu');
19912         
19913         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
19914     },
19915     
19916     initEvents : function() 
19917     {
19918         if(this.isSubMenu){
19919             return;
19920         }
19921         
19922         this.hidden = true;
19923         
19924         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
19925         this.triggerEl.on('click', this.onTriggerPress, this);
19926         
19927         this.buttonEl = this.el.select('button.dropdown-button', true).first();
19928         this.buttonEl.on('click', this.onClick, this);
19929         
19930     },
19931     
19932     list : function()
19933     {
19934         if(this.isSubMenu){
19935             return this.el;
19936         }
19937         
19938         return this.el.select('ul.dropdown-menu', true).first();
19939     },
19940     
19941     onClick : function(e)
19942     {
19943         this.fireEvent("click", this, e);
19944     },
19945     
19946     onTriggerPress  : function(e)
19947     {   
19948         if (this.isVisible()) {
19949             this.hide();
19950         } else {
19951             this.show();
19952         }
19953     },
19954     
19955     isVisible : function(){
19956         return !this.hidden;
19957     },
19958     
19959     show : function()
19960     {
19961         this.fireEvent("beforeshow", this);
19962         
19963         this.hidden = false;
19964         this.el.addClass('open');
19965         
19966         Roo.get(document).on("mouseup", this.onMouseUp, this);
19967         
19968         this.fireEvent("show", this);
19969         
19970         
19971     },
19972     
19973     hide : function()
19974     {
19975         this.fireEvent("beforehide", this);
19976         
19977         this.hidden = true;
19978         this.el.removeClass('open');
19979         
19980         Roo.get(document).un("mouseup", this.onMouseUp);
19981         
19982         this.fireEvent("hide", this);
19983     },
19984     
19985     onMouseUp : function()
19986     {
19987         this.hide();
19988     }
19989     
19990 });
19991
19992  
19993  /*
19994  * - LGPL
19995  *
19996  * menu item
19997  * 
19998  */
19999 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20000
20001 /**
20002  * @class Roo.bootstrap.menu.Item
20003  * @extends Roo.bootstrap.Component
20004  * Bootstrap MenuItem class
20005  * @cfg {Boolean} submenu (true | false) default false
20006  * @cfg {String} html text of the item
20007  * @cfg {String} href the link
20008  * @cfg {Boolean} disable (true | false) default false
20009  * @cfg {Boolean} preventDefault (true | false) default true
20010  * @cfg {String} icon Font awesome icon
20011  * @cfg {String} pos Submenu align to (left | right) default right 
20012  * 
20013  * 
20014  * @constructor
20015  * Create a new Item
20016  * @param {Object} config The config object
20017  */
20018
20019
20020 Roo.bootstrap.menu.Item = function(config){
20021     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
20022     this.addEvents({
20023         /**
20024          * @event mouseover
20025          * Fires when the mouse is hovering over this menu
20026          * @param {Roo.bootstrap.menu.Item} this
20027          * @param {Roo.EventObject} e
20028          */
20029         mouseover : true,
20030         /**
20031          * @event mouseout
20032          * Fires when the mouse exits this menu
20033          * @param {Roo.bootstrap.menu.Item} this
20034          * @param {Roo.EventObject} e
20035          */
20036         mouseout : true,
20037         // raw events
20038         /**
20039          * @event click
20040          * The raw click event for the entire grid.
20041          * @param {Roo.EventObject} e
20042          */
20043         click : true
20044     });
20045 };
20046
20047 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
20048     
20049     submenu : false,
20050     href : '',
20051     html : '',
20052     preventDefault: true,
20053     disable : false,
20054     icon : false,
20055     pos : 'right',
20056     
20057     getAutoCreate : function()
20058     {
20059         var text = [
20060             {
20061                 tag : 'span',
20062                 cls : 'roo-menu-item-text',
20063                 html : this.html
20064             }
20065         ];
20066         
20067         if(this.icon){
20068             text.unshift({
20069                 tag : 'i',
20070                 cls : 'fa ' + this.icon
20071             })
20072         }
20073         
20074         var cfg = {
20075             tag : 'li',
20076             cn : [
20077                 {
20078                     tag : 'a',
20079                     href : this.href || '#',
20080                     cn : text
20081                 }
20082             ]
20083         };
20084         
20085         if(this.disable){
20086             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
20087         }
20088         
20089         if(this.submenu){
20090             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
20091             
20092             if(this.pos == 'left'){
20093                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
20094             }
20095         }
20096         
20097         return cfg;
20098     },
20099     
20100     initEvents : function() 
20101     {
20102         this.el.on('mouseover', this.onMouseOver, this);
20103         this.el.on('mouseout', this.onMouseOut, this);
20104         
20105         this.el.select('a', true).first().on('click', this.onClick, this);
20106         
20107     },
20108     
20109     onClick : function(e)
20110     {
20111         if(this.preventDefault){
20112             e.preventDefault();
20113         }
20114         
20115         this.fireEvent("click", this, e);
20116     },
20117     
20118     onMouseOver : function(e)
20119     {
20120         if(this.submenu && this.pos == 'left'){
20121             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
20122         }
20123         
20124         this.fireEvent("mouseover", this, e);
20125     },
20126     
20127     onMouseOut : function(e)
20128     {
20129         this.fireEvent("mouseout", this, e);
20130     }
20131 });
20132
20133  
20134
20135  /*
20136  * - LGPL
20137  *
20138  * menu separator
20139  * 
20140  */
20141 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20142
20143 /**
20144  * @class Roo.bootstrap.menu.Separator
20145  * @extends Roo.bootstrap.Component
20146  * Bootstrap Separator class
20147  * 
20148  * @constructor
20149  * Create a new Separator
20150  * @param {Object} config The config object
20151  */
20152
20153
20154 Roo.bootstrap.menu.Separator = function(config){
20155     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
20156 };
20157
20158 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
20159     
20160     getAutoCreate : function(){
20161         var cfg = {
20162             tag : 'li',
20163             cls: 'divider'
20164         };
20165         
20166         return cfg;
20167     }
20168    
20169 });
20170
20171  
20172
20173