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         Roo.log("fire event clicked");
3563         if(this.fireEvent('click', this, e) === false){
3564             return;
3565         };
3566         
3567         if (['tabs','pills'].indexOf(this.parent().type)!==-1) {
3568             if (typeof(this.parent().setActiveItem) !== 'undefined') {
3569                 this.parent().setActiveItem(this);
3570             }
3571         } 
3572     },
3573     
3574     isActive: function () {
3575         return this.active
3576     },
3577     setActive : function(state, fire, is_was_active)
3578     {
3579         if (this.active && !state & this.navId) {
3580             this.was_active = true;
3581             var nv = Roo.bootstrap.NavGroup.get(this.navId);
3582             if (nv) {
3583                 nv.clearWasActive(this);
3584             }
3585             
3586         }
3587         this.active = state;
3588         
3589         if (!state ) {
3590             this.el.removeClass('active');
3591         } else if (!this.el.hasClass('active')) {
3592             this.el.addClass('active');
3593         }
3594         if (fire) {
3595             this.fireEvent('changed', this, state);
3596         }
3597         
3598         // show a panel if it's registered and related..
3599         
3600         if (!this.navId || !this.tabId || !state || is_was_active) {
3601             return;
3602         }
3603         
3604         var tg = Roo.bootstrap.TabGroup.get(this.navId);
3605         if (!tg) {
3606             return;
3607         }
3608         var pan = tg.getPanelByName(this.tabId);
3609         if (!pan) {
3610             return;
3611         }
3612         // if we can not flip to new panel - go back to old nav highlight..
3613         if (false == tg.showPanel(pan)) {
3614             var nv = Roo.bootstrap.NavGroup.get(this.navId);
3615             if (nv) {
3616                 var onav = nv.getWasActive();
3617                 if (onav) {
3618                     onav.setActive(true, false, true);
3619                 }
3620             }
3621             
3622         }
3623         
3624         
3625         
3626     },
3627      // this should not be here...
3628     setDisabled : function(state)
3629     {
3630         this.disabled = state;
3631         if (!state ) {
3632             this.el.removeClass('disabled');
3633         } else if (!this.el.hasClass('disabled')) {
3634             this.el.addClass('disabled');
3635         }
3636         
3637     }
3638 });
3639  
3640
3641  /*
3642  * - LGPL
3643  *
3644  * sidebar item
3645  *
3646  *  li
3647  *    <span> icon </span>
3648  *    <span> text </span>
3649  *    <span>badge </span>
3650  */
3651
3652 /**
3653  * @class Roo.bootstrap.NavSidebarItem
3654  * @extends Roo.bootstrap.NavItem
3655  * Bootstrap Navbar.NavSidebarItem class
3656  * @constructor
3657  * Create a new Navbar Button
3658  * @param {Object} config The config object
3659  */
3660 Roo.bootstrap.NavSidebarItem = function(config){
3661     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
3662     this.addEvents({
3663         // raw events
3664         /**
3665          * @event click
3666          * The raw click event for the entire grid.
3667          * @param {Roo.EventObject} e
3668          */
3669         "click" : true,
3670          /**
3671             * @event changed
3672             * Fires when the active item active state changes
3673             * @param {Roo.bootstrap.NavSidebarItem} this
3674             * @param {boolean} state the new state
3675              
3676          */
3677         'changed': true
3678     });
3679    
3680 };
3681
3682 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
3683     
3684     
3685     getAutoCreate : function(){
3686         
3687         
3688         var a = {
3689                 tag: 'a',
3690                 href : this.href || '#',
3691                 cls: '',
3692                 html : '',
3693                 cn : []
3694         };
3695         var cfg = {
3696             tag: 'li',
3697             cls: '',
3698             cn: [ a ]
3699         }
3700         var span = {
3701             tag: 'span',
3702             html : this.html || ''
3703         }
3704         
3705         
3706         if (this.active) {
3707             cfg.cls += ' active';
3708         }
3709         
3710         // left icon..
3711         if (this.glyphicon || this.icon) {
3712             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
3713             a.cn.push({ tag : 'i', cls : c }) ;
3714         }
3715         // html..
3716         a.cn.push(span);
3717         // then badge..
3718         if (this.badge !== '') {
3719             a.cn.push({ tag: 'span',  cls : 'badge pull-right ' + (this.badgecls || ''), html: this.badge }); 
3720         }
3721         // fi
3722         if (this.menu) {
3723             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
3724             a.cls += 'dropdown-toggle treeview' ;
3725             
3726         }
3727         
3728         
3729         
3730         return cfg;
3731          
3732            
3733     }
3734    
3735      
3736  
3737 });
3738  
3739
3740  /*
3741  * - LGPL
3742  *
3743  * row
3744  * 
3745  */
3746
3747 /**
3748  * @class Roo.bootstrap.Row
3749  * @extends Roo.bootstrap.Component
3750  * Bootstrap Row class (contains columns...)
3751  * 
3752  * @constructor
3753  * Create a new Row
3754  * @param {Object} config The config object
3755  */
3756
3757 Roo.bootstrap.Row = function(config){
3758     Roo.bootstrap.Row.superclass.constructor.call(this, config);
3759 };
3760
3761 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
3762     
3763     getAutoCreate : function(){
3764        return {
3765             cls: 'row clearfix'
3766        };
3767     }
3768     
3769     
3770 });
3771
3772  
3773
3774  /*
3775  * - LGPL
3776  *
3777  * element
3778  * 
3779  */
3780
3781 /**
3782  * @class Roo.bootstrap.Element
3783  * @extends Roo.bootstrap.Component
3784  * Bootstrap Element class
3785  * @cfg {String} html contents of the element
3786  * @cfg {String} tag tag of the element
3787  * @cfg {String} cls class of the element
3788  * 
3789  * @constructor
3790  * Create a new Element
3791  * @param {Object} config The config object
3792  */
3793
3794 Roo.bootstrap.Element = function(config){
3795     Roo.bootstrap.Element.superclass.constructor.call(this, config);
3796 };
3797
3798 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
3799     
3800     tag: 'div',
3801     cls: '',
3802     html: '',
3803      
3804     
3805     getAutoCreate : function(){
3806         
3807         var cfg = {
3808             tag: this.tag,
3809             cls: this.cls,
3810             html: this.html
3811         }
3812         
3813         
3814         
3815         return cfg;
3816     }
3817    
3818 });
3819
3820  
3821
3822  /*
3823  * - LGPL
3824  *
3825  * pagination
3826  * 
3827  */
3828
3829 /**
3830  * @class Roo.bootstrap.Pagination
3831  * @extends Roo.bootstrap.Component
3832  * Bootstrap Pagination class
3833  * @cfg {String} size xs | sm | md | lg
3834  * @cfg {Boolean} inverse false | true
3835  * 
3836  * @constructor
3837  * Create a new Pagination
3838  * @param {Object} config The config object
3839  */
3840
3841 Roo.bootstrap.Pagination = function(config){
3842     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
3843 };
3844
3845 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
3846     
3847     cls: false,
3848     size: false,
3849     inverse: false,
3850     
3851     getAutoCreate : function(){
3852         var cfg = {
3853             tag: 'ul',
3854                 cls: 'pagination'
3855         };
3856         if (this.inverse) {
3857             cfg.cls += ' inverse';
3858         }
3859         if (this.html) {
3860             cfg.html=this.html;
3861         }
3862         if (this.cls) {
3863             cfg.cls += " " + this.cls;
3864         }
3865         return cfg;
3866     }
3867    
3868 });
3869
3870  
3871
3872  /*
3873  * - LGPL
3874  *
3875  * Pagination item
3876  * 
3877  */
3878
3879
3880 /**
3881  * @class Roo.bootstrap.PaginationItem
3882  * @extends Roo.bootstrap.Component
3883  * Bootstrap PaginationItem class
3884  * @cfg {String} html text
3885  * @cfg {String} href the link
3886  * @cfg {Boolean} preventDefault (true | false) default true
3887  * @cfg {Boolean} active (true | false) default false
3888  * 
3889  * 
3890  * @constructor
3891  * Create a new PaginationItem
3892  * @param {Object} config The config object
3893  */
3894
3895
3896 Roo.bootstrap.PaginationItem = function(config){
3897     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
3898     this.addEvents({
3899         // raw events
3900         /**
3901          * @event click
3902          * The raw click event for the entire grid.
3903          * @param {Roo.EventObject} e
3904          */
3905         "click" : true
3906     });
3907 };
3908
3909 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
3910     
3911     href : false,
3912     html : false,
3913     preventDefault: true,
3914     active : false,
3915     cls : false,
3916     
3917     getAutoCreate : function(){
3918         var cfg= {
3919             tag: 'li',
3920             cn: [
3921                 {
3922                     tag : 'a',
3923                     href : this.href ? this.href : '#',
3924                     html : this.html ? this.html : ''
3925                 }
3926             ]
3927         };
3928         
3929         if(this.cls){
3930             cfg.cls = this.cls;
3931         }
3932         
3933         if(this.active){
3934             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
3935         }
3936         
3937         return cfg;
3938     },
3939     
3940     initEvents: function() {
3941         
3942         this.el.on('click', this.onClick, this);
3943         
3944     },
3945     onClick : function(e)
3946     {
3947         Roo.log('PaginationItem on click ');
3948         if(this.preventDefault){
3949             e.preventDefault();
3950         }
3951         
3952         this.fireEvent('click', this, e);
3953     }
3954    
3955 });
3956
3957  
3958
3959  /*
3960  * - LGPL
3961  *
3962  * slider
3963  * 
3964  */
3965
3966
3967 /**
3968  * @class Roo.bootstrap.Slider
3969  * @extends Roo.bootstrap.Component
3970  * Bootstrap Slider class
3971  *    
3972  * @constructor
3973  * Create a new Slider
3974  * @param {Object} config The config object
3975  */
3976
3977 Roo.bootstrap.Slider = function(config){
3978     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
3979 };
3980
3981 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
3982     
3983     getAutoCreate : function(){
3984         
3985         var cfg = {
3986             tag: 'div',
3987             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
3988             cn: [
3989                 {
3990                     tag: 'a',
3991                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
3992                 }
3993             ]
3994         }
3995         
3996         return cfg;
3997     }
3998    
3999 });
4000
4001  /*
4002  * Based on:
4003  * Ext JS Library 1.1.1
4004  * Copyright(c) 2006-2007, Ext JS, LLC.
4005  *
4006  * Originally Released Under LGPL - original licence link has changed is not relivant.
4007  *
4008  * Fork - LGPL
4009  * <script type="text/javascript">
4010  */
4011  
4012
4013 /**
4014  * @class Roo.grid.ColumnModel
4015  * @extends Roo.util.Observable
4016  * This is the default implementation of a ColumnModel used by the Grid. It defines
4017  * the columns in the grid.
4018  * <br>Usage:<br>
4019  <pre><code>
4020  var colModel = new Roo.grid.ColumnModel([
4021         {header: "Ticker", width: 60, sortable: true, locked: true},
4022         {header: "Company Name", width: 150, sortable: true},
4023         {header: "Market Cap.", width: 100, sortable: true},
4024         {header: "$ Sales", width: 100, sortable: true, renderer: money},
4025         {header: "Employees", width: 100, sortable: true, resizable: false}
4026  ]);
4027  </code></pre>
4028  * <p>
4029  
4030  * The config options listed for this class are options which may appear in each
4031  * individual column definition.
4032  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4033  * @constructor
4034  * @param {Object} config An Array of column config objects. See this class's
4035  * config objects for details.
4036 */
4037 Roo.grid.ColumnModel = function(config){
4038         /**
4039      * The config passed into the constructor
4040      */
4041     this.config = config;
4042     this.lookup = {};
4043
4044     // if no id, create one
4045     // if the column does not have a dataIndex mapping,
4046     // map it to the order it is in the config
4047     for(var i = 0, len = config.length; i < len; i++){
4048         var c = config[i];
4049         if(typeof c.dataIndex == "undefined"){
4050             c.dataIndex = i;
4051         }
4052         if(typeof c.renderer == "string"){
4053             c.renderer = Roo.util.Format[c.renderer];
4054         }
4055         if(typeof c.id == "undefined"){
4056             c.id = Roo.id();
4057         }
4058         if(c.editor && c.editor.xtype){
4059             c.editor  = Roo.factory(c.editor, Roo.grid);
4060         }
4061         if(c.editor && c.editor.isFormField){
4062             c.editor = new Roo.grid.GridEditor(c.editor);
4063         }
4064         this.lookup[c.id] = c;
4065     }
4066
4067     /**
4068      * The width of columns which have no width specified (defaults to 100)
4069      * @type Number
4070      */
4071     this.defaultWidth = 100;
4072
4073     /**
4074      * Default sortable of columns which have no sortable specified (defaults to false)
4075      * @type Boolean
4076      */
4077     this.defaultSortable = false;
4078
4079     this.addEvents({
4080         /**
4081              * @event widthchange
4082              * Fires when the width of a column changes.
4083              * @param {ColumnModel} this
4084              * @param {Number} columnIndex The column index
4085              * @param {Number} newWidth The new width
4086              */
4087             "widthchange": true,
4088         /**
4089              * @event headerchange
4090              * Fires when the text of a header changes.
4091              * @param {ColumnModel} this
4092              * @param {Number} columnIndex The column index
4093              * @param {Number} newText The new header text
4094              */
4095             "headerchange": true,
4096         /**
4097              * @event hiddenchange
4098              * Fires when a column is hidden or "unhidden".
4099              * @param {ColumnModel} this
4100              * @param {Number} columnIndex The column index
4101              * @param {Boolean} hidden true if hidden, false otherwise
4102              */
4103             "hiddenchange": true,
4104             /**
4105          * @event columnmoved
4106          * Fires when a column is moved.
4107          * @param {ColumnModel} this
4108          * @param {Number} oldIndex
4109          * @param {Number} newIndex
4110          */
4111         "columnmoved" : true,
4112         /**
4113          * @event columlockchange
4114          * Fires when a column's locked state is changed
4115          * @param {ColumnModel} this
4116          * @param {Number} colIndex
4117          * @param {Boolean} locked true if locked
4118          */
4119         "columnlockchange" : true
4120     });
4121     Roo.grid.ColumnModel.superclass.constructor.call(this);
4122 };
4123 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4124     /**
4125      * @cfg {String} header The header text to display in the Grid view.
4126      */
4127     /**
4128      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4129      * {@link Roo.data.Record} definition from which to draw the column's value. If not
4130      * specified, the column's index is used as an index into the Record's data Array.
4131      */
4132     /**
4133      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4134      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4135      */
4136     /**
4137      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4138      * Defaults to the value of the {@link #defaultSortable} property.
4139      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4140      */
4141     /**
4142      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
4143      */
4144     /**
4145      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
4146      */
4147     /**
4148      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4149      */
4150     /**
4151      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4152      */
4153     /**
4154      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4155      * given the cell's data value. See {@link #setRenderer}. If not specified, the
4156      * default renderer uses the raw data value. If an object is returned (bootstrap only)
4157      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4158      */
4159        /**
4160      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
4161      */
4162     /**
4163      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
4164      */
4165
4166     /**
4167      * Returns the id of the column at the specified index.
4168      * @param {Number} index The column index
4169      * @return {String} the id
4170      */
4171     getColumnId : function(index){
4172         return this.config[index].id;
4173     },
4174
4175     /**
4176      * Returns the column for a specified id.
4177      * @param {String} id The column id
4178      * @return {Object} the column
4179      */
4180     getColumnById : function(id){
4181         return this.lookup[id];
4182     },
4183
4184     
4185     /**
4186      * Returns the column for a specified dataIndex.
4187      * @param {String} dataIndex The column dataIndex
4188      * @return {Object|Boolean} the column or false if not found
4189      */
4190     getColumnByDataIndex: function(dataIndex){
4191         var index = this.findColumnIndex(dataIndex);
4192         return index > -1 ? this.config[index] : false;
4193     },
4194     
4195     /**
4196      * Returns the index for a specified column id.
4197      * @param {String} id The column id
4198      * @return {Number} the index, or -1 if not found
4199      */
4200     getIndexById : function(id){
4201         for(var i = 0, len = this.config.length; i < len; i++){
4202             if(this.config[i].id == id){
4203                 return i;
4204             }
4205         }
4206         return -1;
4207     },
4208     
4209     /**
4210      * Returns the index for a specified column dataIndex.
4211      * @param {String} dataIndex The column dataIndex
4212      * @return {Number} the index, or -1 if not found
4213      */
4214     
4215     findColumnIndex : function(dataIndex){
4216         for(var i = 0, len = this.config.length; i < len; i++){
4217             if(this.config[i].dataIndex == dataIndex){
4218                 return i;
4219             }
4220         }
4221         return -1;
4222     },
4223     
4224     
4225     moveColumn : function(oldIndex, newIndex){
4226         var c = this.config[oldIndex];
4227         this.config.splice(oldIndex, 1);
4228         this.config.splice(newIndex, 0, c);
4229         this.dataMap = null;
4230         this.fireEvent("columnmoved", this, oldIndex, newIndex);
4231     },
4232
4233     isLocked : function(colIndex){
4234         return this.config[colIndex].locked === true;
4235     },
4236
4237     setLocked : function(colIndex, value, suppressEvent){
4238         if(this.isLocked(colIndex) == value){
4239             return;
4240         }
4241         this.config[colIndex].locked = value;
4242         if(!suppressEvent){
4243             this.fireEvent("columnlockchange", this, colIndex, value);
4244         }
4245     },
4246
4247     getTotalLockedWidth : function(){
4248         var totalWidth = 0;
4249         for(var i = 0; i < this.config.length; i++){
4250             if(this.isLocked(i) && !this.isHidden(i)){
4251                 this.totalWidth += this.getColumnWidth(i);
4252             }
4253         }
4254         return totalWidth;
4255     },
4256
4257     getLockedCount : function(){
4258         for(var i = 0, len = this.config.length; i < len; i++){
4259             if(!this.isLocked(i)){
4260                 return i;
4261             }
4262         }
4263     },
4264
4265     /**
4266      * Returns the number of columns.
4267      * @return {Number}
4268      */
4269     getColumnCount : function(visibleOnly){
4270         if(visibleOnly === true){
4271             var c = 0;
4272             for(var i = 0, len = this.config.length; i < len; i++){
4273                 if(!this.isHidden(i)){
4274                     c++;
4275                 }
4276             }
4277             return c;
4278         }
4279         return this.config.length;
4280     },
4281
4282     /**
4283      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
4284      * @param {Function} fn
4285      * @param {Object} scope (optional)
4286      * @return {Array} result
4287      */
4288     getColumnsBy : function(fn, scope){
4289         var r = [];
4290         for(var i = 0, len = this.config.length; i < len; i++){
4291             var c = this.config[i];
4292             if(fn.call(scope||this, c, i) === true){
4293                 r[r.length] = c;
4294             }
4295         }
4296         return r;
4297     },
4298
4299     /**
4300      * Returns true if the specified column is sortable.
4301      * @param {Number} col The column index
4302      * @return {Boolean}
4303      */
4304     isSortable : function(col){
4305         if(typeof this.config[col].sortable == "undefined"){
4306             return this.defaultSortable;
4307         }
4308         return this.config[col].sortable;
4309     },
4310
4311     /**
4312      * Returns the rendering (formatting) function defined for the column.
4313      * @param {Number} col The column index.
4314      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
4315      */
4316     getRenderer : function(col){
4317         if(!this.config[col].renderer){
4318             return Roo.grid.ColumnModel.defaultRenderer;
4319         }
4320         return this.config[col].renderer;
4321     },
4322
4323     /**
4324      * Sets the rendering (formatting) function for a column.
4325      * @param {Number} col The column index
4326      * @param {Function} fn The function to use to process the cell's raw data
4327      * to return HTML markup for the grid view. The render function is called with
4328      * the following parameters:<ul>
4329      * <li>Data value.</li>
4330      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
4331      * <li>css A CSS style string to apply to the table cell.</li>
4332      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
4333      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
4334      * <li>Row index</li>
4335      * <li>Column index</li>
4336      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
4337      */
4338     setRenderer : function(col, fn){
4339         this.config[col].renderer = fn;
4340     },
4341
4342     /**
4343      * Returns the width for the specified column.
4344      * @param {Number} col The column index
4345      * @return {Number}
4346      */
4347     getColumnWidth : function(col){
4348         return this.config[col].width * 1 || this.defaultWidth;
4349     },
4350
4351     /**
4352      * Sets the width for a column.
4353      * @param {Number} col The column index
4354      * @param {Number} width The new width
4355      */
4356     setColumnWidth : function(col, width, suppressEvent){
4357         this.config[col].width = width;
4358         this.totalWidth = null;
4359         if(!suppressEvent){
4360              this.fireEvent("widthchange", this, col, width);
4361         }
4362     },
4363
4364     /**
4365      * Returns the total width of all columns.
4366      * @param {Boolean} includeHidden True to include hidden column widths
4367      * @return {Number}
4368      */
4369     getTotalWidth : function(includeHidden){
4370         if(!this.totalWidth){
4371             this.totalWidth = 0;
4372             for(var i = 0, len = this.config.length; i < len; i++){
4373                 if(includeHidden || !this.isHidden(i)){
4374                     this.totalWidth += this.getColumnWidth(i);
4375                 }
4376             }
4377         }
4378         return this.totalWidth;
4379     },
4380
4381     /**
4382      * Returns the header for the specified column.
4383      * @param {Number} col The column index
4384      * @return {String}
4385      */
4386     getColumnHeader : function(col){
4387         return this.config[col].header;
4388     },
4389
4390     /**
4391      * Sets the header for a column.
4392      * @param {Number} col The column index
4393      * @param {String} header The new header
4394      */
4395     setColumnHeader : function(col, header){
4396         this.config[col].header = header;
4397         this.fireEvent("headerchange", this, col, header);
4398     },
4399
4400     /**
4401      * Returns the tooltip for the specified column.
4402      * @param {Number} col The column index
4403      * @return {String}
4404      */
4405     getColumnTooltip : function(col){
4406             return this.config[col].tooltip;
4407     },
4408     /**
4409      * Sets the tooltip for a column.
4410      * @param {Number} col The column index
4411      * @param {String} tooltip The new tooltip
4412      */
4413     setColumnTooltip : function(col, tooltip){
4414             this.config[col].tooltip = tooltip;
4415     },
4416
4417     /**
4418      * Returns the dataIndex for the specified column.
4419      * @param {Number} col The column index
4420      * @return {Number}
4421      */
4422     getDataIndex : function(col){
4423         return this.config[col].dataIndex;
4424     },
4425
4426     /**
4427      * Sets the dataIndex for a column.
4428      * @param {Number} col The column index
4429      * @param {Number} dataIndex The new dataIndex
4430      */
4431     setDataIndex : function(col, dataIndex){
4432         this.config[col].dataIndex = dataIndex;
4433     },
4434
4435     
4436     
4437     /**
4438      * Returns true if the cell is editable.
4439      * @param {Number} colIndex The column index
4440      * @param {Number} rowIndex The row index
4441      * @return {Boolean}
4442      */
4443     isCellEditable : function(colIndex, rowIndex){
4444         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
4445     },
4446
4447     /**
4448      * Returns the editor defined for the cell/column.
4449      * return false or null to disable editing.
4450      * @param {Number} colIndex The column index
4451      * @param {Number} rowIndex The row index
4452      * @return {Object}
4453      */
4454     getCellEditor : function(colIndex, rowIndex){
4455         return this.config[colIndex].editor;
4456     },
4457
4458     /**
4459      * Sets if a column is editable.
4460      * @param {Number} col The column index
4461      * @param {Boolean} editable True if the column is editable
4462      */
4463     setEditable : function(col, editable){
4464         this.config[col].editable = editable;
4465     },
4466
4467
4468     /**
4469      * Returns true if the column is hidden.
4470      * @param {Number} colIndex The column index
4471      * @return {Boolean}
4472      */
4473     isHidden : function(colIndex){
4474         return this.config[colIndex].hidden;
4475     },
4476
4477
4478     /**
4479      * Returns true if the column width cannot be changed
4480      */
4481     isFixed : function(colIndex){
4482         return this.config[colIndex].fixed;
4483     },
4484
4485     /**
4486      * Returns true if the column can be resized
4487      * @return {Boolean}
4488      */
4489     isResizable : function(colIndex){
4490         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
4491     },
4492     /**
4493      * Sets if a column is hidden.
4494      * @param {Number} colIndex The column index
4495      * @param {Boolean} hidden True if the column is hidden
4496      */
4497     setHidden : function(colIndex, hidden){
4498         this.config[colIndex].hidden = hidden;
4499         this.totalWidth = null;
4500         this.fireEvent("hiddenchange", this, colIndex, hidden);
4501     },
4502
4503     /**
4504      * Sets the editor for a column.
4505      * @param {Number} col The column index
4506      * @param {Object} editor The editor object
4507      */
4508     setEditor : function(col, editor){
4509         this.config[col].editor = editor;
4510     }
4511 });
4512
4513 Roo.grid.ColumnModel.defaultRenderer = function(value){
4514         if(typeof value == "string" && value.length < 1){
4515             return "&#160;";
4516         }
4517         return value;
4518 };
4519
4520 // Alias for backwards compatibility
4521 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
4522 /*
4523  * Based on:
4524  * Ext JS Library 1.1.1
4525  * Copyright(c) 2006-2007, Ext JS, LLC.
4526  *
4527  * Originally Released Under LGPL - original licence link has changed is not relivant.
4528  *
4529  * Fork - LGPL
4530  * <script type="text/javascript">
4531  */
4532  
4533 /**
4534  * @class Roo.LoadMask
4535  * A simple utility class for generically masking elements while loading data.  If the element being masked has
4536  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
4537  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
4538  * element's UpdateManager load indicator and will be destroyed after the initial load.
4539  * @constructor
4540  * Create a new LoadMask
4541  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
4542  * @param {Object} config The config object
4543  */
4544 Roo.LoadMask = function(el, config){
4545     this.el = Roo.get(el);
4546     Roo.apply(this, config);
4547     if(this.store){
4548         this.store.on('beforeload', this.onBeforeLoad, this);
4549         this.store.on('load', this.onLoad, this);
4550         this.store.on('loadexception', this.onLoadException, this);
4551         this.removeMask = false;
4552     }else{
4553         var um = this.el.getUpdateManager();
4554         um.showLoadIndicator = false; // disable the default indicator
4555         um.on('beforeupdate', this.onBeforeLoad, this);
4556         um.on('update', this.onLoad, this);
4557         um.on('failure', this.onLoad, this);
4558         this.removeMask = true;
4559     }
4560 };
4561
4562 Roo.LoadMask.prototype = {
4563     /**
4564      * @cfg {Boolean} removeMask
4565      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
4566      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
4567      */
4568     /**
4569      * @cfg {String} msg
4570      * The text to display in a centered loading message box (defaults to 'Loading...')
4571      */
4572     msg : 'Loading...',
4573     /**
4574      * @cfg {String} msgCls
4575      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
4576      */
4577     msgCls : 'x-mask-loading',
4578
4579     /**
4580      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
4581      * @type Boolean
4582      */
4583     disabled: false,
4584
4585     /**
4586      * Disables the mask to prevent it from being displayed
4587      */
4588     disable : function(){
4589        this.disabled = true;
4590     },
4591
4592     /**
4593      * Enables the mask so that it can be displayed
4594      */
4595     enable : function(){
4596         this.disabled = false;
4597     },
4598     
4599     onLoadException : function()
4600     {
4601         Roo.log(arguments);
4602         
4603         if (typeof(arguments[3]) != 'undefined') {
4604             Roo.MessageBox.alert("Error loading",arguments[3]);
4605         } 
4606         /*
4607         try {
4608             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
4609                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
4610             }   
4611         } catch(e) {
4612             
4613         }
4614         */
4615     
4616         
4617         
4618         this.el.unmask(this.removeMask);
4619     },
4620     // private
4621     onLoad : function()
4622     {
4623         this.el.unmask(this.removeMask);
4624     },
4625
4626     // private
4627     onBeforeLoad : function(){
4628         if(!this.disabled){
4629             this.el.mask(this.msg, this.msgCls);
4630         }
4631     },
4632
4633     // private
4634     destroy : function(){
4635         if(this.store){
4636             this.store.un('beforeload', this.onBeforeLoad, this);
4637             this.store.un('load', this.onLoad, this);
4638             this.store.un('loadexception', this.onLoadException, this);
4639         }else{
4640             var um = this.el.getUpdateManager();
4641             um.un('beforeupdate', this.onBeforeLoad, this);
4642             um.un('update', this.onLoad, this);
4643             um.un('failure', this.onLoad, this);
4644         }
4645     }
4646 };/*
4647  * - LGPL
4648  *
4649  * table
4650  * 
4651  */
4652
4653 /**
4654  * @class Roo.bootstrap.Table
4655  * @extends Roo.bootstrap.Component
4656  * Bootstrap Table class
4657  * @cfg {String} cls table class
4658  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
4659  * @cfg {String} bgcolor Specifies the background color for a table
4660  * @cfg {Number} border Specifies whether the table cells should have borders or not
4661  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
4662  * @cfg {Number} cellspacing Specifies the space between cells
4663  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
4664  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
4665  * @cfg {String} sortable Specifies that the table should be sortable
4666  * @cfg {String} summary Specifies a summary of the content of a table
4667  * @cfg {Number} width Specifies the width of a table
4668  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
4669  * 
4670  * @cfg {boolean} striped Should the rows be alternative striped
4671  * @cfg {boolean} bordered Add borders to the table
4672  * @cfg {boolean} hover Add hover highlighting
4673  * @cfg {boolean} condensed Format condensed
4674  * @cfg {boolean} responsive Format condensed
4675  * @cfg {Boolean} loadMask (true|false) default false
4676  * @cfg {Boolean} tfoot (true|false) generate tfoot, default true
4677  * @cfg {Boolean} thead (true|false) generate thead, default true
4678  * @cfg {Boolean} RowSelection (true|false) default false
4679  * @cfg {Boolean} CellSelection (true|false) default false
4680  *
4681  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
4682  
4683  * 
4684  * @constructor
4685  * Create a new Table
4686  * @param {Object} config The config object
4687  */
4688
4689 Roo.bootstrap.Table = function(config){
4690     Roo.bootstrap.Table.superclass.constructor.call(this, config);
4691     
4692     if (this.sm) {
4693         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
4694         this.sm = this.selModel;
4695         this.sm.xmodule = this.xmodule || false;
4696     }
4697     if (this.cm && typeof(this.cm.config) == 'undefined') {
4698         this.colModel = new Roo.grid.ColumnModel(this.cm);
4699         this.cm = this.colModel;
4700         this.cm.xmodule = this.xmodule || false;
4701     }
4702     if (this.store) {
4703         this.store= Roo.factory(this.store, Roo.data);
4704         this.ds = this.store;
4705         this.ds.xmodule = this.xmodule || false;
4706          
4707     }
4708     if (this.footer && this.store) {
4709         this.footer.dataSource = this.ds;
4710         this.footer = Roo.factory(this.footer);
4711     }
4712     
4713     /** @private */
4714     this.addEvents({
4715         /**
4716          * @event cellclick
4717          * Fires when a cell is clicked
4718          * @param {Roo.bootstrap.Table} this
4719          * @param {Roo.Element} el
4720          * @param {Number} rowIndex
4721          * @param {Number} columnIndex
4722          * @param {Roo.EventObject} e
4723          */
4724         "cellclick" : true,
4725         /**
4726          * @event celldblclick
4727          * Fires when a cell is double clicked
4728          * @param {Roo.bootstrap.Table} this
4729          * @param {Roo.Element} el
4730          * @param {Number} rowIndex
4731          * @param {Number} columnIndex
4732          * @param {Roo.EventObject} e
4733          */
4734         "celldblclick" : true,
4735         /**
4736          * @event rowclick
4737          * Fires when a row is clicked
4738          * @param {Roo.bootstrap.Table} this
4739          * @param {Roo.Element} el
4740          * @param {Number} rowIndex
4741          * @param {Roo.EventObject} e
4742          */
4743         "rowclick" : true,
4744         /**
4745          * @event rowdblclick
4746          * Fires when a row is double clicked
4747          * @param {Roo.bootstrap.Table} this
4748          * @param {Roo.Element} el
4749          * @param {Number} rowIndex
4750          * @param {Roo.EventObject} e
4751          */
4752         "rowdblclick" : true,
4753         /**
4754          * @event mouseover
4755          * Fires when a mouseover occur
4756          * @param {Roo.bootstrap.Table} this
4757          * @param {Roo.Element} el
4758          * @param {Number} rowIndex
4759          * @param {Number} columnIndex
4760          * @param {Roo.EventObject} e
4761          */
4762         "mouseover" : true,
4763         /**
4764          * @event mouseout
4765          * Fires when a mouseout occur
4766          * @param {Roo.bootstrap.Table} this
4767          * @param {Roo.Element} el
4768          * @param {Number} rowIndex
4769          * @param {Number} columnIndex
4770          * @param {Roo.EventObject} e
4771          */
4772         "mouseout" : true,
4773         /**
4774          * @event rowclass
4775          * Fires when a row is rendered, so you can change add a style to it.
4776          * @param {Roo.bootstrap.Table} this
4777          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
4778          */
4779         'rowclass' : true
4780         
4781     });
4782 };
4783
4784 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
4785     
4786     cls: false,
4787     align: false,
4788     bgcolor: false,
4789     border: false,
4790     cellpadding: false,
4791     cellspacing: false,
4792     frame: false,
4793     rules: false,
4794     sortable: false,
4795     summary: false,
4796     width: false,
4797     striped : false,
4798     bordered: false,
4799     hover:  false,
4800     condensed : false,
4801     responsive : false,
4802     sm : false,
4803     cm : false,
4804     store : false,
4805     loadMask : false,
4806     tfoot : true,
4807     thead : true,
4808     RowSelection : false,
4809     CellSelection : false,
4810     layout : false,
4811     
4812     // Roo.Element - the tbody
4813     mainBody: false, 
4814     
4815     getAutoCreate : function(){
4816         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
4817         
4818         cfg = {
4819             tag: 'table',
4820             cls : 'table',
4821             cn : []
4822         }
4823             
4824         if (this.striped) {
4825             cfg.cls += ' table-striped';
4826         }
4827         
4828         if (this.hover) {
4829             cfg.cls += ' table-hover';
4830         }
4831         if (this.bordered) {
4832             cfg.cls += ' table-bordered';
4833         }
4834         if (this.condensed) {
4835             cfg.cls += ' table-condensed';
4836         }
4837         if (this.responsive) {
4838             cfg.cls += ' table-responsive';
4839         }
4840         
4841         if (this.cls) {
4842             cfg.cls+=  ' ' +this.cls;
4843         }
4844         
4845         // this lot should be simplifed...
4846         
4847         if (this.align) {
4848             cfg.align=this.align;
4849         }
4850         if (this.bgcolor) {
4851             cfg.bgcolor=this.bgcolor;
4852         }
4853         if (this.border) {
4854             cfg.border=this.border;
4855         }
4856         if (this.cellpadding) {
4857             cfg.cellpadding=this.cellpadding;
4858         }
4859         if (this.cellspacing) {
4860             cfg.cellspacing=this.cellspacing;
4861         }
4862         if (this.frame) {
4863             cfg.frame=this.frame;
4864         }
4865         if (this.rules) {
4866             cfg.rules=this.rules;
4867         }
4868         if (this.sortable) {
4869             cfg.sortable=this.sortable;
4870         }
4871         if (this.summary) {
4872             cfg.summary=this.summary;
4873         }
4874         if (this.width) {
4875             cfg.width=this.width;
4876         }
4877         if (this.layout) {
4878             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
4879         }
4880         
4881         if(this.store || this.cm){
4882             if(this.thead){
4883                 cfg.cn.push(this.renderHeader());
4884             }
4885             
4886             cfg.cn.push(this.renderBody());
4887             
4888             if(this.tfoot){
4889                 cfg.cn.push(this.renderFooter());
4890             }
4891             
4892             cfg.cls+=  ' TableGrid';
4893         }
4894         
4895         return { cn : [ cfg ] };
4896     },
4897     
4898     initEvents : function()
4899     {   
4900         if(!this.store || !this.cm){
4901             return;
4902         }
4903         
4904         //Roo.log('initEvents with ds!!!!');
4905         
4906         this.mainBody = this.el.select('tbody', true).first();
4907         
4908         
4909         var _this = this;
4910         
4911         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
4912             e.on('click', _this.sort, _this);
4913         });
4914         
4915         this.el.on("click", this.onClick, this);
4916         this.el.on("dblclick", this.onDblClick, this);
4917         
4918         this.parent().el.setStyle('position', 'relative');
4919         if (this.footer) {
4920             this.footer.parentId = this.id;
4921             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
4922         }
4923         
4924         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
4925         
4926         this.store.on('load', this.onLoad, this);
4927         this.store.on('beforeload', this.onBeforeLoad, this);
4928         this.store.on('update', this.onUpdate, this);
4929         
4930     },
4931     
4932     onMouseover : function(e, el)
4933     {
4934         var cell = Roo.get(el);
4935         
4936         if(!cell){
4937             return;
4938         }
4939         
4940         if(e.getTarget().nodeName.toLowerCase() != 'td'){
4941             cell = cell.findParent('td', false, true);
4942         }
4943         
4944         var row = cell.findParent('tr', false, true);
4945         var cellIndex = cell.dom.cellIndex;
4946         var rowIndex = row.dom.rowIndex - 1; // start from 0
4947         
4948         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
4949         
4950     },
4951     
4952     onMouseout : function(e, el)
4953     {
4954         var cell = Roo.get(el);
4955         
4956         if(!cell){
4957             return;
4958         }
4959         
4960         if(e.getTarget().nodeName.toLowerCase() != 'td'){
4961             cell = cell.findParent('td', false, true);
4962         }
4963         
4964         var row = cell.findParent('tr', false, true);
4965         var cellIndex = cell.dom.cellIndex;
4966         var rowIndex = row.dom.rowIndex - 1; // start from 0
4967         
4968         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
4969         
4970     },
4971     
4972     onClick : function(e, el)
4973     {
4974         var cell = Roo.get(el);
4975         
4976         if(!cell || (!this.CellSelection && !this.RowSelection)){
4977             return;
4978         }
4979         
4980         
4981         if(e.getTarget().nodeName.toLowerCase() != 'td'){
4982             cell = cell.findParent('td', false, true);
4983         }
4984         
4985         var row = cell.findParent('tr', false, true);
4986         var cellIndex = cell.dom.cellIndex;
4987         var rowIndex = row.dom.rowIndex - 1;
4988         
4989         if(this.CellSelection){
4990             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
4991         }
4992         
4993         if(this.RowSelection){
4994             this.fireEvent('rowclick', this, row, rowIndex, e);
4995         }
4996         
4997         
4998     },
4999     
5000     onDblClick : function(e,el)
5001     {
5002         var cell = Roo.get(el);
5003         
5004         if(!cell || (!this.CellSelection && !this.RowSelection)){
5005             return;
5006         }
5007         
5008         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5009             cell = cell.findParent('td', false, true);
5010         }
5011         
5012         var row = cell.findParent('tr', false, true);
5013         var cellIndex = cell.dom.cellIndex;
5014         var rowIndex = row.dom.rowIndex - 1;
5015         
5016         if(this.CellSelection){
5017             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5018         }
5019         
5020         if(this.RowSelection){
5021             this.fireEvent('rowdblclick', this, row, rowIndex, e);
5022         }
5023     },
5024     
5025     sort : function(e,el)
5026     {
5027         var col = Roo.get(el)
5028         
5029         if(!col.hasClass('sortable')){
5030             return;
5031         }
5032         
5033         var sort = col.attr('sort');
5034         var dir = 'ASC';
5035         
5036         if(col.hasClass('glyphicon-arrow-up')){
5037             dir = 'DESC';
5038         }
5039         
5040         this.store.sortInfo = {field : sort, direction : dir};
5041         
5042         if (this.footer) {
5043             Roo.log("calling footer first");
5044             this.footer.onClick('first');
5045         } else {
5046         
5047             this.store.load({ params : { start : 0 } });
5048         }
5049     },
5050     
5051     renderHeader : function()
5052     {
5053         var header = {
5054             tag: 'thead',
5055             cn : []
5056         };
5057         
5058         var cm = this.cm;
5059         
5060         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5061             
5062             var config = cm.config[i];
5063                     
5064             var c = {
5065                 tag: 'th',
5066                 style : '',
5067                 html: cm.getColumnHeader(i)
5068             };
5069             
5070             if(typeof(config.hidden) != 'undefined' && config.hidden){
5071                 c.style += ' display:none;';
5072             }
5073             
5074             if(typeof(config.dataIndex) != 'undefined'){
5075                 c.sort = config.dataIndex;
5076             }
5077             
5078             if(typeof(config.sortable) != 'undefined' && config.sortable){
5079                 c.cls = 'sortable';
5080             }
5081             
5082             if(typeof(config.align) != 'undefined' && config.align.length){
5083                 c.style += ' text-align:' + config.align + ';';
5084             }
5085             
5086             if(typeof(config.width) != 'undefined'){
5087                 c.style += ' width:' + config.width + 'px;';
5088             }
5089             
5090             header.cn.push(c)
5091         }
5092         
5093         return header;
5094     },
5095     
5096     renderBody : function()
5097     {
5098         var body = {
5099             tag: 'tbody',
5100             cn : [
5101                 {
5102                     tag: 'tr',
5103                     cn : [
5104                         {
5105                             tag : 'td',
5106                             colspan :  this.cm.getColumnCount()
5107                         }
5108                     ]
5109                 }
5110             ]
5111         };
5112         
5113         return body;
5114     },
5115     
5116     renderFooter : function()
5117     {
5118         var footer = {
5119             tag: 'tfoot',
5120             cn : [
5121                 {
5122                     tag: 'tr',
5123                     cn : [
5124                         {
5125                             tag : 'td',
5126                             colspan :  this.cm.getColumnCount()
5127                         }
5128                     ]
5129                 }
5130             ]
5131         };
5132         
5133         return footer;
5134     },
5135     
5136     
5137     
5138     onLoad : function()
5139     {
5140         Roo.log('ds onload');
5141         this.clear();
5142         
5143         var _this = this;
5144         var cm = this.cm;
5145         var ds = this.store;
5146         
5147         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5148             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
5149             
5150             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
5151                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
5152             }
5153             
5154             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
5155                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
5156             }
5157         });
5158         
5159         var tbody =  this.mainBody;
5160         
5161         var renders = [];
5162                     
5163         if(ds.getCount() > 0){
5164             ds.data.each(function(d,rowIndex){
5165                 var row =  this.renderRow(cm, ds, rowIndex);
5166                 
5167                 tbody.createChild(row);
5168                 
5169                 var _this = this;
5170                 
5171                 if(row.cellObjects.length){
5172                     Roo.each(row.cellObjects, function(r){
5173                         _this.renderCellObject(r);
5174                     })
5175                 }
5176                 
5177             }, this);
5178         }
5179         
5180         Roo.each(this.el.select('tbody td', true).elements, function(e){
5181             e.on('mouseover', _this.onMouseover, _this);
5182         });
5183         
5184         Roo.each(this.el.select('tbody td', true).elements, function(e){
5185             e.on('mouseout', _this.onMouseout, _this);
5186         });
5187
5188         //if(this.loadMask){
5189         //    this.maskEl.hide();
5190         //}
5191     },
5192     
5193     
5194     onUpdate : function(ds,record)
5195     {
5196         this.refreshRow(record);
5197     },
5198     onRemove : function(ds, record, index, isUpdate){
5199         if(isUpdate !== true){
5200             this.fireEvent("beforerowremoved", this, index, record);
5201         }
5202         var bt = this.mainBody.dom;
5203         if(bt.rows[index]){
5204             bt.removeChild(bt.rows[index]);
5205         }
5206         
5207         if(isUpdate !== true){
5208             //this.stripeRows(index);
5209             //this.syncRowHeights(index, index);
5210             //this.layout();
5211             this.fireEvent("rowremoved", this, index, record);
5212         }
5213     },
5214     
5215     
5216     refreshRow : function(record){
5217         var ds = this.store, index;
5218         if(typeof record == 'number'){
5219             index = record;
5220             record = ds.getAt(index);
5221         }else{
5222             index = ds.indexOf(record);
5223         }
5224         this.insertRow(ds, index, true);
5225         this.onRemove(ds, record, index+1, true);
5226         //this.syncRowHeights(index, index);
5227         //this.layout();
5228         this.fireEvent("rowupdated", this, index, record);
5229     },
5230     
5231     insertRow : function(dm, rowIndex, isUpdate){
5232         
5233         if(!isUpdate){
5234             this.fireEvent("beforerowsinserted", this, rowIndex);
5235         }
5236             //var s = this.getScrollState();
5237         var row = this.renderRow(this.cm, this.store, rowIndex);
5238         // insert before rowIndex..
5239         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
5240         Roo.log(e);
5241         
5242         var _this = this;
5243                 
5244         if(row.cellObjects.length){
5245             Roo.each(row.cellObjects, function(r){
5246                 _this.renderCellObject(r);
5247             })
5248         }
5249             
5250         if(!isUpdate){
5251             this.fireEvent("rowsinserted", this, rowIndex);
5252             //this.syncRowHeights(firstRow, lastRow);
5253             //this.stripeRows(firstRow);
5254             //this.layout();
5255         }
5256         
5257     },
5258     
5259     
5260     getRowDom : function(rowIndex)
5261     {
5262         // not sure if I need to check this.. but let's do it anyway..
5263         return (this.mainBody.dom.rows && (rowIndex-1) < this.mainBody.dom.rows.length ) ?
5264                 this.mainBody.dom.rows[rowIndex] : false
5265     },
5266     // returns the object tree for a tr..
5267   
5268     
5269     renderRow : function(cm, ds, rowIndex) {
5270         
5271         var d = ds.getAt(rowIndex);
5272         
5273         var row = {
5274             tag : 'tr',
5275             cn : []
5276         };
5277             
5278         var cellObjects = [];
5279         
5280         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5281             var config = cm.config[i];
5282             
5283             var renderer = cm.getRenderer(i);
5284             var value = '';
5285             var id = false;
5286             
5287             if(typeof(renderer) !== 'undefined'){
5288                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
5289             }
5290             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
5291             // and are rendered into the cells after the row is rendered - using the id for the element.
5292             
5293             if(typeof(value) === 'object'){
5294                 id = Roo.id();
5295                 cellObjects.push({
5296                     container : id,
5297                     cfg : value 
5298                 })
5299             }
5300             
5301             var rowcfg = {
5302                 record: d,
5303                 rowIndex : rowIndex,
5304                 colIndex : i,
5305                 rowClass : ''
5306             }
5307
5308             this.fireEvent('rowclass', this, rowcfg);
5309             
5310             var td = {
5311                 tag: 'td',
5312                 cls : rowcfg.rowClass,
5313                 style: '',
5314                 html: (typeof(value) === 'object') ? '' : value
5315             };
5316             
5317             if (id) {
5318                 td.id = id;
5319             }
5320             
5321             if(typeof(config.hidden) != 'undefined' && config.hidden){
5322                 td.style += ' display:none;';
5323             }
5324             
5325             if(typeof(config.align) != 'undefined' && config.align.length){
5326                 td.style += ' text-align:' + config.align + ';';
5327             }
5328             
5329             if(typeof(config.width) != 'undefined'){
5330                 td.style += ' width:' +  config.width + 'px;';
5331             }
5332              
5333             row.cn.push(td);
5334            
5335         }
5336         
5337         row.cellObjects = cellObjects;
5338         
5339         return row;
5340           
5341     },
5342     
5343     
5344     
5345     onBeforeLoad : function()
5346     {
5347         //Roo.log('ds onBeforeLoad');
5348         
5349         //this.clear();
5350         
5351         //if(this.loadMask){
5352         //    this.maskEl.show();
5353         //}
5354     },
5355     
5356     clear : function()
5357     {
5358         this.el.select('tbody', true).first().dom.innerHTML = '';
5359     },
5360     
5361     getSelectionModel : function(){
5362         if(!this.selModel){
5363             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
5364         }
5365         return this.selModel;
5366     },
5367     /*
5368      * Render the Roo.bootstrap object from renderder
5369      */
5370     renderCellObject : function(r)
5371     {
5372         var _this = this;
5373         
5374         var t = r.cfg.render(r.container);
5375         
5376         if(r.cfg.cn){
5377             Roo.each(r.cfg.cn, function(c){
5378                 var child = {
5379                     container: t.getChildContainer(),
5380                     cfg: c
5381                 }
5382                 _this.renderCellObject(child);
5383             })
5384         }
5385     }
5386    
5387 });
5388
5389  
5390
5391  /*
5392  * - LGPL
5393  *
5394  * table cell
5395  * 
5396  */
5397
5398 /**
5399  * @class Roo.bootstrap.TableCell
5400  * @extends Roo.bootstrap.Component
5401  * Bootstrap TableCell class
5402  * @cfg {String} html cell contain text
5403  * @cfg {String} cls cell class
5404  * @cfg {String} tag cell tag (td|th) default td
5405  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
5406  * @cfg {String} align Aligns the content in a cell
5407  * @cfg {String} axis Categorizes cells
5408  * @cfg {String} bgcolor Specifies the background color of a cell
5409  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5410  * @cfg {Number} colspan Specifies the number of columns a cell should span
5411  * @cfg {String} headers Specifies one or more header cells a cell is related to
5412  * @cfg {Number} height Sets the height of a cell
5413  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
5414  * @cfg {Number} rowspan Sets the number of rows a cell should span
5415  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
5416  * @cfg {String} valign Vertical aligns the content in a cell
5417  * @cfg {Number} width Specifies the width of a cell
5418  * 
5419  * @constructor
5420  * Create a new TableCell
5421  * @param {Object} config The config object
5422  */
5423
5424 Roo.bootstrap.TableCell = function(config){
5425     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
5426 };
5427
5428 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
5429     
5430     html: false,
5431     cls: false,
5432     tag: false,
5433     abbr: false,
5434     align: false,
5435     axis: false,
5436     bgcolor: false,
5437     charoff: false,
5438     colspan: false,
5439     headers: false,
5440     height: false,
5441     nowrap: false,
5442     rowspan: false,
5443     scope: false,
5444     valign: false,
5445     width: false,
5446     
5447     
5448     getAutoCreate : function(){
5449         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
5450         
5451         cfg = {
5452             tag: 'td'
5453         }
5454         
5455         if(this.tag){
5456             cfg.tag = this.tag;
5457         }
5458         
5459         if (this.html) {
5460             cfg.html=this.html
5461         }
5462         if (this.cls) {
5463             cfg.cls=this.cls
5464         }
5465         if (this.abbr) {
5466             cfg.abbr=this.abbr
5467         }
5468         if (this.align) {
5469             cfg.align=this.align
5470         }
5471         if (this.axis) {
5472             cfg.axis=this.axis
5473         }
5474         if (this.bgcolor) {
5475             cfg.bgcolor=this.bgcolor
5476         }
5477         if (this.charoff) {
5478             cfg.charoff=this.charoff
5479         }
5480         if (this.colspan) {
5481             cfg.colspan=this.colspan
5482         }
5483         if (this.headers) {
5484             cfg.headers=this.headers
5485         }
5486         if (this.height) {
5487             cfg.height=this.height
5488         }
5489         if (this.nowrap) {
5490             cfg.nowrap=this.nowrap
5491         }
5492         if (this.rowspan) {
5493             cfg.rowspan=this.rowspan
5494         }
5495         if (this.scope) {
5496             cfg.scope=this.scope
5497         }
5498         if (this.valign) {
5499             cfg.valign=this.valign
5500         }
5501         if (this.width) {
5502             cfg.width=this.width
5503         }
5504         
5505         
5506         return cfg;
5507     }
5508    
5509 });
5510
5511  
5512
5513  /*
5514  * - LGPL
5515  *
5516  * table row
5517  * 
5518  */
5519
5520 /**
5521  * @class Roo.bootstrap.TableRow
5522  * @extends Roo.bootstrap.Component
5523  * Bootstrap TableRow class
5524  * @cfg {String} cls row class
5525  * @cfg {String} align Aligns the content in a table row
5526  * @cfg {String} bgcolor Specifies a background color for a table row
5527  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5528  * @cfg {String} valign Vertical aligns the content in a table row
5529  * 
5530  * @constructor
5531  * Create a new TableRow
5532  * @param {Object} config The config object
5533  */
5534
5535 Roo.bootstrap.TableRow = function(config){
5536     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
5537 };
5538
5539 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
5540     
5541     cls: false,
5542     align: false,
5543     bgcolor: false,
5544     charoff: false,
5545     valign: false,
5546     
5547     getAutoCreate : function(){
5548         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
5549         
5550         cfg = {
5551             tag: 'tr'
5552         }
5553             
5554         if(this.cls){
5555             cfg.cls = this.cls;
5556         }
5557         if(this.align){
5558             cfg.align = this.align;
5559         }
5560         if(this.bgcolor){
5561             cfg.bgcolor = this.bgcolor;
5562         }
5563         if(this.charoff){
5564             cfg.charoff = this.charoff;
5565         }
5566         if(this.valign){
5567             cfg.valign = this.valign;
5568         }
5569         
5570         return cfg;
5571     }
5572    
5573 });
5574
5575  
5576
5577  /*
5578  * - LGPL
5579  *
5580  * table body
5581  * 
5582  */
5583
5584 /**
5585  * @class Roo.bootstrap.TableBody
5586  * @extends Roo.bootstrap.Component
5587  * Bootstrap TableBody class
5588  * @cfg {String} cls element class
5589  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
5590  * @cfg {String} align Aligns the content inside the element
5591  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
5592  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
5593  * 
5594  * @constructor
5595  * Create a new TableBody
5596  * @param {Object} config The config object
5597  */
5598
5599 Roo.bootstrap.TableBody = function(config){
5600     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
5601 };
5602
5603 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
5604     
5605     cls: false,
5606     tag: false,
5607     align: false,
5608     charoff: false,
5609     valign: false,
5610     
5611     getAutoCreate : function(){
5612         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
5613         
5614         cfg = {
5615             tag: 'tbody'
5616         }
5617             
5618         if (this.cls) {
5619             cfg.cls=this.cls
5620         }
5621         if(this.tag){
5622             cfg.tag = this.tag;
5623         }
5624         
5625         if(this.align){
5626             cfg.align = this.align;
5627         }
5628         if(this.charoff){
5629             cfg.charoff = this.charoff;
5630         }
5631         if(this.valign){
5632             cfg.valign = this.valign;
5633         }
5634         
5635         return cfg;
5636     }
5637     
5638     
5639 //    initEvents : function()
5640 //    {
5641 //        
5642 //        if(!this.store){
5643 //            return;
5644 //        }
5645 //        
5646 //        this.store = Roo.factory(this.store, Roo.data);
5647 //        this.store.on('load', this.onLoad, this);
5648 //        
5649 //        this.store.load();
5650 //        
5651 //    },
5652 //    
5653 //    onLoad: function () 
5654 //    {   
5655 //        this.fireEvent('load', this);
5656 //    }
5657 //    
5658 //   
5659 });
5660
5661  
5662
5663  /*
5664  * Based on:
5665  * Ext JS Library 1.1.1
5666  * Copyright(c) 2006-2007, Ext JS, LLC.
5667  *
5668  * Originally Released Under LGPL - original licence link has changed is not relivant.
5669  *
5670  * Fork - LGPL
5671  * <script type="text/javascript">
5672  */
5673
5674 // as we use this in bootstrap.
5675 Roo.namespace('Roo.form');
5676  /**
5677  * @class Roo.form.Action
5678  * Internal Class used to handle form actions
5679  * @constructor
5680  * @param {Roo.form.BasicForm} el The form element or its id
5681  * @param {Object} config Configuration options
5682  */
5683
5684  
5685  
5686 // define the action interface
5687 Roo.form.Action = function(form, options){
5688     this.form = form;
5689     this.options = options || {};
5690 };
5691 /**
5692  * Client Validation Failed
5693  * @const 
5694  */
5695 Roo.form.Action.CLIENT_INVALID = 'client';
5696 /**
5697  * Server Validation Failed
5698  * @const 
5699  */
5700 Roo.form.Action.SERVER_INVALID = 'server';
5701  /**
5702  * Connect to Server Failed
5703  * @const 
5704  */
5705 Roo.form.Action.CONNECT_FAILURE = 'connect';
5706 /**
5707  * Reading Data from Server Failed
5708  * @const 
5709  */
5710 Roo.form.Action.LOAD_FAILURE = 'load';
5711
5712 Roo.form.Action.prototype = {
5713     type : 'default',
5714     failureType : undefined,
5715     response : undefined,
5716     result : undefined,
5717
5718     // interface method
5719     run : function(options){
5720
5721     },
5722
5723     // interface method
5724     success : function(response){
5725
5726     },
5727
5728     // interface method
5729     handleResponse : function(response){
5730
5731     },
5732
5733     // default connection failure
5734     failure : function(response){
5735         
5736         this.response = response;
5737         this.failureType = Roo.form.Action.CONNECT_FAILURE;
5738         this.form.afterAction(this, false);
5739     },
5740
5741     processResponse : function(response){
5742         this.response = response;
5743         if(!response.responseText){
5744             return true;
5745         }
5746         this.result = this.handleResponse(response);
5747         return this.result;
5748     },
5749
5750     // utility functions used internally
5751     getUrl : function(appendParams){
5752         var url = this.options.url || this.form.url || this.form.el.dom.action;
5753         if(appendParams){
5754             var p = this.getParams();
5755             if(p){
5756                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
5757             }
5758         }
5759         return url;
5760     },
5761
5762     getMethod : function(){
5763         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
5764     },
5765
5766     getParams : function(){
5767         var bp = this.form.baseParams;
5768         var p = this.options.params;
5769         if(p){
5770             if(typeof p == "object"){
5771                 p = Roo.urlEncode(Roo.applyIf(p, bp));
5772             }else if(typeof p == 'string' && bp){
5773                 p += '&' + Roo.urlEncode(bp);
5774             }
5775         }else if(bp){
5776             p = Roo.urlEncode(bp);
5777         }
5778         return p;
5779     },
5780
5781     createCallback : function(){
5782         return {
5783             success: this.success,
5784             failure: this.failure,
5785             scope: this,
5786             timeout: (this.form.timeout*1000),
5787             upload: this.form.fileUpload ? this.success : undefined
5788         };
5789     }
5790 };
5791
5792 Roo.form.Action.Submit = function(form, options){
5793     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
5794 };
5795
5796 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
5797     type : 'submit',
5798
5799     haveProgress : false,
5800     uploadComplete : false,
5801     
5802     // uploadProgress indicator.
5803     uploadProgress : function()
5804     {
5805         if (!this.form.progressUrl) {
5806             return;
5807         }
5808         
5809         if (!this.haveProgress) {
5810             Roo.MessageBox.progress("Uploading", "Uploading");
5811         }
5812         if (this.uploadComplete) {
5813            Roo.MessageBox.hide();
5814            return;
5815         }
5816         
5817         this.haveProgress = true;
5818    
5819         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
5820         
5821         var c = new Roo.data.Connection();
5822         c.request({
5823             url : this.form.progressUrl,
5824             params: {
5825                 id : uid
5826             },
5827             method: 'GET',
5828             success : function(req){
5829                //console.log(data);
5830                 var rdata = false;
5831                 var edata;
5832                 try  {
5833                    rdata = Roo.decode(req.responseText)
5834                 } catch (e) {
5835                     Roo.log("Invalid data from server..");
5836                     Roo.log(edata);
5837                     return;
5838                 }
5839                 if (!rdata || !rdata.success) {
5840                     Roo.log(rdata);
5841                     Roo.MessageBox.alert(Roo.encode(rdata));
5842                     return;
5843                 }
5844                 var data = rdata.data;
5845                 
5846                 if (this.uploadComplete) {
5847                    Roo.MessageBox.hide();
5848                    return;
5849                 }
5850                    
5851                 if (data){
5852                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
5853                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
5854                     );
5855                 }
5856                 this.uploadProgress.defer(2000,this);
5857             },
5858        
5859             failure: function(data) {
5860                 Roo.log('progress url failed ');
5861                 Roo.log(data);
5862             },
5863             scope : this
5864         });
5865            
5866     },
5867     
5868     
5869     run : function()
5870     {
5871         // run get Values on the form, so it syncs any secondary forms.
5872         this.form.getValues();
5873         
5874         var o = this.options;
5875         var method = this.getMethod();
5876         var isPost = method == 'POST';
5877         if(o.clientValidation === false || this.form.isValid()){
5878             
5879             if (this.form.progressUrl) {
5880                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
5881                     (new Date() * 1) + '' + Math.random());
5882                     
5883             } 
5884             
5885             
5886             Roo.Ajax.request(Roo.apply(this.createCallback(), {
5887                 form:this.form.el.dom,
5888                 url:this.getUrl(!isPost),
5889                 method: method,
5890                 params:isPost ? this.getParams() : null,
5891                 isUpload: this.form.fileUpload
5892             }));
5893             
5894             this.uploadProgress();
5895
5896         }else if (o.clientValidation !== false){ // client validation failed
5897             this.failureType = Roo.form.Action.CLIENT_INVALID;
5898             this.form.afterAction(this, false);
5899         }
5900     },
5901
5902     success : function(response)
5903     {
5904         this.uploadComplete= true;
5905         if (this.haveProgress) {
5906             Roo.MessageBox.hide();
5907         }
5908         
5909         
5910         var result = this.processResponse(response);
5911         if(result === true || result.success){
5912             this.form.afterAction(this, true);
5913             return;
5914         }
5915         if(result.errors){
5916             this.form.markInvalid(result.errors);
5917             this.failureType = Roo.form.Action.SERVER_INVALID;
5918         }
5919         this.form.afterAction(this, false);
5920     },
5921     failure : function(response)
5922     {
5923         this.uploadComplete= true;
5924         if (this.haveProgress) {
5925             Roo.MessageBox.hide();
5926         }
5927         
5928         this.response = response;
5929         this.failureType = Roo.form.Action.CONNECT_FAILURE;
5930         this.form.afterAction(this, false);
5931     },
5932     
5933     handleResponse : function(response){
5934         if(this.form.errorReader){
5935             var rs = this.form.errorReader.read(response);
5936             var errors = [];
5937             if(rs.records){
5938                 for(var i = 0, len = rs.records.length; i < len; i++) {
5939                     var r = rs.records[i];
5940                     errors[i] = r.data;
5941                 }
5942             }
5943             if(errors.length < 1){
5944                 errors = null;
5945             }
5946             return {
5947                 success : rs.success,
5948                 errors : errors
5949             };
5950         }
5951         var ret = false;
5952         try {
5953             ret = Roo.decode(response.responseText);
5954         } catch (e) {
5955             ret = {
5956                 success: false,
5957                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
5958                 errors : []
5959             };
5960         }
5961         return ret;
5962         
5963     }
5964 });
5965
5966
5967 Roo.form.Action.Load = function(form, options){
5968     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
5969     this.reader = this.form.reader;
5970 };
5971
5972 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
5973     type : 'load',
5974
5975     run : function(){
5976         
5977         Roo.Ajax.request(Roo.apply(
5978                 this.createCallback(), {
5979                     method:this.getMethod(),
5980                     url:this.getUrl(false),
5981                     params:this.getParams()
5982         }));
5983     },
5984
5985     success : function(response){
5986         
5987         var result = this.processResponse(response);
5988         if(result === true || !result.success || !result.data){
5989             this.failureType = Roo.form.Action.LOAD_FAILURE;
5990             this.form.afterAction(this, false);
5991             return;
5992         }
5993         this.form.clearInvalid();
5994         this.form.setValues(result.data);
5995         this.form.afterAction(this, true);
5996     },
5997
5998     handleResponse : function(response){
5999         if(this.form.reader){
6000             var rs = this.form.reader.read(response);
6001             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
6002             return {
6003                 success : rs.success,
6004                 data : data
6005             };
6006         }
6007         return Roo.decode(response.responseText);
6008     }
6009 });
6010
6011 Roo.form.Action.ACTION_TYPES = {
6012     'load' : Roo.form.Action.Load,
6013     'submit' : Roo.form.Action.Submit
6014 };/*
6015  * - LGPL
6016  *
6017  * form
6018  * 
6019  */
6020
6021 /**
6022  * @class Roo.bootstrap.Form
6023  * @extends Roo.bootstrap.Component
6024  * Bootstrap Form class
6025  * @cfg {String} method  GET | POST (default POST)
6026  * @cfg {String} labelAlign top | left (default top)
6027   * @cfg {String} align left  | right - for navbars
6028
6029  * 
6030  * @constructor
6031  * Create a new Form
6032  * @param {Object} config The config object
6033  */
6034
6035
6036 Roo.bootstrap.Form = function(config){
6037     Roo.bootstrap.Form.superclass.constructor.call(this, config);
6038     this.addEvents({
6039         /**
6040          * @event clientvalidation
6041          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
6042          * @param {Form} this
6043          * @param {Boolean} valid true if the form has passed client-side validation
6044          */
6045         clientvalidation: true,
6046         /**
6047          * @event beforeaction
6048          * Fires before any action is performed. Return false to cancel the action.
6049          * @param {Form} this
6050          * @param {Action} action The action to be performed
6051          */
6052         beforeaction: true,
6053         /**
6054          * @event actionfailed
6055          * Fires when an action fails.
6056          * @param {Form} this
6057          * @param {Action} action The action that failed
6058          */
6059         actionfailed : true,
6060         /**
6061          * @event actioncomplete
6062          * Fires when an action is completed.
6063          * @param {Form} this
6064          * @param {Action} action The action that completed
6065          */
6066         actioncomplete : true
6067     });
6068     
6069 };
6070
6071 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
6072       
6073      /**
6074      * @cfg {String} method
6075      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
6076      */
6077     method : 'POST',
6078     /**
6079      * @cfg {String} url
6080      * The URL to use for form actions if one isn't supplied in the action options.
6081      */
6082     /**
6083      * @cfg {Boolean} fileUpload
6084      * Set to true if this form is a file upload.
6085      */
6086      
6087     /**
6088      * @cfg {Object} baseParams
6089      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
6090      */
6091       
6092     /**
6093      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
6094      */
6095     timeout: 30,
6096     /**
6097      * @cfg {Sting} align (left|right) for navbar forms
6098      */
6099     align : 'left',
6100
6101     // private
6102     activeAction : null,
6103  
6104     /**
6105      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
6106      * element by passing it or its id or mask the form itself by passing in true.
6107      * @type Mixed
6108      */
6109     waitMsgTarget : false,
6110     
6111      
6112     
6113     /**
6114      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
6115      * element by passing it or its id or mask the form itself by passing in true.
6116      * @type Mixed
6117      */
6118     
6119     getAutoCreate : function(){
6120         
6121         var cfg = {
6122             tag: 'form',
6123             method : this.method || 'POST',
6124             id : this.id || Roo.id(),
6125             cls : ''
6126         }
6127         if (this.parent().xtype.match(/^Nav/)) {
6128             cfg.cls = 'navbar-form navbar-' + this.align;
6129             
6130         }
6131         
6132         if (this.labelAlign == 'left' ) {
6133             cfg.cls += ' form-horizontal';
6134         }
6135         
6136         
6137         return cfg;
6138     },
6139     initEvents : function()
6140     {
6141         this.el.on('submit', this.onSubmit, this);
6142         // this was added as random key presses on the form where triggering form submit.
6143         this.el.on('keypress', function(e) {
6144             if (e.getCharCode() != 13) {
6145                 return true;
6146             }
6147             // we might need to allow it for textareas.. and some other items.
6148             // check e.getTarget().
6149             
6150             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
6151                 return true;
6152             }
6153         
6154             Roo.log("keypress blocked");
6155             
6156             e.preventDefault();
6157             return false;
6158         });
6159         
6160     },
6161     // private
6162     onSubmit : function(e){
6163         e.stopEvent();
6164     },
6165     
6166      /**
6167      * Returns true if client-side validation on the form is successful.
6168      * @return Boolean
6169      */
6170     isValid : function(){
6171         var items = this.getItems();
6172         var valid = true;
6173         items.each(function(f){
6174            if(!f.validate()){
6175                valid = false;
6176                
6177            }
6178         });
6179         return valid;
6180     },
6181     /**
6182      * Returns true if any fields in this form have changed since their original load.
6183      * @return Boolean
6184      */
6185     isDirty : function(){
6186         var dirty = false;
6187         var items = this.getItems();
6188         items.each(function(f){
6189            if(f.isDirty()){
6190                dirty = true;
6191                return false;
6192            }
6193            return true;
6194         });
6195         return dirty;
6196     },
6197      /**
6198      * Performs a predefined action (submit or load) or custom actions you define on this form.
6199      * @param {String} actionName The name of the action type
6200      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
6201      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
6202      * accept other config options):
6203      * <pre>
6204 Property          Type             Description
6205 ----------------  ---------------  ----------------------------------------------------------------------------------
6206 url               String           The url for the action (defaults to the form's url)
6207 method            String           The form method to use (defaults to the form's method, or POST if not defined)
6208 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
6209 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
6210                                    validate the form on the client (defaults to false)
6211      * </pre>
6212      * @return {BasicForm} this
6213      */
6214     doAction : function(action, options){
6215         if(typeof action == 'string'){
6216             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
6217         }
6218         if(this.fireEvent('beforeaction', this, action) !== false){
6219             this.beforeAction(action);
6220             action.run.defer(100, action);
6221         }
6222         return this;
6223     },
6224     
6225     // private
6226     beforeAction : function(action){
6227         var o = action.options;
6228         
6229         // not really supported yet.. ??
6230         
6231         //if(this.waitMsgTarget === true){
6232             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6233         //}else if(this.waitMsgTarget){
6234         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
6235         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
6236         //}else {
6237         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
6238        // }
6239          
6240     },
6241
6242     // private
6243     afterAction : function(action, success){
6244         this.activeAction = null;
6245         var o = action.options;
6246         
6247         //if(this.waitMsgTarget === true){
6248             this.el.unmask();
6249         //}else if(this.waitMsgTarget){
6250         //    this.waitMsgTarget.unmask();
6251         //}else{
6252         //    Roo.MessageBox.updateProgress(1);
6253         //    Roo.MessageBox.hide();
6254        // }
6255         // 
6256         if(success){
6257             if(o.reset){
6258                 this.reset();
6259             }
6260             Roo.callback(o.success, o.scope, [this, action]);
6261             this.fireEvent('actioncomplete', this, action);
6262             
6263         }else{
6264             
6265             // failure condition..
6266             // we have a scenario where updates need confirming.
6267             // eg. if a locking scenario exists..
6268             // we look for { errors : { needs_confirm : true }} in the response.
6269             if (
6270                 (typeof(action.result) != 'undefined')  &&
6271                 (typeof(action.result.errors) != 'undefined')  &&
6272                 (typeof(action.result.errors.needs_confirm) != 'undefined')
6273            ){
6274                 var _t = this;
6275                 Roo.log("not supported yet");
6276                  /*
6277                 
6278                 Roo.MessageBox.confirm(
6279                     "Change requires confirmation",
6280                     action.result.errorMsg,
6281                     function(r) {
6282                         if (r != 'yes') {
6283                             return;
6284                         }
6285                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
6286                     }
6287                     
6288                 );
6289                 */
6290                 
6291                 
6292                 return;
6293             }
6294             
6295             Roo.callback(o.failure, o.scope, [this, action]);
6296             // show an error message if no failed handler is set..
6297             if (!this.hasListener('actionfailed')) {
6298                 Roo.log("need to add dialog support");
6299                 /*
6300                 Roo.MessageBox.alert("Error",
6301                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
6302                         action.result.errorMsg :
6303                         "Saving Failed, please check your entries or try again"
6304                 );
6305                 */
6306             }
6307             
6308             this.fireEvent('actionfailed', this, action);
6309         }
6310         
6311     },
6312     /**
6313      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
6314      * @param {String} id The value to search for
6315      * @return Field
6316      */
6317     findField : function(id){
6318         var items = this.getItems();
6319         var field = items.get(id);
6320         if(!field){
6321              items.each(function(f){
6322                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
6323                     field = f;
6324                     return false;
6325                 }
6326                 return true;
6327             });
6328         }
6329         return field || null;
6330     },
6331      /**
6332      * Mark fields in this form invalid in bulk.
6333      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
6334      * @return {BasicForm} this
6335      */
6336     markInvalid : function(errors){
6337         if(errors instanceof Array){
6338             for(var i = 0, len = errors.length; i < len; i++){
6339                 var fieldError = errors[i];
6340                 var f = this.findField(fieldError.id);
6341                 if(f){
6342                     f.markInvalid(fieldError.msg);
6343                 }
6344             }
6345         }else{
6346             var field, id;
6347             for(id in errors){
6348                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
6349                     field.markInvalid(errors[id]);
6350                 }
6351             }
6352         }
6353         //Roo.each(this.childForms || [], function (f) {
6354         //    f.markInvalid(errors);
6355         //});
6356         
6357         return this;
6358     },
6359
6360     /**
6361      * Set values for fields in this form in bulk.
6362      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
6363      * @return {BasicForm} this
6364      */
6365     setValues : function(values){
6366         if(values instanceof Array){ // array of objects
6367             for(var i = 0, len = values.length; i < len; i++){
6368                 var v = values[i];
6369                 var f = this.findField(v.id);
6370                 if(f){
6371                     f.setValue(v.value);
6372                     if(this.trackResetOnLoad){
6373                         f.originalValue = f.getValue();
6374                     }
6375                 }
6376             }
6377         }else{ // object hash
6378             var field, id;
6379             for(id in values){
6380                 if(typeof values[id] != 'function' && (field = this.findField(id))){
6381                     
6382                     if (field.setFromData && 
6383                         field.valueField && 
6384                         field.displayField &&
6385                         // combos' with local stores can 
6386                         // be queried via setValue()
6387                         // to set their value..
6388                         (field.store && !field.store.isLocal)
6389                         ) {
6390                         // it's a combo
6391                         var sd = { };
6392                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
6393                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
6394                         field.setFromData(sd);
6395                         
6396                     } else {
6397                         field.setValue(values[id]);
6398                     }
6399                     
6400                     
6401                     if(this.trackResetOnLoad){
6402                         field.originalValue = field.getValue();
6403                     }
6404                 }
6405             }
6406         }
6407          
6408         //Roo.each(this.childForms || [], function (f) {
6409         //    f.setValues(values);
6410         //});
6411                 
6412         return this;
6413     },
6414
6415     /**
6416      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
6417      * they are returned as an array.
6418      * @param {Boolean} asString
6419      * @return {Object}
6420      */
6421     getValues : function(asString){
6422         //if (this.childForms) {
6423             // copy values from the child forms
6424         //    Roo.each(this.childForms, function (f) {
6425         //        this.setValues(f.getValues());
6426         //    }, this);
6427         //}
6428         
6429         
6430         
6431         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
6432         if(asString === true){
6433             return fs;
6434         }
6435         return Roo.urlDecode(fs);
6436     },
6437     
6438     /**
6439      * Returns the fields in this form as an object with key/value pairs. 
6440      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
6441      * @return {Object}
6442      */
6443     getFieldValues : function(with_hidden)
6444     {
6445         var items = this.getItems();
6446         var ret = {};
6447         items.each(function(f){
6448             if (!f.getName()) {
6449                 return;
6450             }
6451             var v = f.getValue();
6452             if (f.inputType =='radio') {
6453                 if (typeof(ret[f.getName()]) == 'undefined') {
6454                     ret[f.getName()] = ''; // empty..
6455                 }
6456                 
6457                 if (!f.el.dom.checked) {
6458                     return;
6459                     
6460                 }
6461                 v = f.el.dom.value;
6462                 
6463             }
6464             
6465             // not sure if this supported any more..
6466             if ((typeof(v) == 'object') && f.getRawValue) {
6467                 v = f.getRawValue() ; // dates..
6468             }
6469             // combo boxes where name != hiddenName...
6470             if (f.name != f.getName()) {
6471                 ret[f.name] = f.getRawValue();
6472             }
6473             ret[f.getName()] = v;
6474         });
6475         
6476         return ret;
6477     },
6478
6479     /**
6480      * Clears all invalid messages in this form.
6481      * @return {BasicForm} this
6482      */
6483     clearInvalid : function(){
6484         var items = this.getItems();
6485         
6486         items.each(function(f){
6487            f.clearInvalid();
6488         });
6489         
6490         
6491         
6492         return this;
6493     },
6494
6495     /**
6496      * Resets this form.
6497      * @return {BasicForm} this
6498      */
6499     reset : function(){
6500         var items = this.getItems();
6501         items.each(function(f){
6502             f.reset();
6503         });
6504         
6505         Roo.each(this.childForms || [], function (f) {
6506             f.reset();
6507         });
6508        
6509         
6510         return this;
6511     },
6512     getItems : function()
6513     {
6514         var r=new Roo.util.MixedCollection(false, function(o){
6515             return o.id || (o.id = Roo.id());
6516         });
6517         var iter = function(el) {
6518             if (el.inputEl) {
6519                 r.add(el);
6520             }
6521             if (!el.items) {
6522                 return;
6523             }
6524             Roo.each(el.items,function(e) {
6525                 iter(e);
6526             });
6527             
6528             
6529         };
6530         iter(this);
6531         return r;
6532         
6533         
6534         
6535         
6536     }
6537     
6538 });
6539
6540  
6541 /*
6542  * Based on:
6543  * Ext JS Library 1.1.1
6544  * Copyright(c) 2006-2007, Ext JS, LLC.
6545  *
6546  * Originally Released Under LGPL - original licence link has changed is not relivant.
6547  *
6548  * Fork - LGPL
6549  * <script type="text/javascript">
6550  */
6551 /**
6552  * @class Roo.form.VTypes
6553  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
6554  * @singleton
6555  */
6556 Roo.form.VTypes = function(){
6557     // closure these in so they are only created once.
6558     var alpha = /^[a-zA-Z_]+$/;
6559     var alphanum = /^[a-zA-Z0-9_]+$/;
6560     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
6561     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
6562
6563     // All these messages and functions are configurable
6564     return {
6565         /**
6566          * The function used to validate email addresses
6567          * @param {String} value The email address
6568          */
6569         'email' : function(v){
6570             return email.test(v);
6571         },
6572         /**
6573          * The error text to display when the email validation function returns false
6574          * @type String
6575          */
6576         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
6577         /**
6578          * The keystroke filter mask to be applied on email input
6579          * @type RegExp
6580          */
6581         'emailMask' : /[a-z0-9_\.\-@]/i,
6582
6583         /**
6584          * The function used to validate URLs
6585          * @param {String} value The URL
6586          */
6587         'url' : function(v){
6588             return url.test(v);
6589         },
6590         /**
6591          * The error text to display when the url validation function returns false
6592          * @type String
6593          */
6594         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
6595         
6596         /**
6597          * The function used to validate alpha values
6598          * @param {String} value The value
6599          */
6600         'alpha' : function(v){
6601             return alpha.test(v);
6602         },
6603         /**
6604          * The error text to display when the alpha validation function returns false
6605          * @type String
6606          */
6607         'alphaText' : 'This field should only contain letters and _',
6608         /**
6609          * The keystroke filter mask to be applied on alpha input
6610          * @type RegExp
6611          */
6612         'alphaMask' : /[a-z_]/i,
6613
6614         /**
6615          * The function used to validate alphanumeric values
6616          * @param {String} value The value
6617          */
6618         'alphanum' : function(v){
6619             return alphanum.test(v);
6620         },
6621         /**
6622          * The error text to display when the alphanumeric validation function returns false
6623          * @type String
6624          */
6625         'alphanumText' : 'This field should only contain letters, numbers and _',
6626         /**
6627          * The keystroke filter mask to be applied on alphanumeric input
6628          * @type RegExp
6629          */
6630         'alphanumMask' : /[a-z0-9_]/i
6631     };
6632 }();/*
6633  * - LGPL
6634  *
6635  * Input
6636  * 
6637  */
6638
6639 /**
6640  * @class Roo.bootstrap.Input
6641  * @extends Roo.bootstrap.Component
6642  * Bootstrap Input class
6643  * @cfg {Boolean} disabled is it disabled
6644  * @cfg {String} fieldLabel - the label associated
6645  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
6646  * @cfg {String} name name of the input
6647  * @cfg {string} fieldLabel - the label associated
6648  * @cfg {string}  inputType - input / file submit ...
6649  * @cfg {string} placeholder - placeholder to put in text.
6650  * @cfg {string}  before - input group add on before
6651  * @cfg {string} after - input group add on after
6652  * @cfg {string} size - (lg|sm) or leave empty..
6653  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
6654  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
6655  * @cfg {Number} md colspan out of 12 for computer-sized screens
6656  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
6657  * @cfg {string} value default value of the input
6658  * @cfg {Number} labelWidth set the width of label (0-12)
6659  * @cfg {String} labelAlign (top|left)
6660  * @cfg {Boolean} readOnly Specifies that the field should be read-only
6661  * @cfg {String} align (left|center|right) Default left
6662  * 
6663  * 
6664  * @constructor
6665  * Create a new Input
6666  * @param {Object} config The config object
6667  */
6668
6669 Roo.bootstrap.Input = function(config){
6670     Roo.bootstrap.Input.superclass.constructor.call(this, config);
6671    
6672         this.addEvents({
6673             /**
6674              * @event focus
6675              * Fires when this field receives input focus.
6676              * @param {Roo.form.Field} this
6677              */
6678             focus : true,
6679             /**
6680              * @event blur
6681              * Fires when this field loses input focus.
6682              * @param {Roo.form.Field} this
6683              */
6684             blur : true,
6685             /**
6686              * @event specialkey
6687              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
6688              * {@link Roo.EventObject#getKey} to determine which key was pressed.
6689              * @param {Roo.form.Field} this
6690              * @param {Roo.EventObject} e The event object
6691              */
6692             specialkey : true,
6693             /**
6694              * @event change
6695              * Fires just before the field blurs if the field value has changed.
6696              * @param {Roo.form.Field} this
6697              * @param {Mixed} newValue The new value
6698              * @param {Mixed} oldValue The original value
6699              */
6700             change : true,
6701             /**
6702              * @event invalid
6703              * Fires after the field has been marked as invalid.
6704              * @param {Roo.form.Field} this
6705              * @param {String} msg The validation message
6706              */
6707             invalid : true,
6708             /**
6709              * @event valid
6710              * Fires after the field has been validated with no errors.
6711              * @param {Roo.form.Field} this
6712              */
6713             valid : true,
6714              /**
6715              * @event keyup
6716              * Fires after the key up
6717              * @param {Roo.form.Field} this
6718              * @param {Roo.EventObject}  e The event Object
6719              */
6720             keyup : true
6721         });
6722 };
6723
6724 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
6725      /**
6726      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
6727       automatic validation (defaults to "keyup").
6728      */
6729     validationEvent : "keyup",
6730      /**
6731      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
6732      */
6733     validateOnBlur : true,
6734     /**
6735      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
6736      */
6737     validationDelay : 250,
6738      /**
6739      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
6740      */
6741     focusClass : "x-form-focus",  // not needed???
6742     
6743        
6744     /**
6745      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
6746      */
6747     invalidClass : "has-error",
6748     
6749     /**
6750      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
6751      */
6752     selectOnFocus : false,
6753     
6754      /**
6755      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
6756      */
6757     maskRe : null,
6758        /**
6759      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
6760      */
6761     vtype : null,
6762     
6763       /**
6764      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
6765      */
6766     disableKeyFilter : false,
6767     
6768        /**
6769      * @cfg {Boolean} disabled True to disable the field (defaults to false).
6770      */
6771     disabled : false,
6772      /**
6773      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
6774      */
6775     allowBlank : true,
6776     /**
6777      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
6778      */
6779     blankText : "This field is required",
6780     
6781      /**
6782      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
6783      */
6784     minLength : 0,
6785     /**
6786      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
6787      */
6788     maxLength : Number.MAX_VALUE,
6789     /**
6790      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
6791      */
6792     minLengthText : "The minimum length for this field is {0}",
6793     /**
6794      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
6795      */
6796     maxLengthText : "The maximum length for this field is {0}",
6797   
6798     
6799     /**
6800      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
6801      * If available, this function will be called only after the basic validators all return true, and will be passed the
6802      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
6803      */
6804     validator : null,
6805     /**
6806      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
6807      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
6808      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
6809      */
6810     regex : null,
6811     /**
6812      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
6813      */
6814     regexText : "",
6815     
6816     
6817     
6818     fieldLabel : '',
6819     inputType : 'text',
6820     
6821     name : false,
6822     placeholder: false,
6823     before : false,
6824     after : false,
6825     size : false,
6826     // private
6827     hasFocus : false,
6828     preventMark: false,
6829     isFormField : true,
6830     value : '',
6831     labelWidth : 2,
6832     labelAlign : false,
6833     readOnly : false,
6834     align : false,
6835     formatedValue : false,
6836     
6837     parentLabelAlign : function()
6838     {
6839         var parent = this;
6840         while (parent.parent()) {
6841             parent = parent.parent();
6842             if (typeof(parent.labelAlign) !='undefined') {
6843                 return parent.labelAlign;
6844             }
6845         }
6846         return 'left';
6847         
6848     },
6849     
6850     getAutoCreate : function(){
6851         
6852         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
6853         
6854         var id = Roo.id();
6855         
6856         var cfg = {};
6857         
6858         if(this.inputType != 'hidden'){
6859             cfg.cls = 'form-group' //input-group
6860         }
6861         
6862         var input =  {
6863             tag: 'input',
6864             id : id,
6865             type : this.inputType,
6866             value : this.value,
6867             cls : 'form-control',
6868             placeholder : this.placeholder || ''
6869             
6870         };
6871         
6872         if(this.align){
6873             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
6874         }
6875         
6876         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
6877             input.maxLength = this.maxLength;
6878         }
6879         
6880         if (this.disabled) {
6881             input.disabled=true;
6882         }
6883         
6884         if (this.readOnly) {
6885             input.readonly=true;
6886         }
6887         
6888         if (this.name) {
6889             input.name = this.name;
6890         }
6891         if (this.size) {
6892             input.cls += ' input-' + this.size;
6893         }
6894         var settings=this;
6895         ['xs','sm','md','lg'].map(function(size){
6896             if (settings[size]) {
6897                 cfg.cls += ' col-' + size + '-' + settings[size];
6898             }
6899         });
6900         
6901         var inputblock = input;
6902         
6903         if (this.before || this.after) {
6904             
6905             inputblock = {
6906                 cls : 'input-group',
6907                 cn :  [] 
6908             };
6909             if (this.before && typeof(this.before) == 'string') {
6910                 
6911                 inputblock.cn.push({
6912                     tag :'span',
6913                     cls : 'roo-input-before input-group-addon',
6914                     html : this.before
6915                 });
6916             }
6917             if (this.before && typeof(this.before) == 'object') {
6918                 this.before = Roo.factory(this.before);
6919                 Roo.log(this.before);
6920                 inputblock.cn.push({
6921                     tag :'span',
6922                     cls : 'roo-input-before input-group-' +
6923                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
6924                 });
6925             }
6926             
6927             inputblock.cn.push(input);
6928             
6929             if (this.after && typeof(this.after) == 'string') {
6930                 inputblock.cn.push({
6931                     tag :'span',
6932                     cls : 'roo-input-after input-group-addon',
6933                     html : this.after
6934                 });
6935             }
6936             if (this.after && typeof(this.after) == 'object') {
6937                 this.after = Roo.factory(this.after);
6938                 Roo.log(this.after);
6939                 inputblock.cn.push({
6940                     tag :'span',
6941                     cls : 'roo-input-after input-group-' +
6942                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
6943                 });
6944             }
6945         };
6946         
6947         if (align ==='left' && this.fieldLabel.length) {
6948                 Roo.log("left and has label");
6949                 cfg.cn = [
6950                     
6951                     {
6952                         tag: 'label',
6953                         'for' :  id,
6954                         cls : 'control-label col-sm-' + this.labelWidth,
6955                         html : this.fieldLabel
6956                         
6957                     },
6958                     {
6959                         cls : "col-sm-" + (12 - this.labelWidth), 
6960                         cn: [
6961                             inputblock
6962                         ]
6963                     }
6964                     
6965                 ];
6966         } else if ( this.fieldLabel.length) {
6967                 Roo.log(" label");
6968                  cfg.cn = [
6969                    
6970                     {
6971                         tag: 'label',
6972                         //cls : 'input-group-addon',
6973                         html : this.fieldLabel
6974                         
6975                     },
6976                     
6977                     inputblock
6978                     
6979                 ];
6980
6981         } else {
6982             
6983                 Roo.log(" no label && no align");
6984                 cfg.cn = [
6985                     
6986                         inputblock
6987                     
6988                 ];
6989                 
6990                 
6991         };
6992         Roo.log('input-parentType: ' + this.parentType);
6993         
6994         if (this.parentType === 'Navbar' &&  this.parent().bar) {
6995            cfg.cls += ' navbar-form';
6996            Roo.log(cfg);
6997         }
6998         
6999         return cfg;
7000         
7001     },
7002     /**
7003      * return the real input element.
7004      */
7005     inputEl: function ()
7006     {
7007         return this.el.select('input.form-control',true).first();
7008     },
7009     setDisabled : function(v)
7010     {
7011         var i  = this.inputEl().dom;
7012         if (!v) {
7013             i.removeAttribute('disabled');
7014             return;
7015             
7016         }
7017         i.setAttribute('disabled','true');
7018     },
7019     initEvents : function()
7020     {
7021         
7022         this.inputEl().on("keydown" , this.fireKey,  this);
7023         this.inputEl().on("focus", this.onFocus,  this);
7024         this.inputEl().on("blur", this.onBlur,  this);
7025         
7026         this.inputEl().relayEvent('keyup', this);
7027
7028         // reference to original value for reset
7029         this.originalValue = this.getValue();
7030         //Roo.form.TextField.superclass.initEvents.call(this);
7031         if(this.validationEvent == 'keyup'){
7032             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
7033             this.inputEl().on('keyup', this.filterValidation, this);
7034         }
7035         else if(this.validationEvent !== false){
7036             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
7037         }
7038         
7039         if(this.selectOnFocus){
7040             this.on("focus", this.preFocus, this);
7041             
7042         }
7043         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
7044             this.inputEl().on("keypress", this.filterKeys, this);
7045         }
7046        /* if(this.grow){
7047             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
7048             this.el.on("click", this.autoSize,  this);
7049         }
7050         */
7051         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
7052             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
7053         }
7054         
7055         if (typeof(this.before) == 'object') {
7056             this.before.render(this.el.select('.roo-input-before',true).first());
7057         }
7058         if (typeof(this.after) == 'object') {
7059             this.after.render(this.el.select('.roo-input-after',true).first());
7060         }
7061         
7062         
7063     },
7064     filterValidation : function(e){
7065         if(!e.isNavKeyPress()){
7066             this.validationTask.delay(this.validationDelay);
7067         }
7068     },
7069      /**
7070      * Validates the field value
7071      * @return {Boolean} True if the value is valid, else false
7072      */
7073     validate : function(){
7074         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
7075         if(this.disabled || this.validateValue(this.getRawValue())){
7076             this.clearInvalid();
7077             return true;
7078         }
7079         return false;
7080     },
7081     
7082     
7083     /**
7084      * Validates a value according to the field's validation rules and marks the field as invalid
7085      * if the validation fails
7086      * @param {Mixed} value The value to validate
7087      * @return {Boolean} True if the value is valid, else false
7088      */
7089     validateValue : function(value){
7090         if(value.length < 1)  { // if it's blank
7091              if(this.allowBlank){
7092                 this.clearInvalid();
7093                 return true;
7094              }else{
7095                 this.markInvalid(this.blankText);
7096                 return false;
7097              }
7098         }
7099         if(value.length < this.minLength){
7100             this.markInvalid(String.format(this.minLengthText, this.minLength));
7101             return false;
7102         }
7103         if(value.length > this.maxLength){
7104             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
7105             return false;
7106         }
7107         if(this.vtype){
7108             var vt = Roo.form.VTypes;
7109             if(!vt[this.vtype](value, this)){
7110                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
7111                 return false;
7112             }
7113         }
7114         if(typeof this.validator == "function"){
7115             var msg = this.validator(value);
7116             if(msg !== true){
7117                 this.markInvalid(msg);
7118                 return false;
7119             }
7120         }
7121         if(this.regex && !this.regex.test(value)){
7122             this.markInvalid(this.regexText);
7123             return false;
7124         }
7125         return true;
7126     },
7127
7128     
7129     
7130      // private
7131     fireKey : function(e){
7132         //Roo.log('field ' + e.getKey());
7133         if(e.isNavKeyPress()){
7134             this.fireEvent("specialkey", this, e);
7135         }
7136     },
7137     focus : function (selectText){
7138         if(this.rendered){
7139             this.inputEl().focus();
7140             if(selectText === true){
7141                 this.inputEl().dom.select();
7142             }
7143         }
7144         return this;
7145     } ,
7146     
7147     onFocus : function(){
7148         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7149            // this.el.addClass(this.focusClass);
7150         }
7151         if(!this.hasFocus){
7152             this.hasFocus = true;
7153             this.startValue = this.getValue();
7154             this.fireEvent("focus", this);
7155         }
7156     },
7157     
7158     beforeBlur : Roo.emptyFn,
7159
7160     
7161     // private
7162     onBlur : function(){
7163         this.beforeBlur();
7164         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7165             //this.el.removeClass(this.focusClass);
7166         }
7167         this.hasFocus = false;
7168         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
7169             this.validate();
7170         }
7171         var v = this.getValue();
7172         if(String(v) !== String(this.startValue)){
7173             this.fireEvent('change', this, v, this.startValue);
7174         }
7175         this.fireEvent("blur", this);
7176     },
7177     
7178     /**
7179      * Resets the current field value to the originally loaded value and clears any validation messages
7180      */
7181     reset : function(){
7182         this.setValue(this.originalValue);
7183         this.clearInvalid();
7184     },
7185      /**
7186      * Returns the name of the field
7187      * @return {Mixed} name The name field
7188      */
7189     getName: function(){
7190         return this.name;
7191     },
7192      /**
7193      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
7194      * @return {Mixed} value The field value
7195      */
7196     getValue : function(){
7197         
7198         var v = this.inputEl().getValue();
7199         
7200         return v;
7201     },
7202     /**
7203      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
7204      * @return {Mixed} value The field value
7205      */
7206     getRawValue : function(){
7207         var v = this.inputEl().getValue();
7208         
7209         return v;
7210     },
7211     
7212     /**
7213      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
7214      * @param {Mixed} value The value to set
7215      */
7216     setRawValue : function(v){
7217         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7218     },
7219     
7220     selectText : function(start, end){
7221         var v = this.getRawValue();
7222         if(v.length > 0){
7223             start = start === undefined ? 0 : start;
7224             end = end === undefined ? v.length : end;
7225             var d = this.inputEl().dom;
7226             if(d.setSelectionRange){
7227                 d.setSelectionRange(start, end);
7228             }else if(d.createTextRange){
7229                 var range = d.createTextRange();
7230                 range.moveStart("character", start);
7231                 range.moveEnd("character", v.length-end);
7232                 range.select();
7233             }
7234         }
7235     },
7236     
7237     /**
7238      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
7239      * @param {Mixed} value The value to set
7240      */
7241     setValue : function(v){
7242         this.value = v;
7243         if(this.rendered){
7244             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7245             this.validate();
7246         }
7247     },
7248     
7249     /*
7250     processValue : function(value){
7251         if(this.stripCharsRe){
7252             var newValue = value.replace(this.stripCharsRe, '');
7253             if(newValue !== value){
7254                 this.setRawValue(newValue);
7255                 return newValue;
7256             }
7257         }
7258         return value;
7259     },
7260   */
7261     preFocus : function(){
7262         
7263         if(this.selectOnFocus){
7264             this.inputEl().dom.select();
7265         }
7266     },
7267     filterKeys : function(e){
7268         var k = e.getKey();
7269         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
7270             return;
7271         }
7272         var c = e.getCharCode(), cc = String.fromCharCode(c);
7273         if(Roo.isIE && (e.isSpecialKey() || !cc)){
7274             return;
7275         }
7276         if(!this.maskRe.test(cc)){
7277             e.stopEvent();
7278         }
7279     },
7280      /**
7281      * Clear any invalid styles/messages for this field
7282      */
7283     clearInvalid : function(){
7284         
7285         if(!this.el || this.preventMark){ // not rendered
7286             return;
7287         }
7288         this.el.removeClass(this.invalidClass);
7289         /*
7290         switch(this.msgTarget){
7291             case 'qtip':
7292                 this.el.dom.qtip = '';
7293                 break;
7294             case 'title':
7295                 this.el.dom.title = '';
7296                 break;
7297             case 'under':
7298                 if(this.errorEl){
7299                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
7300                 }
7301                 break;
7302             case 'side':
7303                 if(this.errorIcon){
7304                     this.errorIcon.dom.qtip = '';
7305                     this.errorIcon.hide();
7306                     this.un('resize', this.alignErrorIcon, this);
7307                 }
7308                 break;
7309             default:
7310                 var t = Roo.getDom(this.msgTarget);
7311                 t.innerHTML = '';
7312                 t.style.display = 'none';
7313                 break;
7314         }
7315         */
7316         this.fireEvent('valid', this);
7317     },
7318      /**
7319      * Mark this field as invalid
7320      * @param {String} msg The validation message
7321      */
7322     markInvalid : function(msg){
7323         if(!this.el  || this.preventMark){ // not rendered
7324             return;
7325         }
7326         this.el.addClass(this.invalidClass);
7327         /*
7328         msg = msg || this.invalidText;
7329         switch(this.msgTarget){
7330             case 'qtip':
7331                 this.el.dom.qtip = msg;
7332                 this.el.dom.qclass = 'x-form-invalid-tip';
7333                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
7334                     Roo.QuickTips.enable();
7335                 }
7336                 break;
7337             case 'title':
7338                 this.el.dom.title = msg;
7339                 break;
7340             case 'under':
7341                 if(!this.errorEl){
7342                     var elp = this.el.findParent('.x-form-element', 5, true);
7343                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
7344                     this.errorEl.setWidth(elp.getWidth(true)-20);
7345                 }
7346                 this.errorEl.update(msg);
7347                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
7348                 break;
7349             case 'side':
7350                 if(!this.errorIcon){
7351                     var elp = this.el.findParent('.x-form-element', 5, true);
7352                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
7353                 }
7354                 this.alignErrorIcon();
7355                 this.errorIcon.dom.qtip = msg;
7356                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
7357                 this.errorIcon.show();
7358                 this.on('resize', this.alignErrorIcon, this);
7359                 break;
7360             default:
7361                 var t = Roo.getDom(this.msgTarget);
7362                 t.innerHTML = msg;
7363                 t.style.display = this.msgDisplay;
7364                 break;
7365         }
7366         */
7367         this.fireEvent('invalid', this, msg);
7368     },
7369     // private
7370     SafariOnKeyDown : function(event)
7371     {
7372         // this is a workaround for a password hang bug on chrome/ webkit.
7373         
7374         var isSelectAll = false;
7375         
7376         if(this.inputEl().dom.selectionEnd > 0){
7377             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
7378         }
7379         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
7380             event.preventDefault();
7381             this.setValue('');
7382             return;
7383         }
7384         
7385         if(isSelectAll){ // backspace and delete key
7386             
7387             event.preventDefault();
7388             // this is very hacky as keydown always get's upper case.
7389             //
7390             var cc = String.fromCharCode(event.getCharCode());
7391             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
7392             
7393         }
7394     },
7395     adjustWidth : function(tag, w){
7396         tag = tag.toLowerCase();
7397         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
7398             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
7399                 if(tag == 'input'){
7400                     return w + 2;
7401                 }
7402                 if(tag == 'textarea'){
7403                     return w-2;
7404                 }
7405             }else if(Roo.isOpera){
7406                 if(tag == 'input'){
7407                     return w + 2;
7408                 }
7409                 if(tag == 'textarea'){
7410                     return w-2;
7411                 }
7412             }
7413         }
7414         return w;
7415     }
7416     
7417 });
7418
7419  
7420 /*
7421  * - LGPL
7422  *
7423  * Input
7424  * 
7425  */
7426
7427 /**
7428  * @class Roo.bootstrap.TextArea
7429  * @extends Roo.bootstrap.Input
7430  * Bootstrap TextArea class
7431  * @cfg {Number} cols Specifies the visible width of a text area
7432  * @cfg {Number} rows Specifies the visible number of lines in a text area
7433  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
7434  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
7435  * @cfg {string} html text
7436  * 
7437  * @constructor
7438  * Create a new TextArea
7439  * @param {Object} config The config object
7440  */
7441
7442 Roo.bootstrap.TextArea = function(config){
7443     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
7444    
7445 };
7446
7447 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
7448      
7449     cols : false,
7450     rows : 5,
7451     readOnly : false,
7452     warp : 'soft',
7453     resize : false,
7454     value: false,
7455     html: false,
7456     
7457     getAutoCreate : function(){
7458         
7459         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7460         
7461         var id = Roo.id();
7462         
7463         var cfg = {};
7464         
7465         var input =  {
7466             tag: 'textarea',
7467             id : id,
7468             warp : this.warp,
7469             rows : this.rows,
7470             value : this.value || '',
7471             html: this.html || '',
7472             cls : 'form-control',
7473             placeholder : this.placeholder || '' 
7474             
7475         };
7476         
7477         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7478             input.maxLength = this.maxLength;
7479         }
7480         
7481         if(this.resize){
7482             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
7483         }
7484         
7485         if(this.cols){
7486             input.cols = this.cols;
7487         }
7488         
7489         if (this.readOnly) {
7490             input.readonly = true;
7491         }
7492         
7493         if (this.name) {
7494             input.name = this.name;
7495         }
7496         
7497         if (this.size) {
7498             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
7499         }
7500         
7501         var settings=this;
7502         ['xs','sm','md','lg'].map(function(size){
7503             if (settings[size]) {
7504                 cfg.cls += ' col-' + size + '-' + settings[size];
7505             }
7506         });
7507         
7508         var inputblock = input;
7509         
7510         if (this.before || this.after) {
7511             
7512             inputblock = {
7513                 cls : 'input-group',
7514                 cn :  [] 
7515             };
7516             if (this.before) {
7517                 inputblock.cn.push({
7518                     tag :'span',
7519                     cls : 'input-group-addon',
7520                     html : this.before
7521                 });
7522             }
7523             inputblock.cn.push(input);
7524             if (this.after) {
7525                 inputblock.cn.push({
7526                     tag :'span',
7527                     cls : 'input-group-addon',
7528                     html : this.after
7529                 });
7530             }
7531             
7532         }
7533         
7534         if (align ==='left' && this.fieldLabel.length) {
7535                 Roo.log("left and has label");
7536                 cfg.cn = [
7537                     
7538                     {
7539                         tag: 'label',
7540                         'for' :  id,
7541                         cls : 'control-label col-sm-' + this.labelWidth,
7542                         html : this.fieldLabel
7543                         
7544                     },
7545                     {
7546                         cls : "col-sm-" + (12 - this.labelWidth), 
7547                         cn: [
7548                             inputblock
7549                         ]
7550                     }
7551                     
7552                 ];
7553         } else if ( this.fieldLabel.length) {
7554                 Roo.log(" label");
7555                  cfg.cn = [
7556                    
7557                     {
7558                         tag: 'label',
7559                         //cls : 'input-group-addon',
7560                         html : this.fieldLabel
7561                         
7562                     },
7563                     
7564                     inputblock
7565                     
7566                 ];
7567
7568         } else {
7569             
7570                    Roo.log(" no label && no align");
7571                 cfg.cn = [
7572                     
7573                         inputblock
7574                     
7575                 ];
7576                 
7577                 
7578         }
7579         
7580         if (this.disabled) {
7581             input.disabled=true;
7582         }
7583         
7584         return cfg;
7585         
7586     },
7587     /**
7588      * return the real textarea element.
7589      */
7590     inputEl: function ()
7591     {
7592         return this.el.select('textarea.form-control',true).first();
7593     }
7594 });
7595
7596  
7597 /*
7598  * - LGPL
7599  *
7600  * trigger field - base class for combo..
7601  * 
7602  */
7603  
7604 /**
7605  * @class Roo.bootstrap.TriggerField
7606  * @extends Roo.bootstrap.Input
7607  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
7608  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
7609  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
7610  * for which you can provide a custom implementation.  For example:
7611  * <pre><code>
7612 var trigger = new Roo.bootstrap.TriggerField();
7613 trigger.onTriggerClick = myTriggerFn;
7614 trigger.applyTo('my-field');
7615 </code></pre>
7616  *
7617  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
7618  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
7619  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
7620  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
7621  * @constructor
7622  * Create a new TriggerField.
7623  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
7624  * to the base TextField)
7625  */
7626 Roo.bootstrap.TriggerField = function(config){
7627     this.mimicing = false;
7628     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
7629 };
7630
7631 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
7632     /**
7633      * @cfg {String} triggerClass A CSS class to apply to the trigger
7634      */
7635      /**
7636      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
7637      */
7638     hideTrigger:false,
7639
7640     /** @cfg {Boolean} grow @hide */
7641     /** @cfg {Number} growMin @hide */
7642     /** @cfg {Number} growMax @hide */
7643
7644     /**
7645      * @hide 
7646      * @method
7647      */
7648     autoSize: Roo.emptyFn,
7649     // private
7650     monitorTab : true,
7651     // private
7652     deferHeight : true,
7653
7654     
7655     actionMode : 'wrap',
7656     
7657     
7658     
7659     getAutoCreate : function(){
7660        
7661         var parent = this.parent();
7662         
7663         var align = this.labelAlign || this.parentLabelAlign();
7664         
7665         var id = Roo.id();
7666         
7667         var cfg = {
7668             cls: 'form-group' //input-group
7669         };
7670         
7671         
7672         var input =  {
7673             tag: 'input',
7674             id : id,
7675             type : this.inputType,
7676             cls : 'form-control',
7677             autocomplete: 'off',
7678             placeholder : this.placeholder || '' 
7679             
7680         };
7681         if (this.name) {
7682             input.name = this.name;
7683         }
7684         if (this.size) {
7685             input.cls += ' input-' + this.size;
7686         }
7687         
7688         if (this.disabled) {
7689             input.disabled=true;
7690         }
7691         
7692         var inputblock = input;
7693         
7694         if (this.before || this.after) {
7695             
7696             inputblock = {
7697                 cls : 'input-group',
7698                 cn :  [] 
7699             };
7700             if (this.before) {
7701                 inputblock.cn.push({
7702                     tag :'span',
7703                     cls : 'input-group-addon',
7704                     html : this.before
7705                 });
7706             }
7707             inputblock.cn.push(input);
7708             if (this.after) {
7709                 inputblock.cn.push({
7710                     tag :'span',
7711                     cls : 'input-group-addon',
7712                     html : this.after
7713                 });
7714             }
7715             
7716         };
7717         
7718         var box = {
7719             tag: 'div',
7720             cn: [
7721                 {
7722                     tag: 'input',
7723                     type : 'hidden',
7724                     cls: 'form-hidden-field'
7725                 },
7726                 inputblock
7727             ]
7728             
7729         };
7730         
7731         if(this.multiple){
7732             Roo.log('multiple');
7733             
7734             box = {
7735                 tag: 'div',
7736                 cn: [
7737                     {
7738                         tag: 'input',
7739                         type : 'hidden',
7740                         cls: 'form-hidden-field'
7741                     },
7742                     {
7743                         tag: 'ul',
7744                         cls: 'select2-choices',
7745                         cn:[
7746                             {
7747                                 tag: 'li',
7748                                 cls: 'select2-search-field',
7749                                 cn: [
7750
7751                                     inputblock
7752                                 ]
7753                             }
7754                         ]
7755                     }
7756                 ]
7757             }
7758         };
7759         
7760         var combobox = {
7761             cls: 'select2-container input-group',
7762             cn: [
7763                 box,
7764                 {
7765                     tag: 'ul',
7766                     cls: 'typeahead typeahead-long dropdown-menu',
7767                     style: 'display:none'
7768                 }
7769             ]
7770         };
7771         
7772         if(!this.multiple){
7773             combobox.cn.push({
7774                 tag :'span',
7775                 cls : 'input-group-addon btn dropdown-toggle',
7776                 cn : [
7777                     {
7778                         tag: 'span',
7779                         cls: 'caret'
7780                     },
7781                     {
7782                         tag: 'span',
7783                         cls: 'combobox-clear',
7784                         cn  : [
7785                             {
7786                                 tag : 'i',
7787                                 cls: 'icon-remove'
7788                             }
7789                         ]
7790                     }
7791                 ]
7792
7793             })
7794         }
7795         
7796         if(this.multiple){
7797             combobox.cls += ' select2-container-multi';
7798         }
7799         
7800         if (align ==='left' && this.fieldLabel.length) {
7801             
7802                 Roo.log("left and has label");
7803                 cfg.cn = [
7804                     
7805                     {
7806                         tag: 'label',
7807                         'for' :  id,
7808                         cls : 'control-label col-sm-' + this.labelWidth,
7809                         html : this.fieldLabel
7810                         
7811                     },
7812                     {
7813                         cls : "col-sm-" + (12 - this.labelWidth), 
7814                         cn: [
7815                             combobox
7816                         ]
7817                     }
7818                     
7819                 ];
7820         } else if ( this.fieldLabel.length) {
7821                 Roo.log(" label");
7822                  cfg.cn = [
7823                    
7824                     {
7825                         tag: 'label',
7826                         //cls : 'input-group-addon',
7827                         html : this.fieldLabel
7828                         
7829                     },
7830                     
7831                     combobox
7832                     
7833                 ];
7834
7835         } else {
7836             
7837                 Roo.log(" no label && no align");
7838                 cfg = combobox
7839                      
7840                 
7841         }
7842          
7843         var settings=this;
7844         ['xs','sm','md','lg'].map(function(size){
7845             if (settings[size]) {
7846                 cfg.cls += ' col-' + size + '-' + settings[size];
7847             }
7848         });
7849         
7850         return cfg;
7851         
7852     },
7853     
7854     
7855     
7856     // private
7857     onResize : function(w, h){
7858 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
7859 //        if(typeof w == 'number'){
7860 //            var x = w - this.trigger.getWidth();
7861 //            this.inputEl().setWidth(this.adjustWidth('input', x));
7862 //            this.trigger.setStyle('left', x+'px');
7863 //        }
7864     },
7865
7866     // private
7867     adjustSize : Roo.BoxComponent.prototype.adjustSize,
7868
7869     // private
7870     getResizeEl : function(){
7871         return this.inputEl();
7872     },
7873
7874     // private
7875     getPositionEl : function(){
7876         return this.inputEl();
7877     },
7878
7879     // private
7880     alignErrorIcon : function(){
7881         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
7882     },
7883
7884     // private
7885     initEvents : function(){
7886         
7887         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
7888         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
7889         if(!this.multiple){
7890             this.trigger = this.el.select('span.dropdown-toggle',true).first();
7891             if(this.hideTrigger){
7892                 this.trigger.setDisplayed(false);
7893             }
7894             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
7895         }
7896         
7897         if(this.multiple){
7898             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
7899         }
7900         
7901         //this.trigger.addClassOnOver('x-form-trigger-over');
7902         //this.trigger.addClassOnClick('x-form-trigger-click');
7903         
7904         //if(!this.width){
7905         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
7906         //}
7907     },
7908
7909     // private
7910     initTrigger : function(){
7911        
7912     },
7913
7914     // private
7915     onDestroy : function(){
7916         if(this.trigger){
7917             this.trigger.removeAllListeners();
7918           //  this.trigger.remove();
7919         }
7920         //if(this.wrap){
7921         //    this.wrap.remove();
7922         //}
7923         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
7924     },
7925
7926     // private
7927     onFocus : function(){
7928         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
7929         /*
7930         if(!this.mimicing){
7931             this.wrap.addClass('x-trigger-wrap-focus');
7932             this.mimicing = true;
7933             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
7934             if(this.monitorTab){
7935                 this.el.on("keydown", this.checkTab, this);
7936             }
7937         }
7938         */
7939     },
7940
7941     // private
7942     checkTab : function(e){
7943         if(e.getKey() == e.TAB){
7944             this.triggerBlur();
7945         }
7946     },
7947
7948     // private
7949     onBlur : function(){
7950         // do nothing
7951     },
7952
7953     // private
7954     mimicBlur : function(e, t){
7955         /*
7956         if(!this.wrap.contains(t) && this.validateBlur()){
7957             this.triggerBlur();
7958         }
7959         */
7960     },
7961
7962     // private
7963     triggerBlur : function(){
7964         this.mimicing = false;
7965         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
7966         if(this.monitorTab){
7967             this.el.un("keydown", this.checkTab, this);
7968         }
7969         //this.wrap.removeClass('x-trigger-wrap-focus');
7970         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
7971     },
7972
7973     // private
7974     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
7975     validateBlur : function(e, t){
7976         return true;
7977     },
7978
7979     // private
7980     onDisable : function(){
7981         this.inputEl().dom.disabled = true;
7982         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
7983         //if(this.wrap){
7984         //    this.wrap.addClass('x-item-disabled');
7985         //}
7986     },
7987
7988     // private
7989     onEnable : function(){
7990         this.inputEl().dom.disabled = false;
7991         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
7992         //if(this.wrap){
7993         //    this.el.removeClass('x-item-disabled');
7994         //}
7995     },
7996
7997     // private
7998     onShow : function(){
7999         var ae = this.getActionEl();
8000         
8001         if(ae){
8002             ae.dom.style.display = '';
8003             ae.dom.style.visibility = 'visible';
8004         }
8005     },
8006
8007     // private
8008     
8009     onHide : function(){
8010         var ae = this.getActionEl();
8011         ae.dom.style.display = 'none';
8012     },
8013
8014     /**
8015      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
8016      * by an implementing function.
8017      * @method
8018      * @param {EventObject} e
8019      */
8020     onTriggerClick : Roo.emptyFn
8021 });
8022  /*
8023  * Based on:
8024  * Ext JS Library 1.1.1
8025  * Copyright(c) 2006-2007, Ext JS, LLC.
8026  *
8027  * Originally Released Under LGPL - original licence link has changed is not relivant.
8028  *
8029  * Fork - LGPL
8030  * <script type="text/javascript">
8031  */
8032
8033
8034 /**
8035  * @class Roo.data.SortTypes
8036  * @singleton
8037  * Defines the default sorting (casting?) comparison functions used when sorting data.
8038  */
8039 Roo.data.SortTypes = {
8040     /**
8041      * Default sort that does nothing
8042      * @param {Mixed} s The value being converted
8043      * @return {Mixed} The comparison value
8044      */
8045     none : function(s){
8046         return s;
8047     },
8048     
8049     /**
8050      * The regular expression used to strip tags
8051      * @type {RegExp}
8052      * @property
8053      */
8054     stripTagsRE : /<\/?[^>]+>/gi,
8055     
8056     /**
8057      * Strips all HTML tags to sort on text only
8058      * @param {Mixed} s The value being converted
8059      * @return {String} The comparison value
8060      */
8061     asText : function(s){
8062         return String(s).replace(this.stripTagsRE, "");
8063     },
8064     
8065     /**
8066      * Strips all HTML tags to sort on text only - Case insensitive
8067      * @param {Mixed} s The value being converted
8068      * @return {String} The comparison value
8069      */
8070     asUCText : function(s){
8071         return String(s).toUpperCase().replace(this.stripTagsRE, "");
8072     },
8073     
8074     /**
8075      * Case insensitive string
8076      * @param {Mixed} s The value being converted
8077      * @return {String} The comparison value
8078      */
8079     asUCString : function(s) {
8080         return String(s).toUpperCase();
8081     },
8082     
8083     /**
8084      * Date sorting
8085      * @param {Mixed} s The value being converted
8086      * @return {Number} The comparison value
8087      */
8088     asDate : function(s) {
8089         if(!s){
8090             return 0;
8091         }
8092         if(s instanceof Date){
8093             return s.getTime();
8094         }
8095         return Date.parse(String(s));
8096     },
8097     
8098     /**
8099      * Float sorting
8100      * @param {Mixed} s The value being converted
8101      * @return {Float} The comparison value
8102      */
8103     asFloat : function(s) {
8104         var val = parseFloat(String(s).replace(/,/g, ""));
8105         if(isNaN(val)) val = 0;
8106         return val;
8107     },
8108     
8109     /**
8110      * Integer sorting
8111      * @param {Mixed} s The value being converted
8112      * @return {Number} The comparison value
8113      */
8114     asInt : function(s) {
8115         var val = parseInt(String(s).replace(/,/g, ""));
8116         if(isNaN(val)) val = 0;
8117         return val;
8118     }
8119 };/*
8120  * Based on:
8121  * Ext JS Library 1.1.1
8122  * Copyright(c) 2006-2007, Ext JS, LLC.
8123  *
8124  * Originally Released Under LGPL - original licence link has changed is not relivant.
8125  *
8126  * Fork - LGPL
8127  * <script type="text/javascript">
8128  */
8129
8130 /**
8131 * @class Roo.data.Record
8132  * Instances of this class encapsulate both record <em>definition</em> information, and record
8133  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
8134  * to access Records cached in an {@link Roo.data.Store} object.<br>
8135  * <p>
8136  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
8137  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
8138  * objects.<br>
8139  * <p>
8140  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
8141  * @constructor
8142  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
8143  * {@link #create}. The parameters are the same.
8144  * @param {Array} data An associative Array of data values keyed by the field name.
8145  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
8146  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
8147  * not specified an integer id is generated.
8148  */
8149 Roo.data.Record = function(data, id){
8150     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
8151     this.data = data;
8152 };
8153
8154 /**
8155  * Generate a constructor for a specific record layout.
8156  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
8157  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
8158  * Each field definition object may contain the following properties: <ul>
8159  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
8160  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
8161  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
8162  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
8163  * is being used, then this is a string containing the javascript expression to reference the data relative to 
8164  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
8165  * to the data item relative to the record element. If the mapping expression is the same as the field name,
8166  * this may be omitted.</p></li>
8167  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
8168  * <ul><li>auto (Default, implies no conversion)</li>
8169  * <li>string</li>
8170  * <li>int</li>
8171  * <li>float</li>
8172  * <li>boolean</li>
8173  * <li>date</li></ul></p></li>
8174  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
8175  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
8176  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
8177  * by the Reader into an object that will be stored in the Record. It is passed the
8178  * following parameters:<ul>
8179  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
8180  * </ul></p></li>
8181  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
8182  * </ul>
8183  * <br>usage:<br><pre><code>
8184 var TopicRecord = Roo.data.Record.create(
8185     {name: 'title', mapping: 'topic_title'},
8186     {name: 'author', mapping: 'username'},
8187     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
8188     {name: 'lastPost', mapping: 'post_time', type: 'date'},
8189     {name: 'lastPoster', mapping: 'user2'},
8190     {name: 'excerpt', mapping: 'post_text'}
8191 );
8192
8193 var myNewRecord = new TopicRecord({
8194     title: 'Do my job please',
8195     author: 'noobie',
8196     totalPosts: 1,
8197     lastPost: new Date(),
8198     lastPoster: 'Animal',
8199     excerpt: 'No way dude!'
8200 });
8201 myStore.add(myNewRecord);
8202 </code></pre>
8203  * @method create
8204  * @static
8205  */
8206 Roo.data.Record.create = function(o){
8207     var f = function(){
8208         f.superclass.constructor.apply(this, arguments);
8209     };
8210     Roo.extend(f, Roo.data.Record);
8211     var p = f.prototype;
8212     p.fields = new Roo.util.MixedCollection(false, function(field){
8213         return field.name;
8214     });
8215     for(var i = 0, len = o.length; i < len; i++){
8216         p.fields.add(new Roo.data.Field(o[i]));
8217     }
8218     f.getField = function(name){
8219         return p.fields.get(name);  
8220     };
8221     return f;
8222 };
8223
8224 Roo.data.Record.AUTO_ID = 1000;
8225 Roo.data.Record.EDIT = 'edit';
8226 Roo.data.Record.REJECT = 'reject';
8227 Roo.data.Record.COMMIT = 'commit';
8228
8229 Roo.data.Record.prototype = {
8230     /**
8231      * Readonly flag - true if this record has been modified.
8232      * @type Boolean
8233      */
8234     dirty : false,
8235     editing : false,
8236     error: null,
8237     modified: null,
8238
8239     // private
8240     join : function(store){
8241         this.store = store;
8242     },
8243
8244     /**
8245      * Set the named field to the specified value.
8246      * @param {String} name The name of the field to set.
8247      * @param {Object} value The value to set the field to.
8248      */
8249     set : function(name, value){
8250         if(this.data[name] == value){
8251             return;
8252         }
8253         this.dirty = true;
8254         if(!this.modified){
8255             this.modified = {};
8256         }
8257         if(typeof this.modified[name] == 'undefined'){
8258             this.modified[name] = this.data[name];
8259         }
8260         this.data[name] = value;
8261         if(!this.editing && this.store){
8262             this.store.afterEdit(this);
8263         }       
8264     },
8265
8266     /**
8267      * Get the value of the named field.
8268      * @param {String} name The name of the field to get the value of.
8269      * @return {Object} The value of the field.
8270      */
8271     get : function(name){
8272         return this.data[name]; 
8273     },
8274
8275     // private
8276     beginEdit : function(){
8277         this.editing = true;
8278         this.modified = {}; 
8279     },
8280
8281     // private
8282     cancelEdit : function(){
8283         this.editing = false;
8284         delete this.modified;
8285     },
8286
8287     // private
8288     endEdit : function(){
8289         this.editing = false;
8290         if(this.dirty && this.store){
8291             this.store.afterEdit(this);
8292         }
8293     },
8294
8295     /**
8296      * Usually called by the {@link Roo.data.Store} which owns the Record.
8297      * Rejects all changes made to the Record since either creation, or the last commit operation.
8298      * Modified fields are reverted to their original values.
8299      * <p>
8300      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8301      * of reject operations.
8302      */
8303     reject : function(){
8304         var m = this.modified;
8305         for(var n in m){
8306             if(typeof m[n] != "function"){
8307                 this.data[n] = m[n];
8308             }
8309         }
8310         this.dirty = false;
8311         delete this.modified;
8312         this.editing = false;
8313         if(this.store){
8314             this.store.afterReject(this);
8315         }
8316     },
8317
8318     /**
8319      * Usually called by the {@link Roo.data.Store} which owns the Record.
8320      * Commits all changes made to the Record since either creation, or the last commit operation.
8321      * <p>
8322      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8323      * of commit operations.
8324      */
8325     commit : function(){
8326         this.dirty = false;
8327         delete this.modified;
8328         this.editing = false;
8329         if(this.store){
8330             this.store.afterCommit(this);
8331         }
8332     },
8333
8334     // private
8335     hasError : function(){
8336         return this.error != null;
8337     },
8338
8339     // private
8340     clearError : function(){
8341         this.error = null;
8342     },
8343
8344     /**
8345      * Creates a copy of this record.
8346      * @param {String} id (optional) A new record id if you don't want to use this record's id
8347      * @return {Record}
8348      */
8349     copy : function(newId) {
8350         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
8351     }
8352 };/*
8353  * Based on:
8354  * Ext JS Library 1.1.1
8355  * Copyright(c) 2006-2007, Ext JS, LLC.
8356  *
8357  * Originally Released Under LGPL - original licence link has changed is not relivant.
8358  *
8359  * Fork - LGPL
8360  * <script type="text/javascript">
8361  */
8362
8363
8364
8365 /**
8366  * @class Roo.data.Store
8367  * @extends Roo.util.Observable
8368  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
8369  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
8370  * <p>
8371  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
8372  * has no knowledge of the format of the data returned by the Proxy.<br>
8373  * <p>
8374  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
8375  * instances from the data object. These records are cached and made available through accessor functions.
8376  * @constructor
8377  * Creates a new Store.
8378  * @param {Object} config A config object containing the objects needed for the Store to access data,
8379  * and read the data into Records.
8380  */
8381 Roo.data.Store = function(config){
8382     this.data = new Roo.util.MixedCollection(false);
8383     this.data.getKey = function(o){
8384         return o.id;
8385     };
8386     this.baseParams = {};
8387     // private
8388     this.paramNames = {
8389         "start" : "start",
8390         "limit" : "limit",
8391         "sort" : "sort",
8392         "dir" : "dir",
8393         "multisort" : "_multisort"
8394     };
8395
8396     if(config && config.data){
8397         this.inlineData = config.data;
8398         delete config.data;
8399     }
8400
8401     Roo.apply(this, config);
8402     
8403     if(this.reader){ // reader passed
8404         this.reader = Roo.factory(this.reader, Roo.data);
8405         this.reader.xmodule = this.xmodule || false;
8406         if(!this.recordType){
8407             this.recordType = this.reader.recordType;
8408         }
8409         if(this.reader.onMetaChange){
8410             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
8411         }
8412     }
8413
8414     if(this.recordType){
8415         this.fields = this.recordType.prototype.fields;
8416     }
8417     this.modified = [];
8418
8419     this.addEvents({
8420         /**
8421          * @event datachanged
8422          * Fires when the data cache has changed, and a widget which is using this Store
8423          * as a Record cache should refresh its view.
8424          * @param {Store} this
8425          */
8426         datachanged : true,
8427         /**
8428          * @event metachange
8429          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
8430          * @param {Store} this
8431          * @param {Object} meta The JSON metadata
8432          */
8433         metachange : true,
8434         /**
8435          * @event add
8436          * Fires when Records have been added to the Store
8437          * @param {Store} this
8438          * @param {Roo.data.Record[]} records The array of Records added
8439          * @param {Number} index The index at which the record(s) were added
8440          */
8441         add : true,
8442         /**
8443          * @event remove
8444          * Fires when a Record has been removed from the Store
8445          * @param {Store} this
8446          * @param {Roo.data.Record} record The Record that was removed
8447          * @param {Number} index The index at which the record was removed
8448          */
8449         remove : true,
8450         /**
8451          * @event update
8452          * Fires when a Record has been updated
8453          * @param {Store} this
8454          * @param {Roo.data.Record} record The Record that was updated
8455          * @param {String} operation The update operation being performed.  Value may be one of:
8456          * <pre><code>
8457  Roo.data.Record.EDIT
8458  Roo.data.Record.REJECT
8459  Roo.data.Record.COMMIT
8460          * </code></pre>
8461          */
8462         update : true,
8463         /**
8464          * @event clear
8465          * Fires when the data cache has been cleared.
8466          * @param {Store} this
8467          */
8468         clear : true,
8469         /**
8470          * @event beforeload
8471          * Fires before a request is made for a new data object.  If the beforeload handler returns false
8472          * the load action will be canceled.
8473          * @param {Store} this
8474          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8475          */
8476         beforeload : true,
8477         /**
8478          * @event beforeloadadd
8479          * Fires after a new set of Records has been loaded.
8480          * @param {Store} this
8481          * @param {Roo.data.Record[]} records The Records that were loaded
8482          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8483          */
8484         beforeloadadd : true,
8485         /**
8486          * @event load
8487          * Fires after a new set of Records has been loaded, before they are added to the store.
8488          * @param {Store} this
8489          * @param {Roo.data.Record[]} records The Records that were loaded
8490          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8491          * @params {Object} return from reader
8492          */
8493         load : true,
8494         /**
8495          * @event loadexception
8496          * Fires if an exception occurs in the Proxy during loading.
8497          * Called with the signature of the Proxy's "loadexception" event.
8498          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
8499          * 
8500          * @param {Proxy} 
8501          * @param {Object} return from JsonData.reader() - success, totalRecords, records
8502          * @param {Object} load options 
8503          * @param {Object} jsonData from your request (normally this contains the Exception)
8504          */
8505         loadexception : true
8506     });
8507     
8508     if(this.proxy){
8509         this.proxy = Roo.factory(this.proxy, Roo.data);
8510         this.proxy.xmodule = this.xmodule || false;
8511         this.relayEvents(this.proxy,  ["loadexception"]);
8512     }
8513     this.sortToggle = {};
8514     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
8515
8516     Roo.data.Store.superclass.constructor.call(this);
8517
8518     if(this.inlineData){
8519         this.loadData(this.inlineData);
8520         delete this.inlineData;
8521     }
8522 };
8523
8524 Roo.extend(Roo.data.Store, Roo.util.Observable, {
8525      /**
8526     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
8527     * without a remote query - used by combo/forms at present.
8528     */
8529     
8530     /**
8531     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
8532     */
8533     /**
8534     * @cfg {Array} data Inline data to be loaded when the store is initialized.
8535     */
8536     /**
8537     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
8538     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
8539     */
8540     /**
8541     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
8542     * on any HTTP request
8543     */
8544     /**
8545     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
8546     */
8547     /**
8548     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
8549     */
8550     multiSort: false,
8551     /**
8552     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
8553     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
8554     */
8555     remoteSort : false,
8556
8557     /**
8558     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
8559      * loaded or when a record is removed. (defaults to false).
8560     */
8561     pruneModifiedRecords : false,
8562
8563     // private
8564     lastOptions : null,
8565
8566     /**
8567      * Add Records to the Store and fires the add event.
8568      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8569      */
8570     add : function(records){
8571         records = [].concat(records);
8572         for(var i = 0, len = records.length; i < len; i++){
8573             records[i].join(this);
8574         }
8575         var index = this.data.length;
8576         this.data.addAll(records);
8577         this.fireEvent("add", this, records, index);
8578     },
8579
8580     /**
8581      * Remove a Record from the Store and fires the remove event.
8582      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
8583      */
8584     remove : function(record){
8585         var index = this.data.indexOf(record);
8586         this.data.removeAt(index);
8587         if(this.pruneModifiedRecords){
8588             this.modified.remove(record);
8589         }
8590         this.fireEvent("remove", this, record, index);
8591     },
8592
8593     /**
8594      * Remove all Records from the Store and fires the clear event.
8595      */
8596     removeAll : function(){
8597         this.data.clear();
8598         if(this.pruneModifiedRecords){
8599             this.modified = [];
8600         }
8601         this.fireEvent("clear", this);
8602     },
8603
8604     /**
8605      * Inserts Records to the Store at the given index and fires the add event.
8606      * @param {Number} index The start index at which to insert the passed Records.
8607      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8608      */
8609     insert : function(index, records){
8610         records = [].concat(records);
8611         for(var i = 0, len = records.length; i < len; i++){
8612             this.data.insert(index, records[i]);
8613             records[i].join(this);
8614         }
8615         this.fireEvent("add", this, records, index);
8616     },
8617
8618     /**
8619      * Get the index within the cache of the passed Record.
8620      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
8621      * @return {Number} The index of the passed Record. Returns -1 if not found.
8622      */
8623     indexOf : function(record){
8624         return this.data.indexOf(record);
8625     },
8626
8627     /**
8628      * Get the index within the cache of the Record with the passed id.
8629      * @param {String} id The id of the Record to find.
8630      * @return {Number} The index of the Record. Returns -1 if not found.
8631      */
8632     indexOfId : function(id){
8633         return this.data.indexOfKey(id);
8634     },
8635
8636     /**
8637      * Get the Record with the specified id.
8638      * @param {String} id The id of the Record to find.
8639      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
8640      */
8641     getById : function(id){
8642         return this.data.key(id);
8643     },
8644
8645     /**
8646      * Get the Record at the specified index.
8647      * @param {Number} index The index of the Record to find.
8648      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
8649      */
8650     getAt : function(index){
8651         return this.data.itemAt(index);
8652     },
8653
8654     /**
8655      * Returns a range of Records between specified indices.
8656      * @param {Number} startIndex (optional) The starting index (defaults to 0)
8657      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
8658      * @return {Roo.data.Record[]} An array of Records
8659      */
8660     getRange : function(start, end){
8661         return this.data.getRange(start, end);
8662     },
8663
8664     // private
8665     storeOptions : function(o){
8666         o = Roo.apply({}, o);
8667         delete o.callback;
8668         delete o.scope;
8669         this.lastOptions = o;
8670     },
8671
8672     /**
8673      * Loads the Record cache from the configured Proxy using the configured Reader.
8674      * <p>
8675      * If using remote paging, then the first load call must specify the <em>start</em>
8676      * and <em>limit</em> properties in the options.params property to establish the initial
8677      * position within the dataset, and the number of Records to cache on each read from the Proxy.
8678      * <p>
8679      * <strong>It is important to note that for remote data sources, loading is asynchronous,
8680      * and this call will return before the new data has been loaded. Perform any post-processing
8681      * in a callback function, or in a "load" event handler.</strong>
8682      * <p>
8683      * @param {Object} options An object containing properties which control loading options:<ul>
8684      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
8685      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
8686      * passed the following arguments:<ul>
8687      * <li>r : Roo.data.Record[]</li>
8688      * <li>options: Options object from the load call</li>
8689      * <li>success: Boolean success indicator</li></ul></li>
8690      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
8691      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
8692      * </ul>
8693      */
8694     load : function(options){
8695         options = options || {};
8696         if(this.fireEvent("beforeload", this, options) !== false){
8697             this.storeOptions(options);
8698             var p = Roo.apply(options.params || {}, this.baseParams);
8699             // if meta was not loaded from remote source.. try requesting it.
8700             if (!this.reader.metaFromRemote) {
8701                 p._requestMeta = 1;
8702             }
8703             if(this.sortInfo && this.remoteSort){
8704                 var pn = this.paramNames;
8705                 p[pn["sort"]] = this.sortInfo.field;
8706                 p[pn["dir"]] = this.sortInfo.direction;
8707             }
8708             if (this.multiSort) {
8709                 var pn = this.paramNames;
8710                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
8711             }
8712             
8713             this.proxy.load(p, this.reader, this.loadRecords, this, options);
8714         }
8715     },
8716
8717     /**
8718      * Reloads the Record cache from the configured Proxy using the configured Reader and
8719      * the options from the last load operation performed.
8720      * @param {Object} options (optional) An object containing properties which may override the options
8721      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
8722      * the most recently used options are reused).
8723      */
8724     reload : function(options){
8725         this.load(Roo.applyIf(options||{}, this.lastOptions));
8726     },
8727
8728     // private
8729     // Called as a callback by the Reader during a load operation.
8730     loadRecords : function(o, options, success){
8731         if(!o || success === false){
8732             if(success !== false){
8733                 this.fireEvent("load", this, [], options, o);
8734             }
8735             if(options.callback){
8736                 options.callback.call(options.scope || this, [], options, false);
8737             }
8738             return;
8739         }
8740         // if data returned failure - throw an exception.
8741         if (o.success === false) {
8742             // show a message if no listener is registered.
8743             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
8744                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
8745             }
8746             // loadmask wil be hooked into this..
8747             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
8748             return;
8749         }
8750         var r = o.records, t = o.totalRecords || r.length;
8751         
8752         this.fireEvent("beforeloadadd", this, r, options, o);
8753         
8754         if(!options || options.add !== true){
8755             if(this.pruneModifiedRecords){
8756                 this.modified = [];
8757             }
8758             for(var i = 0, len = r.length; i < len; i++){
8759                 r[i].join(this);
8760             }
8761             if(this.snapshot){
8762                 this.data = this.snapshot;
8763                 delete this.snapshot;
8764             }
8765             this.data.clear();
8766             this.data.addAll(r);
8767             this.totalLength = t;
8768             this.applySort();
8769             this.fireEvent("datachanged", this);
8770         }else{
8771             this.totalLength = Math.max(t, this.data.length+r.length);
8772             this.add(r);
8773         }
8774         this.fireEvent("load", this, r, options, o);
8775         if(options.callback){
8776             options.callback.call(options.scope || this, r, options, true);
8777         }
8778     },
8779
8780
8781     /**
8782      * Loads data from a passed data block. A Reader which understands the format of the data
8783      * must have been configured in the constructor.
8784      * @param {Object} data The data block from which to read the Records.  The format of the data expected
8785      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
8786      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
8787      */
8788     loadData : function(o, append){
8789         var r = this.reader.readRecords(o);
8790         this.loadRecords(r, {add: append}, true);
8791     },
8792
8793     /**
8794      * Gets the number of cached records.
8795      * <p>
8796      * <em>If using paging, this may not be the total size of the dataset. If the data object
8797      * used by the Reader contains the dataset size, then the getTotalCount() function returns
8798      * the data set size</em>
8799      */
8800     getCount : function(){
8801         return this.data.length || 0;
8802     },
8803
8804     /**
8805      * Gets the total number of records in the dataset as returned by the server.
8806      * <p>
8807      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
8808      * the dataset size</em>
8809      */
8810     getTotalCount : function(){
8811         return this.totalLength || 0;
8812     },
8813
8814     /**
8815      * Returns the sort state of the Store as an object with two properties:
8816      * <pre><code>
8817  field {String} The name of the field by which the Records are sorted
8818  direction {String} The sort order, "ASC" or "DESC"
8819      * </code></pre>
8820      */
8821     getSortState : function(){
8822         return this.sortInfo;
8823     },
8824
8825     // private
8826     applySort : function(){
8827         if(this.sortInfo && !this.remoteSort){
8828             var s = this.sortInfo, f = s.field;
8829             var st = this.fields.get(f).sortType;
8830             var fn = function(r1, r2){
8831                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
8832                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
8833             };
8834             this.data.sort(s.direction, fn);
8835             if(this.snapshot && this.snapshot != this.data){
8836                 this.snapshot.sort(s.direction, fn);
8837             }
8838         }
8839     },
8840
8841     /**
8842      * Sets the default sort column and order to be used by the next load operation.
8843      * @param {String} fieldName The name of the field to sort by.
8844      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
8845      */
8846     setDefaultSort : function(field, dir){
8847         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
8848     },
8849
8850     /**
8851      * Sort the Records.
8852      * If remote sorting is used, the sort is performed on the server, and the cache is
8853      * reloaded. If local sorting is used, the cache is sorted internally.
8854      * @param {String} fieldName The name of the field to sort by.
8855      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
8856      */
8857     sort : function(fieldName, dir){
8858         var f = this.fields.get(fieldName);
8859         if(!dir){
8860             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
8861             
8862             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
8863                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
8864             }else{
8865                 dir = f.sortDir;
8866             }
8867         }
8868         this.sortToggle[f.name] = dir;
8869         this.sortInfo = {field: f.name, direction: dir};
8870         if(!this.remoteSort){
8871             this.applySort();
8872             this.fireEvent("datachanged", this);
8873         }else{
8874             this.load(this.lastOptions);
8875         }
8876     },
8877
8878     /**
8879      * Calls the specified function for each of the Records in the cache.
8880      * @param {Function} fn The function to call. The Record is passed as the first parameter.
8881      * Returning <em>false</em> aborts and exits the iteration.
8882      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
8883      */
8884     each : function(fn, scope){
8885         this.data.each(fn, scope);
8886     },
8887
8888     /**
8889      * Gets all records modified since the last commit.  Modified records are persisted across load operations
8890      * (e.g., during paging).
8891      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
8892      */
8893     getModifiedRecords : function(){
8894         return this.modified;
8895     },
8896
8897     // private
8898     createFilterFn : function(property, value, anyMatch){
8899         if(!value.exec){ // not a regex
8900             value = String(value);
8901             if(value.length == 0){
8902                 return false;
8903             }
8904             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
8905         }
8906         return function(r){
8907             return value.test(r.data[property]);
8908         };
8909     },
8910
8911     /**
8912      * Sums the value of <i>property</i> for each record between start and end and returns the result.
8913      * @param {String} property A field on your records
8914      * @param {Number} start The record index to start at (defaults to 0)
8915      * @param {Number} end The last record index to include (defaults to length - 1)
8916      * @return {Number} The sum
8917      */
8918     sum : function(property, start, end){
8919         var rs = this.data.items, v = 0;
8920         start = start || 0;
8921         end = (end || end === 0) ? end : rs.length-1;
8922
8923         for(var i = start; i <= end; i++){
8924             v += (rs[i].data[property] || 0);
8925         }
8926         return v;
8927     },
8928
8929     /**
8930      * Filter the records by a specified property.
8931      * @param {String} field A field on your records
8932      * @param {String/RegExp} value Either a string that the field
8933      * should start with or a RegExp to test against the field
8934      * @param {Boolean} anyMatch True to match any part not just the beginning
8935      */
8936     filter : function(property, value, anyMatch){
8937         var fn = this.createFilterFn(property, value, anyMatch);
8938         return fn ? this.filterBy(fn) : this.clearFilter();
8939     },
8940
8941     /**
8942      * Filter by a function. The specified function will be called with each
8943      * record in this data source. If the function returns true the record is included,
8944      * otherwise it is filtered.
8945      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
8946      * @param {Object} scope (optional) The scope of the function (defaults to this)
8947      */
8948     filterBy : function(fn, scope){
8949         this.snapshot = this.snapshot || this.data;
8950         this.data = this.queryBy(fn, scope||this);
8951         this.fireEvent("datachanged", this);
8952     },
8953
8954     /**
8955      * Query the records by a specified property.
8956      * @param {String} field A field on your records
8957      * @param {String/RegExp} value Either a string that the field
8958      * should start with or a RegExp to test against the field
8959      * @param {Boolean} anyMatch True to match any part not just the beginning
8960      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
8961      */
8962     query : function(property, value, anyMatch){
8963         var fn = this.createFilterFn(property, value, anyMatch);
8964         return fn ? this.queryBy(fn) : this.data.clone();
8965     },
8966
8967     /**
8968      * Query by a function. The specified function will be called with each
8969      * record in this data source. If the function returns true the record is included
8970      * in the results.
8971      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
8972      * @param {Object} scope (optional) The scope of the function (defaults to this)
8973       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
8974      **/
8975     queryBy : function(fn, scope){
8976         var data = this.snapshot || this.data;
8977         return data.filterBy(fn, scope||this);
8978     },
8979
8980     /**
8981      * Collects unique values for a particular dataIndex from this store.
8982      * @param {String} dataIndex The property to collect
8983      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
8984      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
8985      * @return {Array} An array of the unique values
8986      **/
8987     collect : function(dataIndex, allowNull, bypassFilter){
8988         var d = (bypassFilter === true && this.snapshot) ?
8989                 this.snapshot.items : this.data.items;
8990         var v, sv, r = [], l = {};
8991         for(var i = 0, len = d.length; i < len; i++){
8992             v = d[i].data[dataIndex];
8993             sv = String(v);
8994             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
8995                 l[sv] = true;
8996                 r[r.length] = v;
8997             }
8998         }
8999         return r;
9000     },
9001
9002     /**
9003      * Revert to a view of the Record cache with no filtering applied.
9004      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
9005      */
9006     clearFilter : function(suppressEvent){
9007         if(this.snapshot && this.snapshot != this.data){
9008             this.data = this.snapshot;
9009             delete this.snapshot;
9010             if(suppressEvent !== true){
9011                 this.fireEvent("datachanged", this);
9012             }
9013         }
9014     },
9015
9016     // private
9017     afterEdit : function(record){
9018         if(this.modified.indexOf(record) == -1){
9019             this.modified.push(record);
9020         }
9021         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
9022     },
9023     
9024     // private
9025     afterReject : function(record){
9026         this.modified.remove(record);
9027         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
9028     },
9029
9030     // private
9031     afterCommit : function(record){
9032         this.modified.remove(record);
9033         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
9034     },
9035
9036     /**
9037      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
9038      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
9039      */
9040     commitChanges : function(){
9041         var m = this.modified.slice(0);
9042         this.modified = [];
9043         for(var i = 0, len = m.length; i < len; i++){
9044             m[i].commit();
9045         }
9046     },
9047
9048     /**
9049      * Cancel outstanding changes on all changed records.
9050      */
9051     rejectChanges : function(){
9052         var m = this.modified.slice(0);
9053         this.modified = [];
9054         for(var i = 0, len = m.length; i < len; i++){
9055             m[i].reject();
9056         }
9057     },
9058
9059     onMetaChange : function(meta, rtype, o){
9060         this.recordType = rtype;
9061         this.fields = rtype.prototype.fields;
9062         delete this.snapshot;
9063         this.sortInfo = meta.sortInfo || this.sortInfo;
9064         this.modified = [];
9065         this.fireEvent('metachange', this, this.reader.meta);
9066     },
9067     
9068     moveIndex : function(data, type)
9069     {
9070         var index = this.indexOf(data);
9071         
9072         var newIndex = index + type;
9073         
9074         this.remove(data);
9075         
9076         this.insert(newIndex, data);
9077         
9078     }
9079 });/*
9080  * Based on:
9081  * Ext JS Library 1.1.1
9082  * Copyright(c) 2006-2007, Ext JS, LLC.
9083  *
9084  * Originally Released Under LGPL - original licence link has changed is not relivant.
9085  *
9086  * Fork - LGPL
9087  * <script type="text/javascript">
9088  */
9089
9090 /**
9091  * @class Roo.data.SimpleStore
9092  * @extends Roo.data.Store
9093  * Small helper class to make creating Stores from Array data easier.
9094  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
9095  * @cfg {Array} fields An array of field definition objects, or field name strings.
9096  * @cfg {Array} data The multi-dimensional array of data
9097  * @constructor
9098  * @param {Object} config
9099  */
9100 Roo.data.SimpleStore = function(config){
9101     Roo.data.SimpleStore.superclass.constructor.call(this, {
9102         isLocal : true,
9103         reader: new Roo.data.ArrayReader({
9104                 id: config.id
9105             },
9106             Roo.data.Record.create(config.fields)
9107         ),
9108         proxy : new Roo.data.MemoryProxy(config.data)
9109     });
9110     this.load();
9111 };
9112 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
9113  * Based on:
9114  * Ext JS Library 1.1.1
9115  * Copyright(c) 2006-2007, Ext JS, LLC.
9116  *
9117  * Originally Released Under LGPL - original licence link has changed is not relivant.
9118  *
9119  * Fork - LGPL
9120  * <script type="text/javascript">
9121  */
9122
9123 /**
9124 /**
9125  * @extends Roo.data.Store
9126  * @class Roo.data.JsonStore
9127  * Small helper class to make creating Stores for JSON data easier. <br/>
9128 <pre><code>
9129 var store = new Roo.data.JsonStore({
9130     url: 'get-images.php',
9131     root: 'images',
9132     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
9133 });
9134 </code></pre>
9135  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
9136  * JsonReader and HttpProxy (unless inline data is provided).</b>
9137  * @cfg {Array} fields An array of field definition objects, or field name strings.
9138  * @constructor
9139  * @param {Object} config
9140  */
9141 Roo.data.JsonStore = function(c){
9142     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
9143         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
9144         reader: new Roo.data.JsonReader(c, c.fields)
9145     }));
9146 };
9147 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
9148  * Based on:
9149  * Ext JS Library 1.1.1
9150  * Copyright(c) 2006-2007, Ext JS, LLC.
9151  *
9152  * Originally Released Under LGPL - original licence link has changed is not relivant.
9153  *
9154  * Fork - LGPL
9155  * <script type="text/javascript">
9156  */
9157
9158  
9159 Roo.data.Field = function(config){
9160     if(typeof config == "string"){
9161         config = {name: config};
9162     }
9163     Roo.apply(this, config);
9164     
9165     if(!this.type){
9166         this.type = "auto";
9167     }
9168     
9169     var st = Roo.data.SortTypes;
9170     // named sortTypes are supported, here we look them up
9171     if(typeof this.sortType == "string"){
9172         this.sortType = st[this.sortType];
9173     }
9174     
9175     // set default sortType for strings and dates
9176     if(!this.sortType){
9177         switch(this.type){
9178             case "string":
9179                 this.sortType = st.asUCString;
9180                 break;
9181             case "date":
9182                 this.sortType = st.asDate;
9183                 break;
9184             default:
9185                 this.sortType = st.none;
9186         }
9187     }
9188
9189     // define once
9190     var stripRe = /[\$,%]/g;
9191
9192     // prebuilt conversion function for this field, instead of
9193     // switching every time we're reading a value
9194     if(!this.convert){
9195         var cv, dateFormat = this.dateFormat;
9196         switch(this.type){
9197             case "":
9198             case "auto":
9199             case undefined:
9200                 cv = function(v){ return v; };
9201                 break;
9202             case "string":
9203                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
9204                 break;
9205             case "int":
9206                 cv = function(v){
9207                     return v !== undefined && v !== null && v !== '' ?
9208                            parseInt(String(v).replace(stripRe, ""), 10) : '';
9209                     };
9210                 break;
9211             case "float":
9212                 cv = function(v){
9213                     return v !== undefined && v !== null && v !== '' ?
9214                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
9215                     };
9216                 break;
9217             case "bool":
9218             case "boolean":
9219                 cv = function(v){ return v === true || v === "true" || v == 1; };
9220                 break;
9221             case "date":
9222                 cv = function(v){
9223                     if(!v){
9224                         return '';
9225                     }
9226                     if(v instanceof Date){
9227                         return v;
9228                     }
9229                     if(dateFormat){
9230                         if(dateFormat == "timestamp"){
9231                             return new Date(v*1000);
9232                         }
9233                         return Date.parseDate(v, dateFormat);
9234                     }
9235                     var parsed = Date.parse(v);
9236                     return parsed ? new Date(parsed) : null;
9237                 };
9238              break;
9239             
9240         }
9241         this.convert = cv;
9242     }
9243 };
9244
9245 Roo.data.Field.prototype = {
9246     dateFormat: null,
9247     defaultValue: "",
9248     mapping: null,
9249     sortType : null,
9250     sortDir : "ASC"
9251 };/*
9252  * Based on:
9253  * Ext JS Library 1.1.1
9254  * Copyright(c) 2006-2007, Ext JS, LLC.
9255  *
9256  * Originally Released Under LGPL - original licence link has changed is not relivant.
9257  *
9258  * Fork - LGPL
9259  * <script type="text/javascript">
9260  */
9261  
9262 // Base class for reading structured data from a data source.  This class is intended to be
9263 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
9264
9265 /**
9266  * @class Roo.data.DataReader
9267  * Base class for reading structured data from a data source.  This class is intended to be
9268  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
9269  */
9270
9271 Roo.data.DataReader = function(meta, recordType){
9272     
9273     this.meta = meta;
9274     
9275     this.recordType = recordType instanceof Array ? 
9276         Roo.data.Record.create(recordType) : recordType;
9277 };
9278
9279 Roo.data.DataReader.prototype = {
9280      /**
9281      * Create an empty record
9282      * @param {Object} data (optional) - overlay some values
9283      * @return {Roo.data.Record} record created.
9284      */
9285     newRow :  function(d) {
9286         var da =  {};
9287         this.recordType.prototype.fields.each(function(c) {
9288             switch( c.type) {
9289                 case 'int' : da[c.name] = 0; break;
9290                 case 'date' : da[c.name] = new Date(); break;
9291                 case 'float' : da[c.name] = 0.0; break;
9292                 case 'boolean' : da[c.name] = false; break;
9293                 default : da[c.name] = ""; break;
9294             }
9295             
9296         });
9297         return new this.recordType(Roo.apply(da, d));
9298     }
9299     
9300 };/*
9301  * Based on:
9302  * Ext JS Library 1.1.1
9303  * Copyright(c) 2006-2007, Ext JS, LLC.
9304  *
9305  * Originally Released Under LGPL - original licence link has changed is not relivant.
9306  *
9307  * Fork - LGPL
9308  * <script type="text/javascript">
9309  */
9310
9311 /**
9312  * @class Roo.data.DataProxy
9313  * @extends Roo.data.Observable
9314  * This class is an abstract base class for implementations which provide retrieval of
9315  * unformatted data objects.<br>
9316  * <p>
9317  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
9318  * (of the appropriate type which knows how to parse the data object) to provide a block of
9319  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
9320  * <p>
9321  * Custom implementations must implement the load method as described in
9322  * {@link Roo.data.HttpProxy#load}.
9323  */
9324 Roo.data.DataProxy = function(){
9325     this.addEvents({
9326         /**
9327          * @event beforeload
9328          * Fires before a network request is made to retrieve a data object.
9329          * @param {Object} This DataProxy object.
9330          * @param {Object} params The params parameter to the load function.
9331          */
9332         beforeload : true,
9333         /**
9334          * @event load
9335          * Fires before the load method's callback is called.
9336          * @param {Object} This DataProxy object.
9337          * @param {Object} o The data object.
9338          * @param {Object} arg The callback argument object passed to the load function.
9339          */
9340         load : true,
9341         /**
9342          * @event loadexception
9343          * Fires if an Exception occurs during data retrieval.
9344          * @param {Object} This DataProxy object.
9345          * @param {Object} o The data object.
9346          * @param {Object} arg The callback argument object passed to the load function.
9347          * @param {Object} e The Exception.
9348          */
9349         loadexception : true
9350     });
9351     Roo.data.DataProxy.superclass.constructor.call(this);
9352 };
9353
9354 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
9355
9356     /**
9357      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
9358      */
9359 /*
9360  * Based on:
9361  * Ext JS Library 1.1.1
9362  * Copyright(c) 2006-2007, Ext JS, LLC.
9363  *
9364  * Originally Released Under LGPL - original licence link has changed is not relivant.
9365  *
9366  * Fork - LGPL
9367  * <script type="text/javascript">
9368  */
9369 /**
9370  * @class Roo.data.MemoryProxy
9371  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
9372  * to the Reader when its load method is called.
9373  * @constructor
9374  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
9375  */
9376 Roo.data.MemoryProxy = function(data){
9377     if (data.data) {
9378         data = data.data;
9379     }
9380     Roo.data.MemoryProxy.superclass.constructor.call(this);
9381     this.data = data;
9382 };
9383
9384 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
9385     /**
9386      * Load data from the requested source (in this case an in-memory
9387      * data object passed to the constructor), read the data object into
9388      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9389      * process that block using the passed callback.
9390      * @param {Object} params This parameter is not used by the MemoryProxy class.
9391      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9392      * object into a block of Roo.data.Records.
9393      * @param {Function} callback The function into which to pass the block of Roo.data.records.
9394      * The function must be passed <ul>
9395      * <li>The Record block object</li>
9396      * <li>The "arg" argument from the load function</li>
9397      * <li>A boolean success indicator</li>
9398      * </ul>
9399      * @param {Object} scope The scope in which to call the callback
9400      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9401      */
9402     load : function(params, reader, callback, scope, arg){
9403         params = params || {};
9404         var result;
9405         try {
9406             result = reader.readRecords(this.data);
9407         }catch(e){
9408             this.fireEvent("loadexception", this, arg, null, e);
9409             callback.call(scope, null, arg, false);
9410             return;
9411         }
9412         callback.call(scope, result, arg, true);
9413     },
9414     
9415     // private
9416     update : function(params, records){
9417         
9418     }
9419 });/*
9420  * Based on:
9421  * Ext JS Library 1.1.1
9422  * Copyright(c) 2006-2007, Ext JS, LLC.
9423  *
9424  * Originally Released Under LGPL - original licence link has changed is not relivant.
9425  *
9426  * Fork - LGPL
9427  * <script type="text/javascript">
9428  */
9429 /**
9430  * @class Roo.data.HttpProxy
9431  * @extends Roo.data.DataProxy
9432  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
9433  * configured to reference a certain URL.<br><br>
9434  * <p>
9435  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
9436  * from which the running page was served.<br><br>
9437  * <p>
9438  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
9439  * <p>
9440  * Be aware that to enable the browser to parse an XML document, the server must set
9441  * the Content-Type header in the HTTP response to "text/xml".
9442  * @constructor
9443  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
9444  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
9445  * will be used to make the request.
9446  */
9447 Roo.data.HttpProxy = function(conn){
9448     Roo.data.HttpProxy.superclass.constructor.call(this);
9449     // is conn a conn config or a real conn?
9450     this.conn = conn;
9451     this.useAjax = !conn || !conn.events;
9452   
9453 };
9454
9455 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
9456     // thse are take from connection...
9457     
9458     /**
9459      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
9460      */
9461     /**
9462      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
9463      * extra parameters to each request made by this object. (defaults to undefined)
9464      */
9465     /**
9466      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
9467      *  to each request made by this object. (defaults to undefined)
9468      */
9469     /**
9470      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
9471      */
9472     /**
9473      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
9474      */
9475      /**
9476      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
9477      * @type Boolean
9478      */
9479   
9480
9481     /**
9482      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
9483      * @type Boolean
9484      */
9485     /**
9486      * Return the {@link Roo.data.Connection} object being used by this Proxy.
9487      * @return {Connection} The Connection object. This object may be used to subscribe to events on
9488      * a finer-grained basis than the DataProxy events.
9489      */
9490     getConnection : function(){
9491         return this.useAjax ? Roo.Ajax : this.conn;
9492     },
9493
9494     /**
9495      * Load data from the configured {@link Roo.data.Connection}, read the data object into
9496      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
9497      * process that block using the passed callback.
9498      * @param {Object} params An object containing properties which are to be used as HTTP parameters
9499      * for the request to the remote server.
9500      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9501      * object into a block of Roo.data.Records.
9502      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9503      * The function must be passed <ul>
9504      * <li>The Record block object</li>
9505      * <li>The "arg" argument from the load function</li>
9506      * <li>A boolean success indicator</li>
9507      * </ul>
9508      * @param {Object} scope The scope in which to call the callback
9509      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9510      */
9511     load : function(params, reader, callback, scope, arg){
9512         if(this.fireEvent("beforeload", this, params) !== false){
9513             var  o = {
9514                 params : params || {},
9515                 request: {
9516                     callback : callback,
9517                     scope : scope,
9518                     arg : arg
9519                 },
9520                 reader: reader,
9521                 callback : this.loadResponse,
9522                 scope: this
9523             };
9524             if(this.useAjax){
9525                 Roo.applyIf(o, this.conn);
9526                 if(this.activeRequest){
9527                     Roo.Ajax.abort(this.activeRequest);
9528                 }
9529                 this.activeRequest = Roo.Ajax.request(o);
9530             }else{
9531                 this.conn.request(o);
9532             }
9533         }else{
9534             callback.call(scope||this, null, arg, false);
9535         }
9536     },
9537
9538     // private
9539     loadResponse : function(o, success, response){
9540         delete this.activeRequest;
9541         if(!success){
9542             this.fireEvent("loadexception", this, o, response);
9543             o.request.callback.call(o.request.scope, null, o.request.arg, false);
9544             return;
9545         }
9546         var result;
9547         try {
9548             result = o.reader.read(response);
9549         }catch(e){
9550             this.fireEvent("loadexception", this, o, response, e);
9551             o.request.callback.call(o.request.scope, null, o.request.arg, false);
9552             return;
9553         }
9554         
9555         this.fireEvent("load", this, o, o.request.arg);
9556         o.request.callback.call(o.request.scope, result, o.request.arg, true);
9557     },
9558
9559     // private
9560     update : function(dataSet){
9561
9562     },
9563
9564     // private
9565     updateResponse : function(dataSet){
9566
9567     }
9568 });/*
9569  * Based on:
9570  * Ext JS Library 1.1.1
9571  * Copyright(c) 2006-2007, Ext JS, LLC.
9572  *
9573  * Originally Released Under LGPL - original licence link has changed is not relivant.
9574  *
9575  * Fork - LGPL
9576  * <script type="text/javascript">
9577  */
9578
9579 /**
9580  * @class Roo.data.ScriptTagProxy
9581  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
9582  * other than the originating domain of the running page.<br><br>
9583  * <p>
9584  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
9585  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
9586  * <p>
9587  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
9588  * source code that is used as the source inside a &lt;script> tag.<br><br>
9589  * <p>
9590  * In order for the browser to process the returned data, the server must wrap the data object
9591  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
9592  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
9593  * depending on whether the callback name was passed:
9594  * <p>
9595  * <pre><code>
9596 boolean scriptTag = false;
9597 String cb = request.getParameter("callback");
9598 if (cb != null) {
9599     scriptTag = true;
9600     response.setContentType("text/javascript");
9601 } else {
9602     response.setContentType("application/x-json");
9603 }
9604 Writer out = response.getWriter();
9605 if (scriptTag) {
9606     out.write(cb + "(");
9607 }
9608 out.print(dataBlock.toJsonString());
9609 if (scriptTag) {
9610     out.write(");");
9611 }
9612 </pre></code>
9613  *
9614  * @constructor
9615  * @param {Object} config A configuration object.
9616  */
9617 Roo.data.ScriptTagProxy = function(config){
9618     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
9619     Roo.apply(this, config);
9620     this.head = document.getElementsByTagName("head")[0];
9621 };
9622
9623 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
9624
9625 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
9626     /**
9627      * @cfg {String} url The URL from which to request the data object.
9628      */
9629     /**
9630      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
9631      */
9632     timeout : 30000,
9633     /**
9634      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
9635      * the server the name of the callback function set up by the load call to process the returned data object.
9636      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
9637      * javascript output which calls this named function passing the data object as its only parameter.
9638      */
9639     callbackParam : "callback",
9640     /**
9641      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
9642      * name to the request.
9643      */
9644     nocache : true,
9645
9646     /**
9647      * Load data from the configured URL, read the data object into
9648      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9649      * process that block using the passed callback.
9650      * @param {Object} params An object containing properties which are to be used as HTTP parameters
9651      * for the request to the remote server.
9652      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9653      * object into a block of Roo.data.Records.
9654      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9655      * The function must be passed <ul>
9656      * <li>The Record block object</li>
9657      * <li>The "arg" argument from the load function</li>
9658      * <li>A boolean success indicator</li>
9659      * </ul>
9660      * @param {Object} scope The scope in which to call the callback
9661      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9662      */
9663     load : function(params, reader, callback, scope, arg){
9664         if(this.fireEvent("beforeload", this, params) !== false){
9665
9666             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
9667
9668             var url = this.url;
9669             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
9670             if(this.nocache){
9671                 url += "&_dc=" + (new Date().getTime());
9672             }
9673             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
9674             var trans = {
9675                 id : transId,
9676                 cb : "stcCallback"+transId,
9677                 scriptId : "stcScript"+transId,
9678                 params : params,
9679                 arg : arg,
9680                 url : url,
9681                 callback : callback,
9682                 scope : scope,
9683                 reader : reader
9684             };
9685             var conn = this;
9686
9687             window[trans.cb] = function(o){
9688                 conn.handleResponse(o, trans);
9689             };
9690
9691             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
9692
9693             if(this.autoAbort !== false){
9694                 this.abort();
9695             }
9696
9697             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
9698
9699             var script = document.createElement("script");
9700             script.setAttribute("src", url);
9701             script.setAttribute("type", "text/javascript");
9702             script.setAttribute("id", trans.scriptId);
9703             this.head.appendChild(script);
9704
9705             this.trans = trans;
9706         }else{
9707             callback.call(scope||this, null, arg, false);
9708         }
9709     },
9710
9711     // private
9712     isLoading : function(){
9713         return this.trans ? true : false;
9714     },
9715
9716     /**
9717      * Abort the current server request.
9718      */
9719     abort : function(){
9720         if(this.isLoading()){
9721             this.destroyTrans(this.trans);
9722         }
9723     },
9724
9725     // private
9726     destroyTrans : function(trans, isLoaded){
9727         this.head.removeChild(document.getElementById(trans.scriptId));
9728         clearTimeout(trans.timeoutId);
9729         if(isLoaded){
9730             window[trans.cb] = undefined;
9731             try{
9732                 delete window[trans.cb];
9733             }catch(e){}
9734         }else{
9735             // if hasn't been loaded, wait for load to remove it to prevent script error
9736             window[trans.cb] = function(){
9737                 window[trans.cb] = undefined;
9738                 try{
9739                     delete window[trans.cb];
9740                 }catch(e){}
9741             };
9742         }
9743     },
9744
9745     // private
9746     handleResponse : function(o, trans){
9747         this.trans = false;
9748         this.destroyTrans(trans, true);
9749         var result;
9750         try {
9751             result = trans.reader.readRecords(o);
9752         }catch(e){
9753             this.fireEvent("loadexception", this, o, trans.arg, e);
9754             trans.callback.call(trans.scope||window, null, trans.arg, false);
9755             return;
9756         }
9757         this.fireEvent("load", this, o, trans.arg);
9758         trans.callback.call(trans.scope||window, result, trans.arg, true);
9759     },
9760
9761     // private
9762     handleFailure : function(trans){
9763         this.trans = false;
9764         this.destroyTrans(trans, false);
9765         this.fireEvent("loadexception", this, null, trans.arg);
9766         trans.callback.call(trans.scope||window, null, trans.arg, false);
9767     }
9768 });/*
9769  * Based on:
9770  * Ext JS Library 1.1.1
9771  * Copyright(c) 2006-2007, Ext JS, LLC.
9772  *
9773  * Originally Released Under LGPL - original licence link has changed is not relivant.
9774  *
9775  * Fork - LGPL
9776  * <script type="text/javascript">
9777  */
9778
9779 /**
9780  * @class Roo.data.JsonReader
9781  * @extends Roo.data.DataReader
9782  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
9783  * based on mappings in a provided Roo.data.Record constructor.
9784  * 
9785  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
9786  * in the reply previously. 
9787  * 
9788  * <p>
9789  * Example code:
9790  * <pre><code>
9791 var RecordDef = Roo.data.Record.create([
9792     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
9793     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
9794 ]);
9795 var myReader = new Roo.data.JsonReader({
9796     totalProperty: "results",    // The property which contains the total dataset size (optional)
9797     root: "rows",                // The property which contains an Array of row objects
9798     id: "id"                     // The property within each row object that provides an ID for the record (optional)
9799 }, RecordDef);
9800 </code></pre>
9801  * <p>
9802  * This would consume a JSON file like this:
9803  * <pre><code>
9804 { 'results': 2, 'rows': [
9805     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
9806     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
9807 }
9808 </code></pre>
9809  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
9810  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
9811  * paged from the remote server.
9812  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
9813  * @cfg {String} root name of the property which contains the Array of row objects.
9814  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
9815  * @constructor
9816  * Create a new JsonReader
9817  * @param {Object} meta Metadata configuration options
9818  * @param {Object} recordType Either an Array of field definition objects,
9819  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
9820  */
9821 Roo.data.JsonReader = function(meta, recordType){
9822     
9823     meta = meta || {};
9824     // set some defaults:
9825     Roo.applyIf(meta, {
9826         totalProperty: 'total',
9827         successProperty : 'success',
9828         root : 'data',
9829         id : 'id'
9830     });
9831     
9832     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
9833 };
9834 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
9835     
9836     /**
9837      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
9838      * Used by Store query builder to append _requestMeta to params.
9839      * 
9840      */
9841     metaFromRemote : false,
9842     /**
9843      * This method is only used by a DataProxy which has retrieved data from a remote server.
9844      * @param {Object} response The XHR object which contains the JSON data in its responseText.
9845      * @return {Object} data A data block which is used by an Roo.data.Store object as
9846      * a cache of Roo.data.Records.
9847      */
9848     read : function(response){
9849         var json = response.responseText;
9850        
9851         var o = /* eval:var:o */ eval("("+json+")");
9852         if(!o) {
9853             throw {message: "JsonReader.read: Json object not found"};
9854         }
9855         
9856         if(o.metaData){
9857             
9858             delete this.ef;
9859             this.metaFromRemote = true;
9860             this.meta = o.metaData;
9861             this.recordType = Roo.data.Record.create(o.metaData.fields);
9862             this.onMetaChange(this.meta, this.recordType, o);
9863         }
9864         return this.readRecords(o);
9865     },
9866
9867     // private function a store will implement
9868     onMetaChange : function(meta, recordType, o){
9869
9870     },
9871
9872     /**
9873          * @ignore
9874          */
9875     simpleAccess: function(obj, subsc) {
9876         return obj[subsc];
9877     },
9878
9879         /**
9880          * @ignore
9881          */
9882     getJsonAccessor: function(){
9883         var re = /[\[\.]/;
9884         return function(expr) {
9885             try {
9886                 return(re.test(expr))
9887                     ? new Function("obj", "return obj." + expr)
9888                     : function(obj){
9889                         return obj[expr];
9890                     };
9891             } catch(e){}
9892             return Roo.emptyFn;
9893         };
9894     }(),
9895
9896     /**
9897      * Create a data block containing Roo.data.Records from an XML document.
9898      * @param {Object} o An object which contains an Array of row objects in the property specified
9899      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
9900      * which contains the total size of the dataset.
9901      * @return {Object} data A data block which is used by an Roo.data.Store object as
9902      * a cache of Roo.data.Records.
9903      */
9904     readRecords : function(o){
9905         /**
9906          * After any data loads, the raw JSON data is available for further custom processing.
9907          * @type Object
9908          */
9909         this.o = o;
9910         var s = this.meta, Record = this.recordType,
9911             f = Record.prototype.fields, fi = f.items, fl = f.length;
9912
9913 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
9914         if (!this.ef) {
9915             if(s.totalProperty) {
9916                     this.getTotal = this.getJsonAccessor(s.totalProperty);
9917                 }
9918                 if(s.successProperty) {
9919                     this.getSuccess = this.getJsonAccessor(s.successProperty);
9920                 }
9921                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
9922                 if (s.id) {
9923                         var g = this.getJsonAccessor(s.id);
9924                         this.getId = function(rec) {
9925                                 var r = g(rec);
9926                                 return (r === undefined || r === "") ? null : r;
9927                         };
9928                 } else {
9929                         this.getId = function(){return null;};
9930                 }
9931             this.ef = [];
9932             for(var jj = 0; jj < fl; jj++){
9933                 f = fi[jj];
9934                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
9935                 this.ef[jj] = this.getJsonAccessor(map);
9936             }
9937         }
9938
9939         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
9940         if(s.totalProperty){
9941             var vt = parseInt(this.getTotal(o), 10);
9942             if(!isNaN(vt)){
9943                 totalRecords = vt;
9944             }
9945         }
9946         if(s.successProperty){
9947             var vs = this.getSuccess(o);
9948             if(vs === false || vs === 'false'){
9949                 success = false;
9950             }
9951         }
9952         var records = [];
9953             for(var i = 0; i < c; i++){
9954                     var n = root[i];
9955                 var values = {};
9956                 var id = this.getId(n);
9957                 for(var j = 0; j < fl; j++){
9958                     f = fi[j];
9959                 var v = this.ef[j](n);
9960                 if (!f.convert) {
9961                     Roo.log('missing convert for ' + f.name);
9962                     Roo.log(f);
9963                     continue;
9964                 }
9965                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
9966                 }
9967                 var record = new Record(values, id);
9968                 record.json = n;
9969                 records[i] = record;
9970             }
9971             return {
9972             raw : o,
9973                 success : success,
9974                 records : records,
9975                 totalRecords : totalRecords
9976             };
9977     }
9978 });/*
9979  * Based on:
9980  * Ext JS Library 1.1.1
9981  * Copyright(c) 2006-2007, Ext JS, LLC.
9982  *
9983  * Originally Released Under LGPL - original licence link has changed is not relivant.
9984  *
9985  * Fork - LGPL
9986  * <script type="text/javascript">
9987  */
9988
9989 /**
9990  * @class Roo.data.ArrayReader
9991  * @extends Roo.data.DataReader
9992  * Data reader class to create an Array of Roo.data.Record objects from an Array.
9993  * Each element of that Array represents a row of data fields. The
9994  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
9995  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
9996  * <p>
9997  * Example code:.
9998  * <pre><code>
9999 var RecordDef = Roo.data.Record.create([
10000     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
10001     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
10002 ]);
10003 var myReader = new Roo.data.ArrayReader({
10004     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
10005 }, RecordDef);
10006 </code></pre>
10007  * <p>
10008  * This would consume an Array like this:
10009  * <pre><code>
10010 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
10011   </code></pre>
10012  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
10013  * @constructor
10014  * Create a new JsonReader
10015  * @param {Object} meta Metadata configuration options.
10016  * @param {Object} recordType Either an Array of field definition objects
10017  * as specified to {@link Roo.data.Record#create},
10018  * or an {@link Roo.data.Record} object
10019  * created using {@link Roo.data.Record#create}.
10020  */
10021 Roo.data.ArrayReader = function(meta, recordType){
10022     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
10023 };
10024
10025 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
10026     /**
10027      * Create a data block containing Roo.data.Records from an XML document.
10028      * @param {Object} o An Array of row objects which represents the dataset.
10029      * @return {Object} data A data block which is used by an Roo.data.Store object as
10030      * a cache of Roo.data.Records.
10031      */
10032     readRecords : function(o){
10033         var sid = this.meta ? this.meta.id : null;
10034         var recordType = this.recordType, fields = recordType.prototype.fields;
10035         var records = [];
10036         var root = o;
10037             for(var i = 0; i < root.length; i++){
10038                     var n = root[i];
10039                 var values = {};
10040                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
10041                 for(var j = 0, jlen = fields.length; j < jlen; j++){
10042                 var f = fields.items[j];
10043                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
10044                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
10045                 v = f.convert(v);
10046                 values[f.name] = v;
10047             }
10048                 var record = new recordType(values, id);
10049                 record.json = n;
10050                 records[records.length] = record;
10051             }
10052             return {
10053                 records : records,
10054                 totalRecords : records.length
10055             };
10056     }
10057 });/*
10058  * - LGPL
10059  * * 
10060  */
10061
10062 /**
10063  * @class Roo.bootstrap.ComboBox
10064  * @extends Roo.bootstrap.TriggerField
10065  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
10066  * @cfg {Boolean} append (true|false) default false
10067  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
10068  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
10069  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
10070  * @constructor
10071  * Create a new ComboBox.
10072  * @param {Object} config Configuration options
10073  */
10074 Roo.bootstrap.ComboBox = function(config){
10075     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
10076     this.addEvents({
10077         /**
10078          * @event expand
10079          * Fires when the dropdown list is expanded
10080              * @param {Roo.bootstrap.ComboBox} combo This combo box
10081              */
10082         'expand' : true,
10083         /**
10084          * @event collapse
10085          * Fires when the dropdown list is collapsed
10086              * @param {Roo.bootstrap.ComboBox} combo This combo box
10087              */
10088         'collapse' : true,
10089         /**
10090          * @event beforeselect
10091          * Fires before a list item is selected. Return false to cancel the selection.
10092              * @param {Roo.bootstrap.ComboBox} combo This combo box
10093              * @param {Roo.data.Record} record The data record returned from the underlying store
10094              * @param {Number} index The index of the selected item in the dropdown list
10095              */
10096         'beforeselect' : true,
10097         /**
10098          * @event select
10099          * Fires when a list item is selected
10100              * @param {Roo.bootstrap.ComboBox} combo This combo box
10101              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
10102              * @param {Number} index The index of the selected item in the dropdown list
10103              */
10104         'select' : true,
10105         /**
10106          * @event beforequery
10107          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
10108          * The event object passed has these properties:
10109              * @param {Roo.bootstrap.ComboBox} combo This combo box
10110              * @param {String} query The query
10111              * @param {Boolean} forceAll true to force "all" query
10112              * @param {Boolean} cancel true to cancel the query
10113              * @param {Object} e The query event object
10114              */
10115         'beforequery': true,
10116          /**
10117          * @event add
10118          * Fires when the 'add' icon is pressed (add a listener to enable add button)
10119              * @param {Roo.bootstrap.ComboBox} combo This combo box
10120              */
10121         'add' : true,
10122         /**
10123          * @event edit
10124          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
10125              * @param {Roo.bootstrap.ComboBox} combo This combo box
10126              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
10127              */
10128         'edit' : true,
10129         /**
10130          * @event remove
10131          * Fires when the remove value from the combobox array
10132              * @param {Roo.bootstrap.ComboBox} combo This combo box
10133              */
10134         'remove' : true
10135         
10136     });
10137     
10138     this.item = [];
10139     this.tickItems = [];
10140     
10141     this.selectedIndex = -1;
10142     if(this.mode == 'local'){
10143         if(config.queryDelay === undefined){
10144             this.queryDelay = 10;
10145         }
10146         if(config.minChars === undefined){
10147             this.minChars = 0;
10148         }
10149     }
10150 };
10151
10152 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
10153      
10154     /**
10155      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
10156      * rendering into an Roo.Editor, defaults to false)
10157      */
10158     /**
10159      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
10160      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
10161      */
10162     /**
10163      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
10164      */
10165     /**
10166      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
10167      * the dropdown list (defaults to undefined, with no header element)
10168      */
10169
10170      /**
10171      * @cfg {String/Roo.Template} tpl The template to use to render the output
10172      */
10173      
10174      /**
10175      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
10176      */
10177     listWidth: undefined,
10178     /**
10179      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
10180      * mode = 'remote' or 'text' if mode = 'local')
10181      */
10182     displayField: undefined,
10183     /**
10184      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
10185      * mode = 'remote' or 'value' if mode = 'local'). 
10186      * Note: use of a valueField requires the user make a selection
10187      * in order for a value to be mapped.
10188      */
10189     valueField: undefined,
10190     
10191     
10192     /**
10193      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
10194      * field's data value (defaults to the underlying DOM element's name)
10195      */
10196     hiddenName: undefined,
10197     /**
10198      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
10199      */
10200     listClass: '',
10201     /**
10202      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
10203      */
10204     selectedClass: 'active',
10205     
10206     /**
10207      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
10208      */
10209     shadow:'sides',
10210     /**
10211      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
10212      * anchor positions (defaults to 'tl-bl')
10213      */
10214     listAlign: 'tl-bl?',
10215     /**
10216      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
10217      */
10218     maxHeight: 300,
10219     /**
10220      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
10221      * query specified by the allQuery config option (defaults to 'query')
10222      */
10223     triggerAction: 'query',
10224     /**
10225      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
10226      * (defaults to 4, does not apply if editable = false)
10227      */
10228     minChars : 4,
10229     /**
10230      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
10231      * delay (typeAheadDelay) if it matches a known value (defaults to false)
10232      */
10233     typeAhead: false,
10234     /**
10235      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
10236      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
10237      */
10238     queryDelay: 500,
10239     /**
10240      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
10241      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
10242      */
10243     pageSize: 0,
10244     /**
10245      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
10246      * when editable = true (defaults to false)
10247      */
10248     selectOnFocus:false,
10249     /**
10250      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
10251      */
10252     queryParam: 'query',
10253     /**
10254      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
10255      * when mode = 'remote' (defaults to 'Loading...')
10256      */
10257     loadingText: 'Loading...',
10258     /**
10259      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
10260      */
10261     resizable: false,
10262     /**
10263      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
10264      */
10265     handleHeight : 8,
10266     /**
10267      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
10268      * traditional select (defaults to true)
10269      */
10270     editable: true,
10271     /**
10272      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
10273      */
10274     allQuery: '',
10275     /**
10276      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
10277      */
10278     mode: 'remote',
10279     /**
10280      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
10281      * listWidth has a higher value)
10282      */
10283     minListWidth : 70,
10284     /**
10285      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
10286      * allow the user to set arbitrary text into the field (defaults to false)
10287      */
10288     forceSelection:false,
10289     /**
10290      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
10291      * if typeAhead = true (defaults to 250)
10292      */
10293     typeAheadDelay : 250,
10294     /**
10295      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
10296      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
10297      */
10298     valueNotFoundText : undefined,
10299     /**
10300      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
10301      */
10302     blockFocus : false,
10303     
10304     /**
10305      * @cfg {Boolean} disableClear Disable showing of clear button.
10306      */
10307     disableClear : false,
10308     /**
10309      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
10310      */
10311     alwaysQuery : false,
10312     
10313     /**
10314      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
10315      */
10316     multiple : false,
10317     
10318     //private
10319     addicon : false,
10320     editicon: false,
10321     
10322     page: 0,
10323     hasQuery: false,
10324     append: false,
10325     loadNext: false,
10326     autoFocus : true,
10327     tickable : false,
10328     btnPosition : 'right',
10329     
10330     // element that contains real text value.. (when hidden is used..)
10331     
10332     getAutoCreate : function()
10333     {
10334         var cfg = false;
10335         
10336         /*
10337          *  Normal ComboBox
10338          */
10339         if(!this.tickable){
10340             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
10341             return cfg;
10342         }
10343         
10344         /*
10345          *  ComboBox with tickable selections
10346          */
10347              
10348         var align = this.labelAlign || this.parentLabelAlign();
10349         
10350         cfg = {
10351             cls : 'form-group roo-combobox-tickable' //input-group
10352         };
10353         
10354         
10355         var buttons = {
10356             tag : 'div',
10357             cls : 'tickable-buttons',
10358             cn : [
10359                 {
10360                     tag : 'button',
10361                     type : 'button',
10362                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
10363                     html : 'Edit'
10364                 },
10365                 {
10366                     tag : 'button',
10367                     type : 'button',
10368                     name : 'ok',
10369                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
10370                     html : 'Done'
10371                 },
10372                 {
10373                     tag : 'button',
10374                     type : 'button',
10375                     name : 'cancel',
10376                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
10377                     html : 'Cancel'
10378                 }
10379             ]
10380         };
10381         
10382         var _this = this;
10383         Roo.each(buttons.cn, function(c){
10384             if (_this.size) {
10385                 c.cls += ' btn-' + _this.size;
10386             }
10387
10388             if (_this.disabled) {
10389                 c.disabled = true;
10390             }
10391         });
10392         
10393         var box = {
10394             tag: 'div',
10395             cn: [
10396                 {
10397                     tag: 'input',
10398                     type : 'hidden',
10399                     cls: 'form-hidden-field'
10400                 },
10401                 {
10402                     tag: 'ul',
10403                     cls: 'select2-choices',
10404                     cn:[
10405                         {
10406                             tag: 'li',
10407                             cls: 'select2-search-field',
10408                             cn: [
10409
10410                                 buttons
10411                             ]
10412                         }
10413                     ]
10414                 }
10415             ]
10416         }
10417         
10418         var combobox = {
10419             cls: 'select2-container input-group select2-container-multi',
10420             cn: [
10421                 box,
10422                 {
10423                     tag: 'ul',
10424                     cls: 'typeahead typeahead-long dropdown-menu',
10425                     style: 'display:none; max-height:' + this.maxHeight + 'px;'
10426                 }
10427             ]
10428         };
10429         
10430         if (align ==='left' && this.fieldLabel.length) {
10431             
10432                 Roo.log("left and has label");
10433                 cfg.cn = [
10434                     
10435                     {
10436                         tag: 'label',
10437                         'for' :  id,
10438                         cls : 'control-label col-sm-' + this.labelWidth,
10439                         html : this.fieldLabel
10440                         
10441                     },
10442                     {
10443                         cls : "col-sm-" + (12 - this.labelWidth), 
10444                         cn: [
10445                             combobox
10446                         ]
10447                     }
10448                     
10449                 ];
10450         } else if ( this.fieldLabel.length) {
10451                 Roo.log(" label");
10452                  cfg.cn = [
10453                    
10454                     {
10455                         tag: 'label',
10456                         //cls : 'input-group-addon',
10457                         html : this.fieldLabel
10458                         
10459                     },
10460                     
10461                     combobox
10462                     
10463                 ];
10464
10465         } else {
10466             
10467                 Roo.log(" no label && no align");
10468                 cfg = combobox
10469                      
10470                 
10471         }
10472          
10473         var settings=this;
10474         ['xs','sm','md','lg'].map(function(size){
10475             if (settings[size]) {
10476                 cfg.cls += ' col-' + size + '-' + settings[size];
10477             }
10478         });
10479         
10480         return cfg;
10481         
10482     },
10483     
10484     // private
10485     initEvents: function()
10486     {
10487         
10488         if (!this.store) {
10489             throw "can not find store for combo";
10490         }
10491         this.store = Roo.factory(this.store, Roo.data);
10492         
10493         if(this.tickable){
10494             this.initTickableEvnets();
10495             return;
10496         }
10497         
10498         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
10499         
10500         
10501         if(this.hiddenName){
10502             
10503             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10504             
10505             this.hiddenField.dom.value =
10506                 this.hiddenValue !== undefined ? this.hiddenValue :
10507                 this.value !== undefined ? this.value : '';
10508
10509             // prevent input submission
10510             this.el.dom.removeAttribute('name');
10511             this.hiddenField.dom.setAttribute('name', this.hiddenName);
10512              
10513              
10514         }
10515         //if(Roo.isGecko){
10516         //    this.el.dom.setAttribute('autocomplete', 'off');
10517         //}
10518
10519         var cls = 'x-combo-list';
10520         this.list = this.el.select('ul.dropdown-menu',true).first();
10521
10522         //this.list = new Roo.Layer({
10523         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
10524         //});
10525         
10526         var _this = this;
10527         
10528         (function(){
10529             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
10530             _this.list.setWidth(lw);
10531         }).defer(100);
10532         
10533         this.list.on('mouseover', this.onViewOver, this);
10534         this.list.on('mousemove', this.onViewMove, this);
10535         
10536         this.list.on('scroll', this.onViewScroll, this);
10537         
10538         /*
10539         this.list.swallowEvent('mousewheel');
10540         this.assetHeight = 0;
10541
10542         if(this.title){
10543             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
10544             this.assetHeight += this.header.getHeight();
10545         }
10546
10547         this.innerList = this.list.createChild({cls:cls+'-inner'});
10548         this.innerList.on('mouseover', this.onViewOver, this);
10549         this.innerList.on('mousemove', this.onViewMove, this);
10550         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
10551         
10552         if(this.allowBlank && !this.pageSize && !this.disableClear){
10553             this.footer = this.list.createChild({cls:cls+'-ft'});
10554             this.pageTb = new Roo.Toolbar(this.footer);
10555            
10556         }
10557         if(this.pageSize){
10558             this.footer = this.list.createChild({cls:cls+'-ft'});
10559             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
10560                     {pageSize: this.pageSize});
10561             
10562         }
10563         
10564         if (this.pageTb && this.allowBlank && !this.disableClear) {
10565             var _this = this;
10566             this.pageTb.add(new Roo.Toolbar.Fill(), {
10567                 cls: 'x-btn-icon x-btn-clear',
10568                 text: '&#160;',
10569                 handler: function()
10570                 {
10571                     _this.collapse();
10572                     _this.clearValue();
10573                     _this.onSelect(false, -1);
10574                 }
10575             });
10576         }
10577         if (this.footer) {
10578             this.assetHeight += this.footer.getHeight();
10579         }
10580         */
10581             
10582         if(!this.tpl){
10583             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
10584         }
10585
10586         this.view = new Roo.View(this.el.select('ul.dropdown-menu',true).first(), this.tpl, {
10587             singleSelect:true, store: this.store, selectedClass: this.selectedClass
10588         });
10589         //this.view.wrapEl.setDisplayed(false);
10590         this.view.on('click', this.onViewClick, this);
10591         
10592         
10593         
10594         this.store.on('beforeload', this.onBeforeLoad, this);
10595         this.store.on('load', this.onLoad, this);
10596         this.store.on('loadexception', this.onLoadException, this);
10597         /*
10598         if(this.resizable){
10599             this.resizer = new Roo.Resizable(this.list,  {
10600                pinned:true, handles:'se'
10601             });
10602             this.resizer.on('resize', function(r, w, h){
10603                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
10604                 this.listWidth = w;
10605                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
10606                 this.restrictHeight();
10607             }, this);
10608             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
10609         }
10610         */
10611         if(!this.editable){
10612             this.editable = true;
10613             this.setEditable(false);
10614         }
10615         
10616         /*
10617         
10618         if (typeof(this.events.add.listeners) != 'undefined') {
10619             
10620             this.addicon = this.wrap.createChild(
10621                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
10622        
10623             this.addicon.on('click', function(e) {
10624                 this.fireEvent('add', this);
10625             }, this);
10626         }
10627         if (typeof(this.events.edit.listeners) != 'undefined') {
10628             
10629             this.editicon = this.wrap.createChild(
10630                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
10631             if (this.addicon) {
10632                 this.editicon.setStyle('margin-left', '40px');
10633             }
10634             this.editicon.on('click', function(e) {
10635                 
10636                 // we fire even  if inothing is selected..
10637                 this.fireEvent('edit', this, this.lastData );
10638                 
10639             }, this);
10640         }
10641         */
10642         
10643         this.keyNav = new Roo.KeyNav(this.inputEl(), {
10644             "up" : function(e){
10645                 this.inKeyMode = true;
10646                 this.selectPrev();
10647             },
10648
10649             "down" : function(e){
10650                 if(!this.isExpanded()){
10651                     this.onTriggerClick();
10652                 }else{
10653                     this.inKeyMode = true;
10654                     this.selectNext();
10655                 }
10656             },
10657
10658             "enter" : function(e){
10659 //                this.onViewClick();
10660                 //return true;
10661                 this.collapse();
10662                 
10663                 if(this.fireEvent("specialkey", this, e)){
10664                     this.onViewClick(false);
10665                 }
10666                 
10667                 return true;
10668             },
10669
10670             "esc" : function(e){
10671                 this.collapse();
10672             },
10673
10674             "tab" : function(e){
10675                 this.collapse();
10676                 
10677                 if(this.fireEvent("specialkey", this, e)){
10678                     this.onViewClick(false);
10679                 }
10680                 
10681                 return true;
10682             },
10683
10684             scope : this,
10685
10686             doRelay : function(foo, bar, hname){
10687                 if(hname == 'down' || this.scope.isExpanded()){
10688                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
10689                 }
10690                 return true;
10691             },
10692
10693             forceKeyDown: true
10694         });
10695         
10696         
10697         this.queryDelay = Math.max(this.queryDelay || 10,
10698                 this.mode == 'local' ? 10 : 250);
10699         
10700         
10701         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
10702         
10703         if(this.typeAhead){
10704             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
10705         }
10706         if(this.editable !== false){
10707             this.inputEl().on("keyup", this.onKeyUp, this);
10708         }
10709         if(this.forceSelection){
10710             this.inputEl().on('blur', this.doForce, this);
10711         }
10712         
10713         if(this.multiple){
10714             this.choices = this.el.select('ul.select2-choices', true).first();
10715             this.searchField = this.el.select('ul li.select2-search-field', true).first();
10716         }
10717     },
10718     
10719     initTickableEvnets: function()
10720     {   
10721         if(this.hiddenName){
10722             
10723             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10724             
10725             this.hiddenField.dom.value =
10726                 this.hiddenValue !== undefined ? this.hiddenValue :
10727                 this.value !== undefined ? this.value : '';
10728
10729             // prevent input submission
10730             this.el.dom.removeAttribute('name');
10731             this.hiddenField.dom.setAttribute('name', this.hiddenName);
10732              
10733              
10734         }
10735         
10736         this.list = this.el.select('ul.dropdown-menu',true).first();
10737         
10738         this.choices = this.el.select('ul.select2-choices', true).first();
10739         this.searchField = this.el.select('ul li.select2-search-field', true).first();
10740         
10741         this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
10742          
10743         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
10744         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
10745         
10746         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
10747         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
10748         
10749         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
10750         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
10751         
10752         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
10753         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
10754         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
10755         
10756         this.okBtn.hide();
10757         this.cancelBtn.hide();
10758         
10759         var _this = this;
10760         
10761         (function(){
10762             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
10763             _this.list.setWidth(lw);
10764         }).defer(100);
10765         
10766         this.list.on('mouseover', this.onViewOver, this);
10767         this.list.on('mousemove', this.onViewMove, this);
10768         
10769         this.list.on('scroll', this.onViewScroll, this);
10770         
10771         if(!this.tpl){
10772             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>';
10773         }
10774
10775         this.view = new Roo.View(this.el.select('ul.dropdown-menu',true).first(), this.tpl, {
10776             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
10777         });
10778         
10779         //this.view.wrapEl.setDisplayed(false);
10780         this.view.on('click', this.onViewClick, this);
10781         
10782         
10783         
10784         this.store.on('beforeload', this.onBeforeLoad, this);
10785         this.store.on('load', this.onLoad, this);
10786         this.store.on('loadexception', this.onLoadException, this);
10787         
10788 //        this.keyNav = new Roo.KeyNav(this.inputEl(), {
10789 //            "up" : function(e){
10790 //                this.inKeyMode = true;
10791 //                this.selectPrev();
10792 //            },
10793 //
10794 //            "down" : function(e){
10795 //                if(!this.isExpanded()){
10796 //                    this.onTriggerClick();
10797 //                }else{
10798 //                    this.inKeyMode = true;
10799 //                    this.selectNext();
10800 //                }
10801 //            },
10802 //
10803 //            "enter" : function(e){
10804 ////                this.onViewClick();
10805 //                //return true;
10806 //                this.collapse();
10807 //                
10808 //                if(this.fireEvent("specialkey", this, e)){
10809 //                    this.onViewClick(false);
10810 //                }
10811 //                
10812 //                return true;
10813 //            },
10814 //
10815 //            "esc" : function(e){
10816 //                this.collapse();
10817 //            },
10818 //
10819 //            "tab" : function(e){
10820 //                this.collapse();
10821 //                
10822 //                if(this.fireEvent("specialkey", this, e)){
10823 //                    this.onViewClick(false);
10824 //                }
10825 //                
10826 //                return true;
10827 //            },
10828 //
10829 //            scope : this,
10830 //
10831 //            doRelay : function(foo, bar, hname){
10832 //                if(hname == 'down' || this.scope.isExpanded()){
10833 //                   return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
10834 //                }
10835 //                return true;
10836 //            },
10837 //
10838 //            forceKeyDown: true
10839 //        });
10840         
10841         
10842         this.queryDelay = Math.max(this.queryDelay || 10,
10843                 this.mode == 'local' ? 10 : 250);
10844         
10845         
10846         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
10847         
10848         if(this.typeAhead){
10849             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
10850         }
10851     },
10852
10853     onDestroy : function(){
10854         if(this.view){
10855             this.view.setStore(null);
10856             this.view.el.removeAllListeners();
10857             this.view.el.remove();
10858             this.view.purgeListeners();
10859         }
10860         if(this.list){
10861             this.list.dom.innerHTML  = '';
10862         }
10863         
10864         if(this.store){
10865             this.store.un('beforeload', this.onBeforeLoad, this);
10866             this.store.un('load', this.onLoad, this);
10867             this.store.un('loadexception', this.onLoadException, this);
10868         }
10869         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
10870     },
10871
10872     // private
10873     fireKey : function(e){
10874         if(e.isNavKeyPress() && !this.list.isVisible()){
10875             this.fireEvent("specialkey", this, e);
10876         }
10877     },
10878
10879     // private
10880     onResize: function(w, h){
10881 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
10882 //        
10883 //        if(typeof w != 'number'){
10884 //            // we do not handle it!?!?
10885 //            return;
10886 //        }
10887 //        var tw = this.trigger.getWidth();
10888 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
10889 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
10890 //        var x = w - tw;
10891 //        this.inputEl().setWidth( this.adjustWidth('input', x));
10892 //            
10893 //        //this.trigger.setStyle('left', x+'px');
10894 //        
10895 //        if(this.list && this.listWidth === undefined){
10896 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
10897 //            this.list.setWidth(lw);
10898 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
10899 //        }
10900         
10901     
10902         
10903     },
10904
10905     /**
10906      * Allow or prevent the user from directly editing the field text.  If false is passed,
10907      * the user will only be able to select from the items defined in the dropdown list.  This method
10908      * is the runtime equivalent of setting the 'editable' config option at config time.
10909      * @param {Boolean} value True to allow the user to directly edit the field text
10910      */
10911     setEditable : function(value){
10912         if(value == this.editable){
10913             return;
10914         }
10915         this.editable = value;
10916         if(!value){
10917             this.inputEl().dom.setAttribute('readOnly', true);
10918             this.inputEl().on('mousedown', this.onTriggerClick,  this);
10919             this.inputEl().addClass('x-combo-noedit');
10920         }else{
10921             this.inputEl().dom.setAttribute('readOnly', false);
10922             this.inputEl().un('mousedown', this.onTriggerClick,  this);
10923             this.inputEl().removeClass('x-combo-noedit');
10924         }
10925     },
10926
10927     // private
10928     
10929     onBeforeLoad : function(combo,opts){
10930         if(!this.hasFocus){
10931             return;
10932         }
10933          if (!opts.add) {
10934             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
10935          }
10936         this.restrictHeight();
10937         this.selectedIndex = -1;
10938     },
10939
10940     // private
10941     onLoad : function(){
10942         
10943         this.hasQuery = false;
10944         
10945         if(!this.hasFocus){
10946             return;
10947         }
10948         
10949         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
10950             this.loading.hide();
10951         }
10952         
10953         if(this.store.getCount() > 0){
10954             this.expand();
10955             this.restrictHeight();
10956             if(this.lastQuery == this.allQuery){
10957                 if(this.editable && !this.tickable){
10958                     this.inputEl().dom.select();
10959                 }
10960                 if(!this.selectByValue(this.value, true) && this.autoFocus){
10961                     this.select(0, true);
10962                 }
10963             }else{
10964                 if(this.autoFocus){
10965                     this.selectNext();
10966                 }
10967                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
10968                     this.taTask.delay(this.typeAheadDelay);
10969                 }
10970             }
10971         }else{
10972             this.onEmptyResults();
10973         }
10974         
10975         //this.el.focus();
10976     },
10977     // private
10978     onLoadException : function()
10979     {
10980         this.hasQuery = false;
10981         
10982         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
10983             this.loading.hide();
10984         }
10985         
10986         this.collapse();
10987         Roo.log(this.store.reader.jsonData);
10988         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
10989             // fixme
10990             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
10991         }
10992         
10993         
10994     },
10995     // private
10996     onTypeAhead : function(){
10997         if(this.store.getCount() > 0){
10998             var r = this.store.getAt(0);
10999             var newValue = r.data[this.displayField];
11000             var len = newValue.length;
11001             var selStart = this.getRawValue().length;
11002             
11003             if(selStart != len){
11004                 this.setRawValue(newValue);
11005                 this.selectText(selStart, newValue.length);
11006             }
11007         }
11008     },
11009
11010     // private
11011     onSelect : function(record, index){
11012         
11013         if(this.fireEvent('beforeselect', this, record, index) !== false){
11014         
11015             this.setFromData(index > -1 ? record.data : false);
11016             
11017             this.collapse();
11018             this.fireEvent('select', this, record, index);
11019         }
11020     },
11021
11022     /**
11023      * Returns the currently selected field value or empty string if no value is set.
11024      * @return {String} value The selected value
11025      */
11026     getValue : function(){
11027         
11028         if(this.multiple){
11029             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
11030         }
11031         
11032         if(this.valueField){
11033             return typeof this.value != 'undefined' ? this.value : '';
11034         }else{
11035             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
11036         }
11037     },
11038
11039     /**
11040      * Clears any text/value currently set in the field
11041      */
11042     clearValue : function(){
11043         if(this.hiddenField){
11044             this.hiddenField.dom.value = '';
11045         }
11046         this.value = '';
11047         this.setRawValue('');
11048         this.lastSelectionText = '';
11049         
11050     },
11051
11052     /**
11053      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
11054      * will be displayed in the field.  If the value does not match the data value of an existing item,
11055      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
11056      * Otherwise the field will be blank (although the value will still be set).
11057      * @param {String} value The value to match
11058      */
11059     setValue : function(v){
11060         if(this.multiple){
11061             this.syncValue();
11062             return;
11063         }
11064         
11065         var text = v;
11066         if(this.valueField){
11067             var r = this.findRecord(this.valueField, v);
11068             if(r){
11069                 text = r.data[this.displayField];
11070             }else if(this.valueNotFoundText !== undefined){
11071                 text = this.valueNotFoundText;
11072             }
11073         }
11074         this.lastSelectionText = text;
11075         if(this.hiddenField){
11076             this.hiddenField.dom.value = v;
11077         }
11078         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
11079         this.value = v;
11080     },
11081     /**
11082      * @property {Object} the last set data for the element
11083      */
11084     
11085     lastData : false,
11086     /**
11087      * Sets the value of the field based on a object which is related to the record format for the store.
11088      * @param {Object} value the value to set as. or false on reset?
11089      */
11090     setFromData : function(o){
11091         
11092         if(this.multiple){
11093             this.addItem(o);
11094             return;
11095         }
11096             
11097         var dv = ''; // display value
11098         var vv = ''; // value value..
11099         this.lastData = o;
11100         if (this.displayField) {
11101             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11102         } else {
11103             // this is an error condition!!!
11104             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
11105         }
11106         
11107         if(this.valueField){
11108             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
11109         }
11110         
11111         if(this.hiddenField){
11112             this.hiddenField.dom.value = vv;
11113             
11114             this.lastSelectionText = dv;
11115             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11116             this.value = vv;
11117             return;
11118         }
11119         // no hidden field.. - we store the value in 'value', but still display
11120         // display field!!!!
11121         this.lastSelectionText = dv;
11122         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11123         this.value = vv;
11124         
11125         
11126     },
11127     // private
11128     reset : function(){
11129         // overridden so that last data is reset..
11130         this.setValue(this.originalValue);
11131         this.clearInvalid();
11132         this.lastData = false;
11133         if (this.view) {
11134             this.view.clearSelections();
11135         }
11136     },
11137     // private
11138     findRecord : function(prop, value){
11139         var record;
11140         if(this.store.getCount() > 0){
11141             this.store.each(function(r){
11142                 if(r.data[prop] == value){
11143                     record = r;
11144                     return false;
11145                 }
11146                 return true;
11147             });
11148         }
11149         return record;
11150     },
11151     
11152     getName: function()
11153     {
11154         // returns hidden if it's set..
11155         if (!this.rendered) {return ''};
11156         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
11157         
11158     },
11159     // private
11160     onViewMove : function(e, t){
11161         this.inKeyMode = false;
11162     },
11163
11164     // private
11165     onViewOver : function(e, t){
11166         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
11167             return;
11168         }
11169         var item = this.view.findItemFromChild(t);
11170         
11171         if(item){
11172             var index = this.view.indexOf(item);
11173             this.select(index, false);
11174         }
11175     },
11176
11177     // private
11178     onViewClick : function(view, doFocus, el, e)
11179     {
11180         var index = this.view.getSelectedIndexes()[0];
11181         
11182         var r = this.store.getAt(index);
11183         
11184         if(this.tickable){
11185             
11186             if(e.getTarget().nodeName.toLowerCase() != 'input'){
11187                 return;
11188             }
11189             
11190             var rm = false;
11191             var _this = this;
11192             
11193             Roo.each(this.tickItems, function(v,k){
11194                 
11195                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
11196                     _this.tickItems.splice(k, 1);
11197                     rm = true;
11198                     return;
11199                 }
11200             })
11201             
11202             if(rm){
11203                 return;
11204             }
11205             
11206             this.tickItems.push(r.data);
11207             return;
11208         }
11209         
11210         if(r){
11211             this.onSelect(r, index);
11212         }
11213         if(doFocus !== false && !this.blockFocus){
11214             this.inputEl().focus();
11215         }
11216     },
11217
11218     // private
11219     restrictHeight : function(){
11220         //this.innerList.dom.style.height = '';
11221         //var inner = this.innerList.dom;
11222         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
11223         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
11224         //this.list.beginUpdate();
11225         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
11226         this.list.alignTo(this.inputEl(), this.listAlign);
11227         //this.list.endUpdate();
11228     },
11229
11230     // private
11231     onEmptyResults : function(){
11232         this.collapse();
11233     },
11234
11235     /**
11236      * Returns true if the dropdown list is expanded, else false.
11237      */
11238     isExpanded : function(){
11239         return this.list.isVisible();
11240     },
11241
11242     /**
11243      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
11244      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11245      * @param {String} value The data value of the item to select
11246      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11247      * selected item if it is not currently in view (defaults to true)
11248      * @return {Boolean} True if the value matched an item in the list, else false
11249      */
11250     selectByValue : function(v, scrollIntoView){
11251         if(v !== undefined && v !== null){
11252             var r = this.findRecord(this.valueField || this.displayField, v);
11253             if(r){
11254                 this.select(this.store.indexOf(r), scrollIntoView);
11255                 return true;
11256             }
11257         }
11258         return false;
11259     },
11260
11261     /**
11262      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
11263      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11264      * @param {Number} index The zero-based index of the list item to select
11265      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11266      * selected item if it is not currently in view (defaults to true)
11267      */
11268     select : function(index, scrollIntoView){
11269         this.selectedIndex = index;
11270         this.view.select(index);
11271         if(scrollIntoView !== false){
11272             var el = this.view.getNode(index);
11273             if(el){
11274                 //this.innerList.scrollChildIntoView(el, false);
11275                 
11276             }
11277         }
11278     },
11279
11280     // private
11281     selectNext : function(){
11282         var ct = this.store.getCount();
11283         if(ct > 0){
11284             if(this.selectedIndex == -1){
11285                 this.select(0);
11286             }else if(this.selectedIndex < ct-1){
11287                 this.select(this.selectedIndex+1);
11288             }
11289         }
11290     },
11291
11292     // private
11293     selectPrev : function(){
11294         var ct = this.store.getCount();
11295         if(ct > 0){
11296             if(this.selectedIndex == -1){
11297                 this.select(0);
11298             }else if(this.selectedIndex != 0){
11299                 this.select(this.selectedIndex-1);
11300             }
11301         }
11302     },
11303
11304     // private
11305     onKeyUp : function(e){
11306         if(this.editable !== false && !e.isSpecialKey()){
11307             this.lastKey = e.getKey();
11308             this.dqTask.delay(this.queryDelay);
11309         }
11310     },
11311
11312     // private
11313     validateBlur : function(){
11314         return !this.list || !this.list.isVisible();   
11315     },
11316
11317     // private
11318     initQuery : function(){
11319         this.doQuery(this.getRawValue());
11320     },
11321
11322     // private
11323     doForce : function(){
11324         if(this.inputEl().dom.value.length > 0){
11325             this.inputEl().dom.value =
11326                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
11327              
11328         }
11329     },
11330
11331     /**
11332      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
11333      * query allowing the query action to be canceled if needed.
11334      * @param {String} query The SQL query to execute
11335      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
11336      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
11337      * saved in the current store (defaults to false)
11338      */
11339     doQuery : function(q, forceAll){
11340         
11341         if(q === undefined || q === null){
11342             q = '';
11343         }
11344         var qe = {
11345             query: q,
11346             forceAll: forceAll,
11347             combo: this,
11348             cancel:false
11349         };
11350         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
11351             return false;
11352         }
11353         q = qe.query;
11354         
11355         forceAll = qe.forceAll;
11356         if(forceAll === true || (q.length >= this.minChars)){
11357             
11358             this.hasQuery = true;
11359             
11360             if(this.lastQuery != q || this.alwaysQuery){
11361                 this.lastQuery = q;
11362                 if(this.mode == 'local'){
11363                     this.selectedIndex = -1;
11364                     if(forceAll){
11365                         this.store.clearFilter();
11366                     }else{
11367                         this.store.filter(this.displayField, q);
11368                     }
11369                     this.onLoad();
11370                 }else{
11371                     this.store.baseParams[this.queryParam] = q;
11372                     
11373                     var options = {params : this.getParams(q)};
11374                     
11375                     if(this.loadNext){
11376                         options.add = true;
11377                         options.params.start = this.page * this.pageSize;
11378                     }
11379                     
11380                     this.store.load(options);
11381                     /*
11382                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
11383                      *  we should expand the list on onLoad
11384                      *  so command out it
11385                      */
11386 //                    this.expand();
11387                 }
11388             }else{
11389                 this.selectedIndex = -1;
11390                 this.onLoad();   
11391             }
11392         }
11393         
11394         this.loadNext = false;
11395     },
11396
11397     // private
11398     getParams : function(q){
11399         var p = {};
11400         //p[this.queryParam] = q;
11401         
11402         if(this.pageSize){
11403             p.start = 0;
11404             p.limit = this.pageSize;
11405         }
11406         return p;
11407     },
11408
11409     /**
11410      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
11411      */
11412     collapse : function(){
11413         if(!this.isExpanded()){
11414             return;
11415         }
11416         
11417         this.hasFocus = false;
11418         
11419         this.list.hide();
11420         
11421         if(this.tickable){
11422             this.okBtn.hide();
11423             this.cancelBtn.hide();
11424             this.trigger.show();
11425         }
11426         
11427         Roo.get(document).un('mousedown', this.collapseIf, this);
11428         Roo.get(document).un('mousewheel', this.collapseIf, this);
11429         if (!this.editable) {
11430             Roo.get(document).un('keydown', this.listKeyPress, this);
11431         }
11432         this.fireEvent('collapse', this);
11433     },
11434
11435     // private
11436     collapseIf : function(e){
11437         var in_combo  = e.within(this.el);
11438         var in_list =  e.within(this.list);
11439         
11440         if (in_combo || in_list) {
11441             //e.stopPropagation();
11442             return;
11443         }
11444         
11445         if(this.tickable){
11446             this.onTickableFooterButtonClick(e, false, false);
11447         }
11448
11449         this.collapse();
11450         
11451     },
11452
11453     /**
11454      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
11455      */
11456     expand : function(){
11457        
11458         if(this.isExpanded() || !this.hasFocus){
11459             return;
11460         }
11461          Roo.log('expand');
11462         this.list.alignTo(this.inputEl(), this.listAlign);
11463         this.list.show();
11464         
11465         if(this.tickable){
11466             
11467             this.tickItems = Roo.apply([], this.item);
11468             
11469             this.okBtn.show();
11470             this.cancelBtn.show();
11471             this.trigger.hide();
11472             
11473         }
11474         
11475         Roo.get(document).on('mousedown', this.collapseIf, this);
11476         Roo.get(document).on('mousewheel', this.collapseIf, this);
11477         if (!this.editable) {
11478             Roo.get(document).on('keydown', this.listKeyPress, this);
11479         }
11480         
11481         this.fireEvent('expand', this);
11482     },
11483
11484     // private
11485     // Implements the default empty TriggerField.onTriggerClick function
11486     onTriggerClick : function(e)
11487     {
11488         Roo.log('trigger click');
11489         
11490         if(this.disabled){
11491             return;
11492         }
11493         
11494         this.page = 0;
11495         this.loadNext = false;
11496         
11497         if(this.isExpanded()){
11498             this.collapse();
11499             if (!this.blockFocus) {
11500                 this.inputEl().focus();
11501             }
11502             
11503         }else {
11504             this.hasFocus = true;
11505             if(this.triggerAction == 'all') {
11506                 this.doQuery(this.allQuery, true);
11507             } else {
11508                 this.doQuery(this.getRawValue());
11509             }
11510             if (!this.blockFocus) {
11511                 this.inputEl().focus();
11512             }
11513         }
11514     },
11515     
11516     onTickableTriggerClick : function(e)
11517     {
11518         if(this.disabled){
11519             return;
11520         }
11521         
11522         this.page = 0;
11523         this.loadNext = false;
11524         this.hasFocus = true;
11525         
11526         if(this.triggerAction == 'all') {
11527             this.doQuery(this.allQuery, true);
11528         } else {
11529             this.doQuery(this.getRawValue());
11530         }
11531     },
11532     
11533     onSearchFieldClick : function(e)
11534     {
11535         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
11536             return;
11537         }
11538         
11539         this.page = 0;
11540         this.loadNext = false;
11541         this.hasFocus = true;
11542         
11543         if(this.triggerAction == 'all') {
11544             this.doQuery(this.allQuery, true);
11545         } else {
11546             this.doQuery(this.getRawValue());
11547         }
11548     },
11549     
11550     listKeyPress : function(e)
11551     {
11552         //Roo.log('listkeypress');
11553         // scroll to first matching element based on key pres..
11554         if (e.isSpecialKey()) {
11555             return false;
11556         }
11557         var k = String.fromCharCode(e.getKey()).toUpperCase();
11558         //Roo.log(k);
11559         var match  = false;
11560         var csel = this.view.getSelectedNodes();
11561         var cselitem = false;
11562         if (csel.length) {
11563             var ix = this.view.indexOf(csel[0]);
11564             cselitem  = this.store.getAt(ix);
11565             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
11566                 cselitem = false;
11567             }
11568             
11569         }
11570         
11571         this.store.each(function(v) { 
11572             if (cselitem) {
11573                 // start at existing selection.
11574                 if (cselitem.id == v.id) {
11575                     cselitem = false;
11576                 }
11577                 return true;
11578             }
11579                 
11580             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
11581                 match = this.store.indexOf(v);
11582                 return false;
11583             }
11584             return true;
11585         }, this);
11586         
11587         if (match === false) {
11588             return true; // no more action?
11589         }
11590         // scroll to?
11591         this.view.select(match);
11592         var sn = Roo.get(this.view.getSelectedNodes()[0])
11593         //sn.scrollIntoView(sn.dom.parentNode, false);
11594     },
11595     
11596     onViewScroll : function(e, t){
11597         
11598         if(this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
11599             return;
11600         }
11601         
11602         this.hasQuery = true;
11603         
11604         this.loading = this.list.select('.loading', true).first();
11605         
11606         if(this.loading === null){
11607             this.list.createChild({
11608                 tag: 'div',
11609                 cls: 'loading select2-more-results select2-active',
11610                 html: 'Loading more results...'
11611             })
11612             
11613             this.loading = this.list.select('.loading', true).first();
11614             
11615             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
11616             
11617             this.loading.hide();
11618         }
11619         
11620         this.loading.show();
11621         
11622         var _combo = this;
11623         
11624         this.page++;
11625         this.loadNext = true;
11626         
11627         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
11628         
11629         return;
11630     },
11631     
11632     addItem : function(o)
11633     {   
11634         var dv = ''; // display value
11635         
11636         if (this.displayField) {
11637             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11638         } else {
11639             // this is an error condition!!!
11640             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
11641         }
11642         
11643         if(!dv.length){
11644             return;
11645         }
11646         
11647         var choice = this.choices.createChild({
11648             tag: 'li',
11649             cls: 'select2-search-choice',
11650             cn: [
11651                 {
11652                     tag: 'div',
11653                     html: dv
11654                 },
11655                 {
11656                     tag: 'a',
11657                     href: '#',
11658                     cls: 'select2-search-choice-close',
11659                     tabindex: '-1'
11660                 }
11661             ]
11662             
11663         }, this.searchField);
11664         
11665         var close = choice.select('a.select2-search-choice-close', true).first()
11666         
11667         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
11668         
11669         this.item.push(o);
11670         
11671         this.lastData = o;
11672         
11673         this.syncValue();
11674         
11675         this.inputEl().dom.value = '';
11676         
11677     },
11678     
11679     onRemoveItem : function(e, _self, o)
11680     {
11681         e.preventDefault();
11682         var index = this.item.indexOf(o.data) * 1;
11683         
11684         if( index < 0){
11685             Roo.log('not this item?!');
11686             return;
11687         }
11688         
11689         this.item.splice(index, 1);
11690         o.item.remove();
11691         
11692         this.syncValue();
11693         
11694         this.fireEvent('remove', this, e);
11695         
11696     },
11697     
11698     syncValue : function()
11699     {
11700         if(!this.item.length){
11701             this.clearValue();
11702             return;
11703         }
11704             
11705         var value = [];
11706         var _this = this;
11707         Roo.each(this.item, function(i){
11708             if(_this.valueField){
11709                 value.push(i[_this.valueField]);
11710                 return;
11711             }
11712
11713             value.push(i);
11714         });
11715
11716         this.value = value.join(',');
11717
11718         if(this.hiddenField){
11719             this.hiddenField.dom.value = this.value;
11720         }
11721     },
11722     
11723     clearItem : function()
11724     {
11725         if(!this.multiple){
11726             return;
11727         }
11728         
11729         this.item = [];
11730         
11731         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
11732            c.remove();
11733         });
11734         
11735         this.syncValue();
11736     },
11737     
11738     inputEl: function ()
11739     {
11740         if(this.tickable){
11741             return this.searchField;
11742         }
11743         return this.el.select('input.form-control',true).first();
11744     },
11745     
11746     
11747     onTickableFooterButtonClick : function(e, btn, el)
11748     {
11749         e.preventDefault();
11750         
11751         if(btn && btn.name == 'cancel'){
11752             this.tickItems = Roo.apply([], this.item);
11753             this.collapse();
11754             return;
11755         }
11756         
11757         this.clearItem();
11758         
11759         var _this = this;
11760         
11761         Roo.each(this.tickItems, function(o){
11762             _this.addItem(o);
11763         });
11764         
11765         this.collapse();
11766         
11767     }
11768     
11769     
11770
11771     /** 
11772     * @cfg {Boolean} grow 
11773     * @hide 
11774     */
11775     /** 
11776     * @cfg {Number} growMin 
11777     * @hide 
11778     */
11779     /** 
11780     * @cfg {Number} growMax 
11781     * @hide 
11782     */
11783     /**
11784      * @hide
11785      * @method autoSize
11786      */
11787 });
11788 /*
11789  * Based on:
11790  * Ext JS Library 1.1.1
11791  * Copyright(c) 2006-2007, Ext JS, LLC.
11792  *
11793  * Originally Released Under LGPL - original licence link has changed is not relivant.
11794  *
11795  * Fork - LGPL
11796  * <script type="text/javascript">
11797  */
11798
11799 /**
11800  * @class Roo.View
11801  * @extends Roo.util.Observable
11802  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
11803  * This class also supports single and multi selection modes. <br>
11804  * Create a data model bound view:
11805  <pre><code>
11806  var store = new Roo.data.Store(...);
11807
11808  var view = new Roo.View({
11809     el : "my-element",
11810     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
11811  
11812     singleSelect: true,
11813     selectedClass: "ydataview-selected",
11814     store: store
11815  });
11816
11817  // listen for node click?
11818  view.on("click", function(vw, index, node, e){
11819  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
11820  });
11821
11822  // load XML data
11823  dataModel.load("foobar.xml");
11824  </code></pre>
11825  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
11826  * <br><br>
11827  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
11828  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
11829  * 
11830  * Note: old style constructor is still suported (container, template, config)
11831  * 
11832  * @constructor
11833  * Create a new View
11834  * @param {Object} config The config object
11835  * 
11836  */
11837 Roo.View = function(config, depreciated_tpl, depreciated_config){
11838     
11839     this.parent = false;
11840     
11841     if (typeof(depreciated_tpl) == 'undefined') {
11842         // new way.. - universal constructor.
11843         Roo.apply(this, config);
11844         this.el  = Roo.get(this.el);
11845     } else {
11846         // old format..
11847         this.el  = Roo.get(config);
11848         this.tpl = depreciated_tpl;
11849         Roo.apply(this, depreciated_config);
11850     }
11851     this.wrapEl  = this.el.wrap().wrap();
11852     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
11853     
11854     
11855     if(typeof(this.tpl) == "string"){
11856         this.tpl = new Roo.Template(this.tpl);
11857     } else {
11858         // support xtype ctors..
11859         this.tpl = new Roo.factory(this.tpl, Roo);
11860     }
11861     
11862     
11863     this.tpl.compile();
11864     
11865     /** @private */
11866     this.addEvents({
11867         /**
11868          * @event beforeclick
11869          * Fires before a click is processed. Returns false to cancel the default action.
11870          * @param {Roo.View} this
11871          * @param {Number} index The index of the target node
11872          * @param {HTMLElement} node The target node
11873          * @param {Roo.EventObject} e The raw event object
11874          */
11875             "beforeclick" : true,
11876         /**
11877          * @event click
11878          * Fires when a template node is clicked.
11879          * @param {Roo.View} this
11880          * @param {Number} index The index of the target node
11881          * @param {HTMLElement} node The target node
11882          * @param {Roo.EventObject} e The raw event object
11883          */
11884             "click" : true,
11885         /**
11886          * @event dblclick
11887          * Fires when a template node is double clicked.
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             "dblclick" : true,
11894         /**
11895          * @event contextmenu
11896          * Fires when a template node is right 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             "contextmenu" : true,
11903         /**
11904          * @event selectionchange
11905          * Fires when the selected nodes change.
11906          * @param {Roo.View} this
11907          * @param {Array} selections Array of the selected nodes
11908          */
11909             "selectionchange" : true,
11910     
11911         /**
11912          * @event beforeselect
11913          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
11914          * @param {Roo.View} this
11915          * @param {HTMLElement} node The node to be selected
11916          * @param {Array} selections Array of currently selected nodes
11917          */
11918             "beforeselect" : true,
11919         /**
11920          * @event preparedata
11921          * Fires on every row to render, to allow you to change the data.
11922          * @param {Roo.View} this
11923          * @param {Object} data to be rendered (change this)
11924          */
11925           "preparedata" : true
11926           
11927           
11928         });
11929
11930
11931
11932     this.el.on({
11933         "click": this.onClick,
11934         "dblclick": this.onDblClick,
11935         "contextmenu": this.onContextMenu,
11936         scope:this
11937     });
11938
11939     this.selections = [];
11940     this.nodes = [];
11941     this.cmp = new Roo.CompositeElementLite([]);
11942     if(this.store){
11943         this.store = Roo.factory(this.store, Roo.data);
11944         this.setStore(this.store, true);
11945     }
11946     
11947     if ( this.footer && this.footer.xtype) {
11948            
11949          var fctr = this.wrapEl.appendChild(document.createElement("div"));
11950         
11951         this.footer.dataSource = this.store
11952         this.footer.container = fctr;
11953         this.footer = Roo.factory(this.footer, Roo);
11954         fctr.insertFirst(this.el);
11955         
11956         // this is a bit insane - as the paging toolbar seems to detach the el..
11957 //        dom.parentNode.parentNode.parentNode
11958          // they get detached?
11959     }
11960     
11961     
11962     Roo.View.superclass.constructor.call(this);
11963     
11964     
11965 };
11966
11967 Roo.extend(Roo.View, Roo.util.Observable, {
11968     
11969      /**
11970      * @cfg {Roo.data.Store} store Data store to load data from.
11971      */
11972     store : false,
11973     
11974     /**
11975      * @cfg {String|Roo.Element} el The container element.
11976      */
11977     el : '',
11978     
11979     /**
11980      * @cfg {String|Roo.Template} tpl The template used by this View 
11981      */
11982     tpl : false,
11983     /**
11984      * @cfg {String} dataName the named area of the template to use as the data area
11985      *                          Works with domtemplates roo-name="name"
11986      */
11987     dataName: false,
11988     /**
11989      * @cfg {String} selectedClass The css class to add to selected nodes
11990      */
11991     selectedClass : "x-view-selected",
11992      /**
11993      * @cfg {String} emptyText The empty text to show when nothing is loaded.
11994      */
11995     emptyText : "",
11996     
11997     /**
11998      * @cfg {String} text to display on mask (default Loading)
11999      */
12000     mask : false,
12001     /**
12002      * @cfg {Boolean} multiSelect Allow multiple selection
12003      */
12004     multiSelect : false,
12005     /**
12006      * @cfg {Boolean} singleSelect Allow single selection
12007      */
12008     singleSelect:  false,
12009     
12010     /**
12011      * @cfg {Boolean} toggleSelect - selecting 
12012      */
12013     toggleSelect : false,
12014     
12015     /**
12016      * @cfg {Boolean} tickable - selecting 
12017      */
12018     tickable : false,
12019     
12020     /**
12021      * Returns the element this view is bound to.
12022      * @return {Roo.Element}
12023      */
12024     getEl : function(){
12025         return this.wrapEl;
12026     },
12027     
12028     
12029
12030     /**
12031      * Refreshes the view. - called by datachanged on the store. - do not call directly.
12032      */
12033     refresh : function(){
12034         Roo.log('refresh');
12035         var t = this.tpl;
12036         
12037         // if we are using something like 'domtemplate', then
12038         // the what gets used is:
12039         // t.applySubtemplate(NAME, data, wrapping data..)
12040         // the outer template then get' applied with
12041         //     the store 'extra data'
12042         // and the body get's added to the
12043         //      roo-name="data" node?
12044         //      <span class='roo-tpl-{name}'></span> ?????
12045         
12046         
12047         
12048         this.clearSelections();
12049         this.el.update("");
12050         var html = [];
12051         var records = this.store.getRange();
12052         if(records.length < 1) {
12053             
12054             // is this valid??  = should it render a template??
12055             
12056             this.el.update(this.emptyText);
12057             return;
12058         }
12059         var el = this.el;
12060         if (this.dataName) {
12061             this.el.update(t.apply(this.store.meta)); //????
12062             el = this.el.child('.roo-tpl-' + this.dataName);
12063         }
12064         
12065         for(var i = 0, len = records.length; i < len; i++){
12066             var data = this.prepareData(records[i].data, i, records[i]);
12067             this.fireEvent("preparedata", this, data, i, records[i]);
12068             
12069             var d = Roo.apply({}, data);
12070             
12071             if(this.tickable){
12072                 Roo.apply(d, {'roo-id' : Roo.id()});
12073                 
12074                 var _this = this;
12075             
12076                 Roo.each(this.parent.item, function(item){
12077                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
12078                         return;
12079                     }
12080                     Roo.apply(d, {'roo-data-checked' : 'checked'});
12081                 });
12082             }
12083             
12084             html[html.length] = Roo.util.Format.trim(
12085                 this.dataName ?
12086                     t.applySubtemplate(this.dataName, d, this.store.meta) :
12087                     t.apply(d)
12088             );
12089         }
12090         
12091         
12092         
12093         el.update(html.join(""));
12094         this.nodes = el.dom.childNodes;
12095         this.updateIndexes(0);
12096     },
12097     
12098
12099     /**
12100      * Function to override to reformat the data that is sent to
12101      * the template for each node.
12102      * DEPRICATED - use the preparedata event handler.
12103      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
12104      * a JSON object for an UpdateManager bound view).
12105      */
12106     prepareData : function(data, index, record)
12107     {
12108         this.fireEvent("preparedata", this, data, index, record);
12109         return data;
12110     },
12111
12112     onUpdate : function(ds, record){
12113          Roo.log('on update');   
12114         this.clearSelections();
12115         var index = this.store.indexOf(record);
12116         var n = this.nodes[index];
12117         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
12118         n.parentNode.removeChild(n);
12119         this.updateIndexes(index, index);
12120     },
12121
12122     
12123     
12124 // --------- FIXME     
12125     onAdd : function(ds, records, index)
12126     {
12127         Roo.log(['on Add', ds, records, index] );        
12128         this.clearSelections();
12129         if(this.nodes.length == 0){
12130             this.refresh();
12131             return;
12132         }
12133         var n = this.nodes[index];
12134         for(var i = 0, len = records.length; i < len; i++){
12135             var d = this.prepareData(records[i].data, i, records[i]);
12136             if(n){
12137                 this.tpl.insertBefore(n, d);
12138             }else{
12139                 
12140                 this.tpl.append(this.el, d);
12141             }
12142         }
12143         this.updateIndexes(index);
12144     },
12145
12146     onRemove : function(ds, record, index){
12147         Roo.log('onRemove');
12148         this.clearSelections();
12149         var el = this.dataName  ?
12150             this.el.child('.roo-tpl-' + this.dataName) :
12151             this.el; 
12152         
12153         el.dom.removeChild(this.nodes[index]);
12154         this.updateIndexes(index);
12155     },
12156
12157     /**
12158      * Refresh an individual node.
12159      * @param {Number} index
12160      */
12161     refreshNode : function(index){
12162         this.onUpdate(this.store, this.store.getAt(index));
12163     },
12164
12165     updateIndexes : function(startIndex, endIndex){
12166         var ns = this.nodes;
12167         startIndex = startIndex || 0;
12168         endIndex = endIndex || ns.length - 1;
12169         for(var i = startIndex; i <= endIndex; i++){
12170             ns[i].nodeIndex = i;
12171         }
12172     },
12173
12174     /**
12175      * Changes the data store this view uses and refresh the view.
12176      * @param {Store} store
12177      */
12178     setStore : function(store, initial){
12179         if(!initial && this.store){
12180             this.store.un("datachanged", this.refresh);
12181             this.store.un("add", this.onAdd);
12182             this.store.un("remove", this.onRemove);
12183             this.store.un("update", this.onUpdate);
12184             this.store.un("clear", this.refresh);
12185             this.store.un("beforeload", this.onBeforeLoad);
12186             this.store.un("load", this.onLoad);
12187             this.store.un("loadexception", this.onLoad);
12188         }
12189         if(store){
12190           
12191             store.on("datachanged", this.refresh, this);
12192             store.on("add", this.onAdd, this);
12193             store.on("remove", this.onRemove, this);
12194             store.on("update", this.onUpdate, this);
12195             store.on("clear", this.refresh, this);
12196             store.on("beforeload", this.onBeforeLoad, this);
12197             store.on("load", this.onLoad, this);
12198             store.on("loadexception", this.onLoad, this);
12199         }
12200         
12201         if(store){
12202             this.refresh();
12203         }
12204     },
12205     /**
12206      * onbeforeLoad - masks the loading area.
12207      *
12208      */
12209     onBeforeLoad : function(store,opts)
12210     {
12211          Roo.log('onBeforeLoad');   
12212         if (!opts.add) {
12213             this.el.update("");
12214         }
12215         this.el.mask(this.mask ? this.mask : "Loading" ); 
12216     },
12217     onLoad : function ()
12218     {
12219         this.el.unmask();
12220     },
12221     
12222
12223     /**
12224      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
12225      * @param {HTMLElement} node
12226      * @return {HTMLElement} The template node
12227      */
12228     findItemFromChild : function(node){
12229         var el = this.dataName  ?
12230             this.el.child('.roo-tpl-' + this.dataName,true) :
12231             this.el.dom; 
12232         
12233         if(!node || node.parentNode == el){
12234                     return node;
12235             }
12236             var p = node.parentNode;
12237             while(p && p != el){
12238             if(p.parentNode == el){
12239                 return p;
12240             }
12241             p = p.parentNode;
12242         }
12243             return null;
12244     },
12245
12246     /** @ignore */
12247     onClick : function(e){
12248         var item = this.findItemFromChild(e.getTarget());
12249         if(item){
12250             var index = this.indexOf(item);
12251             if(this.onItemClick(item, index, e) !== false){
12252                 this.fireEvent("click", this, index, item, e);
12253             }
12254         }else{
12255             this.clearSelections();
12256         }
12257     },
12258
12259     /** @ignore */
12260     onContextMenu : function(e){
12261         var item = this.findItemFromChild(e.getTarget());
12262         if(item){
12263             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
12264         }
12265     },
12266
12267     /** @ignore */
12268     onDblClick : function(e){
12269         var item = this.findItemFromChild(e.getTarget());
12270         if(item){
12271             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
12272         }
12273     },
12274
12275     onItemClick : function(item, index, e)
12276     {
12277         if(this.fireEvent("beforeclick", this, index, item, e) === false){
12278             return false;
12279         }
12280         if (this.toggleSelect) {
12281             var m = this.isSelected(item) ? 'unselect' : 'select';
12282             Roo.log(m);
12283             var _t = this;
12284             _t[m](item, true, false);
12285             return true;
12286         }
12287         if(this.multiSelect || this.singleSelect){
12288             if(this.multiSelect && e.shiftKey && this.lastSelection){
12289                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
12290             }else{
12291                 this.select(item, this.multiSelect && e.ctrlKey);
12292                 this.lastSelection = item;
12293             }
12294             
12295             if(!this.tickable){
12296                 e.preventDefault();
12297             }
12298             
12299         }
12300         return true;
12301     },
12302
12303     /**
12304      * Get the number of selected nodes.
12305      * @return {Number}
12306      */
12307     getSelectionCount : function(){
12308         return this.selections.length;
12309     },
12310
12311     /**
12312      * Get the currently selected nodes.
12313      * @return {Array} An array of HTMLElements
12314      */
12315     getSelectedNodes : function(){
12316         return this.selections;
12317     },
12318
12319     /**
12320      * Get the indexes of the selected nodes.
12321      * @return {Array}
12322      */
12323     getSelectedIndexes : function(){
12324         var indexes = [], s = this.selections;
12325         for(var i = 0, len = s.length; i < len; i++){
12326             indexes.push(s[i].nodeIndex);
12327         }
12328         return indexes;
12329     },
12330
12331     /**
12332      * Clear all selections
12333      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
12334      */
12335     clearSelections : function(suppressEvent){
12336         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
12337             this.cmp.elements = this.selections;
12338             this.cmp.removeClass(this.selectedClass);
12339             this.selections = [];
12340             if(!suppressEvent){
12341                 this.fireEvent("selectionchange", this, this.selections);
12342             }
12343         }
12344     },
12345
12346     /**
12347      * Returns true if the passed node is selected
12348      * @param {HTMLElement/Number} node The node or node index
12349      * @return {Boolean}
12350      */
12351     isSelected : function(node){
12352         var s = this.selections;
12353         if(s.length < 1){
12354             return false;
12355         }
12356         node = this.getNode(node);
12357         return s.indexOf(node) !== -1;
12358     },
12359
12360     /**
12361      * Selects nodes.
12362      * @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
12363      * @param {Boolean} keepExisting (optional) true to keep existing selections
12364      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12365      */
12366     select : function(nodeInfo, keepExisting, suppressEvent){
12367         if(nodeInfo instanceof Array){
12368             if(!keepExisting){
12369                 this.clearSelections(true);
12370             }
12371             for(var i = 0, len = nodeInfo.length; i < len; i++){
12372                 this.select(nodeInfo[i], true, true);
12373             }
12374             return;
12375         } 
12376         var node = this.getNode(nodeInfo);
12377         if(!node || this.isSelected(node)){
12378             return; // already selected.
12379         }
12380         if(!keepExisting){
12381             this.clearSelections(true);
12382         }
12383         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
12384             Roo.fly(node).addClass(this.selectedClass);
12385             this.selections.push(node);
12386             if(!suppressEvent){
12387                 this.fireEvent("selectionchange", this, this.selections);
12388             }
12389         }
12390         
12391         
12392     },
12393       /**
12394      * Unselects nodes.
12395      * @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
12396      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
12397      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12398      */
12399     unselect : function(nodeInfo, keepExisting, suppressEvent)
12400     {
12401         if(nodeInfo instanceof Array){
12402             Roo.each(this.selections, function(s) {
12403                 this.unselect(s, nodeInfo);
12404             }, this);
12405             return;
12406         }
12407         var node = this.getNode(nodeInfo);
12408         if(!node || !this.isSelected(node)){
12409             Roo.log("not selected");
12410             return; // not selected.
12411         }
12412         // fireevent???
12413         var ns = [];
12414         Roo.each(this.selections, function(s) {
12415             if (s == node ) {
12416                 Roo.fly(node).removeClass(this.selectedClass);
12417
12418                 return;
12419             }
12420             ns.push(s);
12421         },this);
12422         
12423         this.selections= ns;
12424         this.fireEvent("selectionchange", this, this.selections);
12425     },
12426
12427     /**
12428      * Gets a template node.
12429      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12430      * @return {HTMLElement} The node or null if it wasn't found
12431      */
12432     getNode : function(nodeInfo){
12433         if(typeof nodeInfo == "string"){
12434             return document.getElementById(nodeInfo);
12435         }else if(typeof nodeInfo == "number"){
12436             return this.nodes[nodeInfo];
12437         }
12438         return nodeInfo;
12439     },
12440
12441     /**
12442      * Gets a range template nodes.
12443      * @param {Number} startIndex
12444      * @param {Number} endIndex
12445      * @return {Array} An array of nodes
12446      */
12447     getNodes : function(start, end){
12448         var ns = this.nodes;
12449         start = start || 0;
12450         end = typeof end == "undefined" ? ns.length - 1 : end;
12451         var nodes = [];
12452         if(start <= end){
12453             for(var i = start; i <= end; i++){
12454                 nodes.push(ns[i]);
12455             }
12456         } else{
12457             for(var i = start; i >= end; i--){
12458                 nodes.push(ns[i]);
12459             }
12460         }
12461         return nodes;
12462     },
12463
12464     /**
12465      * Finds the index of the passed node
12466      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12467      * @return {Number} The index of the node or -1
12468      */
12469     indexOf : function(node){
12470         node = this.getNode(node);
12471         if(typeof node.nodeIndex == "number"){
12472             return node.nodeIndex;
12473         }
12474         var ns = this.nodes;
12475         for(var i = 0, len = ns.length; i < len; i++){
12476             if(ns[i] == node){
12477                 return i;
12478             }
12479         }
12480         return -1;
12481     }
12482 });
12483 /*
12484  * - LGPL
12485  *
12486  * based on jquery fullcalendar
12487  * 
12488  */
12489
12490 Roo.bootstrap = Roo.bootstrap || {};
12491 /**
12492  * @class Roo.bootstrap.Calendar
12493  * @extends Roo.bootstrap.Component
12494  * Bootstrap Calendar class
12495  * @cfg {Boolean} loadMask (true|false) default false
12496  * @cfg {Object} header generate the user specific header of the calendar, default false
12497
12498  * @constructor
12499  * Create a new Container
12500  * @param {Object} config The config object
12501  */
12502
12503
12504
12505 Roo.bootstrap.Calendar = function(config){
12506     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
12507      this.addEvents({
12508         /**
12509              * @event select
12510              * Fires when a date is selected
12511              * @param {DatePicker} this
12512              * @param {Date} date The selected date
12513              */
12514         'select': true,
12515         /**
12516              * @event monthchange
12517              * Fires when the displayed month changes 
12518              * @param {DatePicker} this
12519              * @param {Date} date The selected month
12520              */
12521         'monthchange': true,
12522         /**
12523              * @event evententer
12524              * Fires when mouse over an event
12525              * @param {Calendar} this
12526              * @param {event} Event
12527              */
12528         'evententer': true,
12529         /**
12530              * @event eventleave
12531              * Fires when the mouse leaves an
12532              * @param {Calendar} this
12533              * @param {event}
12534              */
12535         'eventleave': true,
12536         /**
12537              * @event eventclick
12538              * Fires when the mouse click an
12539              * @param {Calendar} this
12540              * @param {event}
12541              */
12542         'eventclick': true
12543         
12544     });
12545
12546 };
12547
12548 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
12549     
12550      /**
12551      * @cfg {Number} startDay
12552      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
12553      */
12554     startDay : 0,
12555     
12556     loadMask : false,
12557     
12558     header : false,
12559       
12560     getAutoCreate : function(){
12561         
12562         
12563         var fc_button = function(name, corner, style, content ) {
12564             return Roo.apply({},{
12565                 tag : 'span',
12566                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
12567                          (corner.length ?
12568                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
12569                             ''
12570                         ),
12571                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
12572                 unselectable: 'on'
12573             });
12574         };
12575         
12576         var header = {};
12577         
12578         if(!this.header){
12579             header = {
12580                 tag : 'table',
12581                 cls : 'fc-header',
12582                 style : 'width:100%',
12583                 cn : [
12584                     {
12585                         tag: 'tr',
12586                         cn : [
12587                             {
12588                                 tag : 'td',
12589                                 cls : 'fc-header-left',
12590                                 cn : [
12591                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
12592                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
12593                                     { tag: 'span', cls: 'fc-header-space' },
12594                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
12595
12596
12597                                 ]
12598                             },
12599
12600                             {
12601                                 tag : 'td',
12602                                 cls : 'fc-header-center',
12603                                 cn : [
12604                                     {
12605                                         tag: 'span',
12606                                         cls: 'fc-header-title',
12607                                         cn : {
12608                                             tag: 'H2',
12609                                             html : 'month / year'
12610                                         }
12611                                     }
12612
12613                                 ]
12614                             },
12615                             {
12616                                 tag : 'td',
12617                                 cls : 'fc-header-right',
12618                                 cn : [
12619                               /*      fc_button('month', 'left', '', 'month' ),
12620                                     fc_button('week', '', '', 'week' ),
12621                                     fc_button('day', 'right', '', 'day' )
12622                                 */    
12623
12624                                 ]
12625                             }
12626
12627                         ]
12628                     }
12629                 ]
12630             };
12631         }
12632         
12633         header = this.header;
12634         
12635        
12636         var cal_heads = function() {
12637             var ret = [];
12638             // fixme - handle this.
12639             
12640             for (var i =0; i < Date.dayNames.length; i++) {
12641                 var d = Date.dayNames[i];
12642                 ret.push({
12643                     tag: 'th',
12644                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
12645                     html : d.substring(0,3)
12646                 });
12647                 
12648             }
12649             ret[0].cls += ' fc-first';
12650             ret[6].cls += ' fc-last';
12651             return ret;
12652         };
12653         var cal_cell = function(n) {
12654             return  {
12655                 tag: 'td',
12656                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
12657                 cn : [
12658                     {
12659                         cn : [
12660                             {
12661                                 cls: 'fc-day-number',
12662                                 html: 'D'
12663                             },
12664                             {
12665                                 cls: 'fc-day-content',
12666                              
12667                                 cn : [
12668                                      {
12669                                         style: 'position: relative;' // height: 17px;
12670                                     }
12671                                 ]
12672                             }
12673                             
12674                             
12675                         ]
12676                     }
12677                 ]
12678                 
12679             }
12680         };
12681         var cal_rows = function() {
12682             
12683             var ret = []
12684             for (var r = 0; r < 6; r++) {
12685                 var row= {
12686                     tag : 'tr',
12687                     cls : 'fc-week',
12688                     cn : []
12689                 };
12690                 
12691                 for (var i =0; i < Date.dayNames.length; i++) {
12692                     var d = Date.dayNames[i];
12693                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
12694
12695                 }
12696                 row.cn[0].cls+=' fc-first';
12697                 row.cn[0].cn[0].style = 'min-height:90px';
12698                 row.cn[6].cls+=' fc-last';
12699                 ret.push(row);
12700                 
12701             }
12702             ret[0].cls += ' fc-first';
12703             ret[4].cls += ' fc-prev-last';
12704             ret[5].cls += ' fc-last';
12705             return ret;
12706             
12707         };
12708         
12709         var cal_table = {
12710             tag: 'table',
12711             cls: 'fc-border-separate',
12712             style : 'width:100%',
12713             cellspacing  : 0,
12714             cn : [
12715                 { 
12716                     tag: 'thead',
12717                     cn : [
12718                         { 
12719                             tag: 'tr',
12720                             cls : 'fc-first fc-last',
12721                             cn : cal_heads()
12722                         }
12723                     ]
12724                 },
12725                 { 
12726                     tag: 'tbody',
12727                     cn : cal_rows()
12728                 }
12729                   
12730             ]
12731         };
12732          
12733          var cfg = {
12734             cls : 'fc fc-ltr',
12735             cn : [
12736                 header,
12737                 {
12738                     cls : 'fc-content',
12739                     style : "position: relative;",
12740                     cn : [
12741                         {
12742                             cls : 'fc-view fc-view-month fc-grid',
12743                             style : 'position: relative',
12744                             unselectable : 'on',
12745                             cn : [
12746                                 {
12747                                     cls : 'fc-event-container',
12748                                     style : 'position:absolute;z-index:8;top:0;left:0;'
12749                                 },
12750                                 cal_table
12751                             ]
12752                         }
12753                     ]
12754     
12755                 }
12756            ] 
12757             
12758         };
12759         
12760          
12761         
12762         return cfg;
12763     },
12764     
12765     
12766     initEvents : function()
12767     {
12768         if(!this.store){
12769             throw "can not find store for calendar";
12770         }
12771         
12772         var mark = {
12773             tag: "div",
12774             cls:"x-dlg-mask",
12775             style: "text-align:center",
12776             cn: [
12777                 {
12778                     tag: "div",
12779                     style: "background-color:white;width:50%;margin:250 auto",
12780                     cn: [
12781                         {
12782                             tag: "img",
12783                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
12784                         },
12785                         {
12786                             tag: "span",
12787                             html: "Loading"
12788                         }
12789                         
12790                     ]
12791                 }
12792             ]
12793         }
12794         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
12795         
12796         var size = this.el.select('.fc-content', true).first().getSize();
12797         this.maskEl.setSize(size.width, size.height);
12798         this.maskEl.enableDisplayMode("block");
12799         if(!this.loadMask){
12800             this.maskEl.hide();
12801         }
12802         
12803         this.store = Roo.factory(this.store, Roo.data);
12804         this.store.on('load', this.onLoad, this);
12805         this.store.on('beforeload', this.onBeforeLoad, this);
12806         
12807         this.resize();
12808         
12809         this.cells = this.el.select('.fc-day',true);
12810         //Roo.log(this.cells);
12811         this.textNodes = this.el.query('.fc-day-number');
12812         this.cells.addClassOnOver('fc-state-hover');
12813         
12814         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
12815         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
12816         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
12817         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
12818         
12819         this.on('monthchange', this.onMonthChange, this);
12820         
12821         this.update(new Date().clearTime());
12822     },
12823     
12824     resize : function() {
12825         var sz  = this.el.getSize();
12826         
12827         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
12828         this.el.select('.fc-day-content div',true).setHeight(34);
12829     },
12830     
12831     
12832     // private
12833     showPrevMonth : function(e){
12834         this.update(this.activeDate.add("mo", -1));
12835     },
12836     showToday : function(e){
12837         this.update(new Date().clearTime());
12838     },
12839     // private
12840     showNextMonth : function(e){
12841         this.update(this.activeDate.add("mo", 1));
12842     },
12843
12844     // private
12845     showPrevYear : function(){
12846         this.update(this.activeDate.add("y", -1));
12847     },
12848
12849     // private
12850     showNextYear : function(){
12851         this.update(this.activeDate.add("y", 1));
12852     },
12853
12854     
12855    // private
12856     update : function(date)
12857     {
12858         var vd = this.activeDate;
12859         this.activeDate = date;
12860 //        if(vd && this.el){
12861 //            var t = date.getTime();
12862 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
12863 //                Roo.log('using add remove');
12864 //                
12865 //                this.fireEvent('monthchange', this, date);
12866 //                
12867 //                this.cells.removeClass("fc-state-highlight");
12868 //                this.cells.each(function(c){
12869 //                   if(c.dateValue == t){
12870 //                       c.addClass("fc-state-highlight");
12871 //                       setTimeout(function(){
12872 //                            try{c.dom.firstChild.focus();}catch(e){}
12873 //                       }, 50);
12874 //                       return false;
12875 //                   }
12876 //                   return true;
12877 //                });
12878 //                return;
12879 //            }
12880 //        }
12881         
12882         var days = date.getDaysInMonth();
12883         
12884         var firstOfMonth = date.getFirstDateOfMonth();
12885         var startingPos = firstOfMonth.getDay()-this.startDay;
12886         
12887         if(startingPos < this.startDay){
12888             startingPos += 7;
12889         }
12890         
12891         var pm = date.add(Date.MONTH, -1);
12892         var prevStart = pm.getDaysInMonth()-startingPos;
12893 //        
12894         this.cells = this.el.select('.fc-day',true);
12895         this.textNodes = this.el.query('.fc-day-number');
12896         this.cells.addClassOnOver('fc-state-hover');
12897         
12898         var cells = this.cells.elements;
12899         var textEls = this.textNodes;
12900         
12901         Roo.each(cells, function(cell){
12902             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
12903         });
12904         
12905         days += startingPos;
12906
12907         // convert everything to numbers so it's fast
12908         var day = 86400000;
12909         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
12910         //Roo.log(d);
12911         //Roo.log(pm);
12912         //Roo.log(prevStart);
12913         
12914         var today = new Date().clearTime().getTime();
12915         var sel = date.clearTime().getTime();
12916         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
12917         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
12918         var ddMatch = this.disabledDatesRE;
12919         var ddText = this.disabledDatesText;
12920         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
12921         var ddaysText = this.disabledDaysText;
12922         var format = this.format;
12923         
12924         var setCellClass = function(cal, cell){
12925             cell.row = 0;
12926             cell.events = [];
12927             cell.more = [];
12928             //Roo.log('set Cell Class');
12929             cell.title = "";
12930             var t = d.getTime();
12931             
12932             //Roo.log(d);
12933             
12934             cell.dateValue = t;
12935             if(t == today){
12936                 cell.className += " fc-today";
12937                 cell.className += " fc-state-highlight";
12938                 cell.title = cal.todayText;
12939             }
12940             if(t == sel){
12941                 // disable highlight in other month..
12942                 //cell.className += " fc-state-highlight";
12943                 
12944             }
12945             // disabling
12946             if(t < min) {
12947                 cell.className = " fc-state-disabled";
12948                 cell.title = cal.minText;
12949                 return;
12950             }
12951             if(t > max) {
12952                 cell.className = " fc-state-disabled";
12953                 cell.title = cal.maxText;
12954                 return;
12955             }
12956             if(ddays){
12957                 if(ddays.indexOf(d.getDay()) != -1){
12958                     cell.title = ddaysText;
12959                     cell.className = " fc-state-disabled";
12960                 }
12961             }
12962             if(ddMatch && format){
12963                 var fvalue = d.dateFormat(format);
12964                 if(ddMatch.test(fvalue)){
12965                     cell.title = ddText.replace("%0", fvalue);
12966                     cell.className = " fc-state-disabled";
12967                 }
12968             }
12969             
12970             if (!cell.initialClassName) {
12971                 cell.initialClassName = cell.dom.className;
12972             }
12973             
12974             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
12975         };
12976
12977         var i = 0;
12978         
12979         for(; i < startingPos; i++) {
12980             textEls[i].innerHTML = (++prevStart);
12981             d.setDate(d.getDate()+1);
12982             
12983             cells[i].className = "fc-past fc-other-month";
12984             setCellClass(this, cells[i]);
12985         }
12986         
12987         var intDay = 0;
12988         
12989         for(; i < days; i++){
12990             intDay = i - startingPos + 1;
12991             textEls[i].innerHTML = (intDay);
12992             d.setDate(d.getDate()+1);
12993             
12994             cells[i].className = ''; // "x-date-active";
12995             setCellClass(this, cells[i]);
12996         }
12997         var extraDays = 0;
12998         
12999         for(; i < 42; i++) {
13000             textEls[i].innerHTML = (++extraDays);
13001             d.setDate(d.getDate()+1);
13002             
13003             cells[i].className = "fc-future fc-other-month";
13004             setCellClass(this, cells[i]);
13005         }
13006         
13007         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
13008         
13009         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
13010         
13011         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
13012         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
13013         
13014         if(totalRows != 6){
13015             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
13016             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
13017         }
13018         
13019         this.fireEvent('monthchange', this, date);
13020         
13021         
13022         /*
13023         if(!this.internalRender){
13024             var main = this.el.dom.firstChild;
13025             var w = main.offsetWidth;
13026             this.el.setWidth(w + this.el.getBorderWidth("lr"));
13027             Roo.fly(main).setWidth(w);
13028             this.internalRender = true;
13029             // opera does not respect the auto grow header center column
13030             // then, after it gets a width opera refuses to recalculate
13031             // without a second pass
13032             if(Roo.isOpera && !this.secondPass){
13033                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
13034                 this.secondPass = true;
13035                 this.update.defer(10, this, [date]);
13036             }
13037         }
13038         */
13039         
13040     },
13041     
13042     findCell : function(dt) {
13043         dt = dt.clearTime().getTime();
13044         var ret = false;
13045         this.cells.each(function(c){
13046             //Roo.log("check " +c.dateValue + '?=' + dt);
13047             if(c.dateValue == dt){
13048                 ret = c;
13049                 return false;
13050             }
13051             return true;
13052         });
13053         
13054         return ret;
13055     },
13056     
13057     findCells : function(ev) {
13058         var s = ev.start.clone().clearTime().getTime();
13059        // Roo.log(s);
13060         var e= ev.end.clone().clearTime().getTime();
13061        // Roo.log(e);
13062         var ret = [];
13063         this.cells.each(function(c){
13064              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
13065             
13066             if(c.dateValue > e){
13067                 return ;
13068             }
13069             if(c.dateValue < s){
13070                 return ;
13071             }
13072             ret.push(c);
13073         });
13074         
13075         return ret;    
13076     },
13077     
13078 //    findBestRow: function(cells)
13079 //    {
13080 //        var ret = 0;
13081 //        
13082 //        for (var i =0 ; i < cells.length;i++) {
13083 //            ret  = Math.max(cells[i].rows || 0,ret);
13084 //        }
13085 //        return ret;
13086 //        
13087 //    },
13088     
13089     
13090     addItem : function(ev)
13091     {
13092         // look for vertical location slot in
13093         var cells = this.findCells(ev);
13094         
13095 //        ev.row = this.findBestRow(cells);
13096         
13097         // work out the location.
13098         
13099         var crow = false;
13100         var rows = [];
13101         for(var i =0; i < cells.length; i++) {
13102             
13103             cells[i].row = cells[0].row;
13104             
13105             if(i == 0){
13106                 cells[i].row = cells[i].row + 1;
13107             }
13108             
13109             if (!crow) {
13110                 crow = {
13111                     start : cells[i],
13112                     end :  cells[i]
13113                 };
13114                 continue;
13115             }
13116             if (crow.start.getY() == cells[i].getY()) {
13117                 // on same row.
13118                 crow.end = cells[i];
13119                 continue;
13120             }
13121             // different row.
13122             rows.push(crow);
13123             crow = {
13124                 start: cells[i],
13125                 end : cells[i]
13126             };
13127             
13128         }
13129         
13130         rows.push(crow);
13131         ev.els = [];
13132         ev.rows = rows;
13133         ev.cells = cells;
13134         
13135         cells[0].events.push(ev);
13136         
13137         this.calevents.push(ev);
13138     },
13139     
13140     clearEvents: function() {
13141         
13142         if(!this.calevents){
13143             return;
13144         }
13145         
13146         Roo.each(this.cells.elements, function(c){
13147             c.row = 0;
13148             c.events = [];
13149             c.more = [];
13150         });
13151         
13152         Roo.each(this.calevents, function(e) {
13153             Roo.each(e.els, function(el) {
13154                 el.un('mouseenter' ,this.onEventEnter, this);
13155                 el.un('mouseleave' ,this.onEventLeave, this);
13156                 el.remove();
13157             },this);
13158         },this);
13159         
13160         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
13161             e.remove();
13162         });
13163         
13164     },
13165     
13166     renderEvents: function()
13167     {   
13168         var _this = this;
13169         
13170         this.cells.each(function(c) {
13171             
13172             if(c.row < 5){
13173                 return;
13174             }
13175             
13176             var ev = c.events;
13177             
13178             var r = 4;
13179             if(c.row != c.events.length){
13180                 r = 4 - (4 - (c.row - c.events.length));
13181             }
13182             
13183             c.events = ev.slice(0, r);
13184             c.more = ev.slice(r);
13185             
13186             if(c.more.length && c.more.length == 1){
13187                 c.events.push(c.more.pop());
13188             }
13189             
13190             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
13191             
13192         });
13193             
13194         this.cells.each(function(c) {
13195             
13196             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
13197             
13198             
13199             for (var e = 0; e < c.events.length; e++){
13200                 var ev = c.events[e];
13201                 var rows = ev.rows;
13202                 
13203                 for(var i = 0; i < rows.length; i++) {
13204                 
13205                     // how many rows should it span..
13206
13207                     var  cfg = {
13208                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
13209                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
13210
13211                         unselectable : "on",
13212                         cn : [
13213                             {
13214                                 cls: 'fc-event-inner',
13215                                 cn : [
13216     //                                {
13217     //                                  tag:'span',
13218     //                                  cls: 'fc-event-time',
13219     //                                  html : cells.length > 1 ? '' : ev.time
13220     //                                },
13221                                     {
13222                                       tag:'span',
13223                                       cls: 'fc-event-title',
13224                                       html : String.format('{0}', ev.title)
13225                                     }
13226
13227
13228                                 ]
13229                             },
13230                             {
13231                                 cls: 'ui-resizable-handle ui-resizable-e',
13232                                 html : '&nbsp;&nbsp;&nbsp'
13233                             }
13234
13235                         ]
13236                     };
13237
13238                     if (i == 0) {
13239                         cfg.cls += ' fc-event-start';
13240                     }
13241                     if ((i+1) == rows.length) {
13242                         cfg.cls += ' fc-event-end';
13243                     }
13244
13245                     var ctr = _this.el.select('.fc-event-container',true).first();
13246                     var cg = ctr.createChild(cfg);
13247
13248                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
13249                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
13250
13251                     var r = (c.more.length) ? 1 : 0;
13252                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
13253                     cg.setWidth(ebox.right - sbox.x -2);
13254
13255                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
13256                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
13257                     cg.on('click', _this.onEventClick, _this, ev);
13258
13259                     ev.els.push(cg);
13260                     
13261                 }
13262                 
13263             }
13264             
13265             
13266             if(c.more.length){
13267                 var  cfg = {
13268                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
13269                     style : 'position: absolute',
13270                     unselectable : "on",
13271                     cn : [
13272                         {
13273                             cls: 'fc-event-inner',
13274                             cn : [
13275                                 {
13276                                   tag:'span',
13277                                   cls: 'fc-event-title',
13278                                   html : 'More'
13279                                 }
13280
13281
13282                             ]
13283                         },
13284                         {
13285                             cls: 'ui-resizable-handle ui-resizable-e',
13286                             html : '&nbsp;&nbsp;&nbsp'
13287                         }
13288
13289                     ]
13290                 };
13291
13292                 var ctr = _this.el.select('.fc-event-container',true).first();
13293                 var cg = ctr.createChild(cfg);
13294
13295                 var sbox = c.select('.fc-day-content',true).first().getBox();
13296                 var ebox = c.select('.fc-day-content',true).first().getBox();
13297                 //Roo.log(cg);
13298                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
13299                 cg.setWidth(ebox.right - sbox.x -2);
13300
13301                 cg.on('click', _this.onMoreEventClick, _this, c.more);
13302                 
13303             }
13304             
13305         });
13306         
13307         
13308         
13309     },
13310     
13311     onEventEnter: function (e, el,event,d) {
13312         this.fireEvent('evententer', this, el, event);
13313     },
13314     
13315     onEventLeave: function (e, el,event,d) {
13316         this.fireEvent('eventleave', this, el, event);
13317     },
13318     
13319     onEventClick: function (e, el,event,d) {
13320         this.fireEvent('eventclick', this, el, event);
13321     },
13322     
13323     onMonthChange: function () {
13324         this.store.load();
13325     },
13326     
13327     onMoreEventClick: function(e, el, more)
13328     {
13329         var _this = this;
13330         
13331         this.calpopover.placement = 'right';
13332         this.calpopover.setTitle('More');
13333         
13334         this.calpopover.setContent('');
13335         
13336         var ctr = this.calpopover.el.select('.popover-content', true).first();
13337         
13338         Roo.each(more, function(m){
13339             var cfg = {
13340                 cls : 'fc-event-hori fc-event-draggable',
13341                 html : m.title
13342             }
13343             var cg = ctr.createChild(cfg);
13344             
13345             cg.on('click', _this.onEventClick, _this, m);
13346         });
13347         
13348         this.calpopover.show(el);
13349         
13350         
13351     },
13352     
13353     onLoad: function () 
13354     {   
13355         this.calevents = [];
13356         var cal = this;
13357         
13358         if(this.store.getCount() > 0){
13359             this.store.data.each(function(d){
13360                cal.addItem({
13361                     id : d.data.id,
13362                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
13363                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
13364                     time : d.data.start_time,
13365                     title : d.data.title,
13366                     description : d.data.description,
13367                     venue : d.data.venue
13368                 });
13369             });
13370         }
13371         
13372         this.renderEvents();
13373         
13374         if(this.calevents.length && this.loadMask){
13375             this.maskEl.hide();
13376         }
13377     },
13378     
13379     onBeforeLoad: function()
13380     {
13381         this.clearEvents();
13382         if(this.loadMask){
13383             this.maskEl.show();
13384         }
13385     }
13386 });
13387
13388  
13389  /*
13390  * - LGPL
13391  *
13392  * element
13393  * 
13394  */
13395
13396 /**
13397  * @class Roo.bootstrap.Popover
13398  * @extends Roo.bootstrap.Component
13399  * Bootstrap Popover class
13400  * @cfg {String} html contents of the popover   (or false to use children..)
13401  * @cfg {String} title of popover (or false to hide)
13402  * @cfg {String} placement how it is placed
13403  * @cfg {String} trigger click || hover (or false to trigger manually)
13404  * @cfg {String} over what (parent or false to trigger manually.)
13405  * 
13406  * @constructor
13407  * Create a new Popover
13408  * @param {Object} config The config object
13409  */
13410
13411 Roo.bootstrap.Popover = function(config){
13412     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
13413 };
13414
13415 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
13416     
13417     title: 'Fill in a title',
13418     html: false,
13419     
13420     placement : 'right',
13421     trigger : 'hover', // hover
13422     
13423     over: 'parent',
13424     
13425     can_build_overlaid : false,
13426     
13427     getChildContainer : function()
13428     {
13429         return this.el.select('.popover-content',true).first();
13430     },
13431     
13432     getAutoCreate : function(){
13433          Roo.log('make popover?');
13434         var cfg = {
13435            cls : 'popover roo-dynamic',
13436            style: 'display:block',
13437            cn : [
13438                 {
13439                     cls : 'arrow'
13440                 },
13441                 {
13442                     cls : 'popover-inner',
13443                     cn : [
13444                         {
13445                             tag: 'h3',
13446                             cls: 'popover-title',
13447                             html : this.title
13448                         },
13449                         {
13450                             cls : 'popover-content',
13451                             html : this.html
13452                         }
13453                     ]
13454                     
13455                 }
13456            ]
13457         };
13458         
13459         return cfg;
13460     },
13461     setTitle: function(str)
13462     {
13463         this.el.select('.popover-title',true).first().dom.innerHTML = str;
13464     },
13465     setContent: function(str)
13466     {
13467         this.el.select('.popover-content',true).first().dom.innerHTML = str;
13468     },
13469     // as it get's added to the bottom of the page.
13470     onRender : function(ct, position)
13471     {
13472         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
13473         if(!this.el){
13474             var cfg = Roo.apply({},  this.getAutoCreate());
13475             cfg.id = Roo.id();
13476             
13477             if (this.cls) {
13478                 cfg.cls += ' ' + this.cls;
13479             }
13480             if (this.style) {
13481                 cfg.style = this.style;
13482             }
13483             Roo.log("adding to ")
13484             this.el = Roo.get(document.body).createChild(cfg, position);
13485             Roo.log(this.el);
13486         }
13487         this.initEvents();
13488     },
13489     
13490     initEvents : function()
13491     {
13492         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
13493         this.el.enableDisplayMode('block');
13494         this.el.hide();
13495         if (this.over === false) {
13496             return; 
13497         }
13498         if (this.triggers === false) {
13499             return;
13500         }
13501         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13502         var triggers = this.trigger ? this.trigger.split(' ') : [];
13503         Roo.each(triggers, function(trigger) {
13504         
13505             if (trigger == 'click') {
13506                 on_el.on('click', this.toggle, this);
13507             } else if (trigger != 'manual') {
13508                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
13509                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
13510       
13511                 on_el.on(eventIn  ,this.enter, this);
13512                 on_el.on(eventOut, this.leave, this);
13513             }
13514         }, this);
13515         
13516     },
13517     
13518     
13519     // private
13520     timeout : null,
13521     hoverState : null,
13522     
13523     toggle : function () {
13524         this.hoverState == 'in' ? this.leave() : this.enter();
13525     },
13526     
13527     enter : function () {
13528        
13529     
13530         clearTimeout(this.timeout);
13531     
13532         this.hoverState = 'in'
13533     
13534         if (!this.delay || !this.delay.show) {
13535             this.show();
13536             return 
13537         }
13538         var _t = this;
13539         this.timeout = setTimeout(function () {
13540             if (_t.hoverState == 'in') {
13541                 _t.show();
13542             }
13543         }, this.delay.show)
13544     },
13545     leave : function() {
13546         clearTimeout(this.timeout);
13547     
13548         this.hoverState = 'out'
13549     
13550         if (!this.delay || !this.delay.hide) {
13551             this.hide();
13552             return 
13553         }
13554         var _t = this;
13555         this.timeout = setTimeout(function () {
13556             if (_t.hoverState == 'out') {
13557                 _t.hide();
13558             }
13559         }, this.delay.hide)
13560     },
13561     
13562     show : function (on_el)
13563     {
13564         if (!on_el) {
13565             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13566         }
13567         // set content.
13568         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
13569         if (this.html !== false) {
13570             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
13571         }
13572         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
13573         if (!this.title.length) {
13574             this.el.select('.popover-title',true).hide();
13575         }
13576         
13577         var placement = typeof this.placement == 'function' ?
13578             this.placement.call(this, this.el, on_el) :
13579             this.placement;
13580             
13581         var autoToken = /\s?auto?\s?/i;
13582         var autoPlace = autoToken.test(placement);
13583         if (autoPlace) {
13584             placement = placement.replace(autoToken, '') || 'top';
13585         }
13586         
13587         //this.el.detach()
13588         //this.el.setXY([0,0]);
13589         this.el.show();
13590         this.el.dom.style.display='block';
13591         this.el.addClass(placement);
13592         
13593         //this.el.appendTo(on_el);
13594         
13595         var p = this.getPosition();
13596         var box = this.el.getBox();
13597         
13598         if (autoPlace) {
13599             // fixme..
13600         }
13601         var align = Roo.bootstrap.Popover.alignment[placement]
13602         this.el.alignTo(on_el, align[0],align[1]);
13603         //var arrow = this.el.select('.arrow',true).first();
13604         //arrow.set(align[2], 
13605         
13606         this.el.addClass('in');
13607         this.hoverState = null;
13608         
13609         if (this.el.hasClass('fade')) {
13610             // fade it?
13611         }
13612         
13613     },
13614     hide : function()
13615     {
13616         this.el.setXY([0,0]);
13617         this.el.removeClass('in');
13618         this.el.hide();
13619         
13620     }
13621     
13622 });
13623
13624 Roo.bootstrap.Popover.alignment = {
13625     'left' : ['r-l', [-10,0], 'right'],
13626     'right' : ['l-r', [10,0], 'left'],
13627     'bottom' : ['t-b', [0,10], 'top'],
13628     'top' : [ 'b-t', [0,-10], 'bottom']
13629 };
13630
13631  /*
13632  * - LGPL
13633  *
13634  * Progress
13635  * 
13636  */
13637
13638 /**
13639  * @class Roo.bootstrap.Progress
13640  * @extends Roo.bootstrap.Component
13641  * Bootstrap Progress class
13642  * @cfg {Boolean} striped striped of the progress bar
13643  * @cfg {Boolean} active animated of the progress bar
13644  * 
13645  * 
13646  * @constructor
13647  * Create a new Progress
13648  * @param {Object} config The config object
13649  */
13650
13651 Roo.bootstrap.Progress = function(config){
13652     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
13653 };
13654
13655 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
13656     
13657     striped : false,
13658     active: false,
13659     
13660     getAutoCreate : function(){
13661         var cfg = {
13662             tag: 'div',
13663             cls: 'progress'
13664         };
13665         
13666         
13667         if(this.striped){
13668             cfg.cls += ' progress-striped';
13669         }
13670       
13671         if(this.active){
13672             cfg.cls += ' active';
13673         }
13674         
13675         
13676         return cfg;
13677     }
13678    
13679 });
13680
13681  
13682
13683  /*
13684  * - LGPL
13685  *
13686  * ProgressBar
13687  * 
13688  */
13689
13690 /**
13691  * @class Roo.bootstrap.ProgressBar
13692  * @extends Roo.bootstrap.Component
13693  * Bootstrap ProgressBar class
13694  * @cfg {Number} aria_valuenow aria-value now
13695  * @cfg {Number} aria_valuemin aria-value min
13696  * @cfg {Number} aria_valuemax aria-value max
13697  * @cfg {String} label label for the progress bar
13698  * @cfg {String} panel (success | info | warning | danger )
13699  * @cfg {String} role role of the progress bar
13700  * @cfg {String} sr_only text
13701  * 
13702  * 
13703  * @constructor
13704  * Create a new ProgressBar
13705  * @param {Object} config The config object
13706  */
13707
13708 Roo.bootstrap.ProgressBar = function(config){
13709     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
13710 };
13711
13712 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
13713     
13714     aria_valuenow : 0,
13715     aria_valuemin : 0,
13716     aria_valuemax : 100,
13717     label : false,
13718     panel : false,
13719     role : false,
13720     sr_only: false,
13721     
13722     getAutoCreate : function()
13723     {
13724         
13725         var cfg = {
13726             tag: 'div',
13727             cls: 'progress-bar',
13728             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
13729         };
13730         
13731         if(this.sr_only){
13732             cfg.cn = {
13733                 tag: 'span',
13734                 cls: 'sr-only',
13735                 html: this.sr_only
13736             }
13737         }
13738         
13739         if(this.role){
13740             cfg.role = this.role;
13741         }
13742         
13743         if(this.aria_valuenow){
13744             cfg['aria-valuenow'] = this.aria_valuenow;
13745         }
13746         
13747         if(this.aria_valuemin){
13748             cfg['aria-valuemin'] = this.aria_valuemin;
13749         }
13750         
13751         if(this.aria_valuemax){
13752             cfg['aria-valuemax'] = this.aria_valuemax;
13753         }
13754         
13755         if(this.label && !this.sr_only){
13756             cfg.html = this.label;
13757         }
13758         
13759         if(this.panel){
13760             cfg.cls += ' progress-bar-' + this.panel;
13761         }
13762         
13763         return cfg;
13764     },
13765     
13766     update : function(aria_valuenow)
13767     {
13768         this.aria_valuenow = aria_valuenow;
13769         
13770         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
13771     }
13772    
13773 });
13774
13775  
13776
13777  /*
13778  * - LGPL
13779  *
13780  * column
13781  * 
13782  */
13783
13784 /**
13785  * @class Roo.bootstrap.TabGroup
13786  * @extends Roo.bootstrap.Column
13787  * Bootstrap Column class
13788  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
13789  * @cfg {Boolean} carousel true to make the group behave like a carousel
13790  * 
13791  * @constructor
13792  * Create a new TabGroup
13793  * @param {Object} config The config object
13794  */
13795
13796 Roo.bootstrap.TabGroup = function(config){
13797     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
13798     if (!this.navId) {
13799         this.navId = Roo.id();
13800     }
13801     this.tabs = [];
13802     Roo.bootstrap.TabGroup.register(this);
13803     
13804 };
13805
13806 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
13807     
13808     carousel : false,
13809      
13810     getAutoCreate : function()
13811     {
13812         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
13813         
13814         cfg.cls += ' tab-content';
13815         
13816         if (this.carousel) {
13817             cfg.cls += ' carousel slide';
13818             cfg.cn = [{
13819                cls : 'carousel-inner'
13820             }]
13821         }
13822         
13823         
13824         return cfg;
13825     },
13826     getChildContainer : function()
13827     {
13828         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
13829     },
13830     
13831     /**
13832     * register a Navigation item
13833     * @param {Roo.bootstrap.NavItem} the navitem to add
13834     */
13835     register : function(item)
13836     {
13837         this.tabs.push( item);
13838         item.navId = this.navId; // not really needed..
13839     
13840     },
13841     
13842     getActivePanel : function()
13843     {
13844         var r = false;
13845         Roo.each(this.tabs, function(t) {
13846             if (t.active) {
13847                 r = t;
13848                 return false;
13849             }
13850             return null;
13851         });
13852         return r;
13853         
13854     },
13855     getPanelByName : function(n)
13856     {
13857         var r = false;
13858         Roo.each(this.tabs, function(t) {
13859             if (t.tabId == n) {
13860                 r = t;
13861                 return false;
13862             }
13863             return null;
13864         });
13865         return r;
13866     },
13867     indexOfPanel : function(p)
13868     {
13869         var r = false;
13870         Roo.each(this.tabs, function(t,i) {
13871             if (t.tabId == p.tabId) {
13872                 r = i;
13873                 return false;
13874             }
13875             return null;
13876         });
13877         return r;
13878     },
13879     /**
13880      * show a specific panel
13881      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
13882      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
13883      */
13884     showPanel : function (pan)
13885     {
13886         
13887         
13888         
13889         if (typeof(pan) == 'number') {
13890             pan = this.tabs[pan];
13891         }
13892         if (typeof(pan) == 'string') {
13893             pan = this.getPanelByName(pan);
13894         }
13895         if (pan.tabId == this.getActivePanel().tabId) {
13896             return true;
13897         }
13898         var cur = this.getActivePanel();
13899         
13900         if (false === cur.fireEvent('beforedeactivate')) {
13901             return false;
13902         }
13903         
13904         
13905         
13906         if (this.carousel) {
13907             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
13908             var lr = dir == 'next' ? 'left' : 'right';
13909             pan.el.addClass(dir); // or prev
13910             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
13911             cur.el.addClass(lr); // or right
13912             pan.el.addClass(lr);
13913             cur.el.on('transitionend', function() {
13914                 Roo.log("trans end?");
13915                 
13916                 pan.el.removeClass([lr,dir]);
13917                 pan.setActive(true);
13918                 
13919                 cur.el.removeClass([lr]);
13920                 cur.setActive(false);
13921                 
13922                 
13923             }, this, { single:  true } );
13924             return true;
13925         }
13926         
13927         cur.setActive(false);
13928         pan.setActive(true);
13929         return true;
13930         
13931     },
13932     showPanelNext : function()
13933     {
13934         var i = this.indexOfPanel(this.getActivePanel());
13935         if (i > this.tabs.length) {
13936             return;
13937         }
13938         this.showPanel(this.tabs[i+1]);
13939     },
13940     showPanelPrev : function()
13941     {
13942         var i = this.indexOfPanel(this.getActivePanel());
13943         if (i  < 1) {
13944             return;
13945         }
13946         this.showPanel(this.tabs[i-1]);
13947     }
13948     
13949     
13950   
13951 });
13952
13953  
13954
13955  
13956  
13957 Roo.apply(Roo.bootstrap.TabGroup, {
13958     
13959     groups: {},
13960      /**
13961     * register a Navigation Group
13962     * @param {Roo.bootstrap.NavGroup} the navgroup to add
13963     */
13964     register : function(navgrp)
13965     {
13966         this.groups[navgrp.navId] = navgrp;
13967         
13968     },
13969     /**
13970     * fetch a Navigation Group based on the navigation ID
13971     * if one does not exist , it will get created.
13972     * @param {string} the navgroup to add
13973     * @returns {Roo.bootstrap.NavGroup} the navgroup 
13974     */
13975     get: function(navId) {
13976         if (typeof(this.groups[navId]) == 'undefined') {
13977             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
13978         }
13979         return this.groups[navId] ;
13980     }
13981     
13982     
13983     
13984 });
13985
13986  /*
13987  * - LGPL
13988  *
13989  * TabPanel
13990  * 
13991  */
13992
13993 /**
13994  * @class Roo.bootstrap.TabPanel
13995  * @extends Roo.bootstrap.Component
13996  * Bootstrap TabPanel class
13997  * @cfg {Boolean} active panel active
13998  * @cfg {String} html panel content
13999  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
14000  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
14001  * 
14002  * 
14003  * @constructor
14004  * Create a new TabPanel
14005  * @param {Object} config The config object
14006  */
14007
14008 Roo.bootstrap.TabPanel = function(config){
14009     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
14010     this.addEvents({
14011         /**
14012              * @event changed
14013              * Fires when the active status changes
14014              * @param {Roo.bootstrap.TabPanel} this
14015              * @param {Boolean} state the new state
14016             
14017          */
14018         'changed': true,
14019         /**
14020              * @event beforedeactivate
14021              * Fires before a tab is de-activated - can be used to do validation on a form.
14022              * @param {Roo.bootstrap.TabPanel} this
14023              * @return {Boolean} false if there is an error
14024             
14025          */
14026         'beforedeactivate': true
14027      });
14028     
14029     this.tabId = this.tabId || Roo.id();
14030   
14031 };
14032
14033 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
14034     
14035     active: false,
14036     html: false,
14037     tabId: false,
14038     navId : false,
14039     
14040     getAutoCreate : function(){
14041         var cfg = {
14042             tag: 'div',
14043             // item is needed for carousel - not sure if it has any effect otherwise
14044             cls: 'tab-pane item',
14045             html: this.html || ''
14046         };
14047         
14048         if(this.active){
14049             cfg.cls += ' active';
14050         }
14051         
14052         if(this.tabId){
14053             cfg.tabId = this.tabId;
14054         }
14055         
14056         
14057         return cfg;
14058     },
14059     
14060     initEvents:  function()
14061     {
14062         Roo.log('-------- init events on tab panel ---------');
14063         
14064         var p = this.parent();
14065         this.navId = this.navId || p.navId;
14066         
14067         if (typeof(this.navId) != 'undefined') {
14068             // not really needed.. but just in case.. parent should be a NavGroup.
14069             var tg = Roo.bootstrap.TabGroup.get(this.navId);
14070             Roo.log(['register', tg, this]);
14071             tg.register(this);
14072         }
14073     },
14074     
14075     
14076     onRender : function(ct, position)
14077     {
14078        // Roo.log("Call onRender: " + this.xtype);
14079         
14080         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
14081         
14082         
14083         
14084         
14085         
14086     },
14087     
14088     setActive: function(state)
14089     {
14090         Roo.log("panel - set active " + this.tabId + "=" + state);
14091         
14092         this.active = state;
14093         if (!state) {
14094             this.el.removeClass('active');
14095             
14096         } else  if (!this.el.hasClass('active')) {
14097             this.el.addClass('active');
14098         }
14099         this.fireEvent('changed', this, state);
14100     }
14101     
14102     
14103 });
14104  
14105
14106  
14107
14108  /*
14109  * - LGPL
14110  *
14111  * DateField
14112  * 
14113  */
14114
14115 /**
14116  * @class Roo.bootstrap.DateField
14117  * @extends Roo.bootstrap.Input
14118  * Bootstrap DateField class
14119  * @cfg {Number} weekStart default 0
14120  * @cfg {Number} weekStart default 0
14121  * @cfg {Number} viewMode default empty, (months|years)
14122  * @cfg {Number} minViewMode default empty, (months|years)
14123  * @cfg {Number} startDate default -Infinity
14124  * @cfg {Number} endDate default Infinity
14125  * @cfg {Boolean} todayHighlight default false
14126  * @cfg {Boolean} todayBtn default false
14127  * @cfg {Boolean} calendarWeeks default false
14128  * @cfg {Object} daysOfWeekDisabled default empty
14129  * 
14130  * @cfg {Boolean} keyboardNavigation default true
14131  * @cfg {String} language default en
14132  * 
14133  * @constructor
14134  * Create a new DateField
14135  * @param {Object} config The config object
14136  */
14137
14138 Roo.bootstrap.DateField = function(config){
14139     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
14140      this.addEvents({
14141             /**
14142              * @event show
14143              * Fires when this field show.
14144              * @param {Roo.bootstrap.DateField} this
14145              * @param {Mixed} date The date value
14146              */
14147             show : true,
14148             /**
14149              * @event show
14150              * Fires when this field hide.
14151              * @param {Roo.bootstrap.DateField} this
14152              * @param {Mixed} date The date value
14153              */
14154             hide : true,
14155             /**
14156              * @event select
14157              * Fires when select a date.
14158              * @param {Roo.bootstrap.DateField} this
14159              * @param {Mixed} date The date value
14160              */
14161             select : true
14162         });
14163 };
14164
14165 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
14166     
14167     /**
14168      * @cfg {String} format
14169      * The default date format string which can be overriden for localization support.  The format must be
14170      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
14171      */
14172     format : "m/d/y",
14173     /**
14174      * @cfg {String} altFormats
14175      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
14176      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
14177      */
14178     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
14179     
14180     weekStart : 0,
14181     
14182     viewMode : '',
14183     
14184     minViewMode : '',
14185     
14186     todayHighlight : false,
14187     
14188     todayBtn: false,
14189     
14190     language: 'en',
14191     
14192     keyboardNavigation: true,
14193     
14194     calendarWeeks: false,
14195     
14196     startDate: -Infinity,
14197     
14198     endDate: Infinity,
14199     
14200     daysOfWeekDisabled: [],
14201     
14202     _events: [],
14203     
14204     UTCDate: function()
14205     {
14206         return new Date(Date.UTC.apply(Date, arguments));
14207     },
14208     
14209     UTCToday: function()
14210     {
14211         var today = new Date();
14212         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
14213     },
14214     
14215     getDate: function() {
14216             var d = this.getUTCDate();
14217             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
14218     },
14219     
14220     getUTCDate: function() {
14221             return this.date;
14222     },
14223     
14224     setDate: function(d) {
14225             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
14226     },
14227     
14228     setUTCDate: function(d) {
14229             this.date = d;
14230             this.setValue(this.formatDate(this.date));
14231     },
14232         
14233     onRender: function(ct, position)
14234     {
14235         
14236         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
14237         
14238         this.language = this.language || 'en';
14239         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
14240         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
14241         
14242         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
14243         this.format = this.format || 'm/d/y';
14244         this.isInline = false;
14245         this.isInput = true;
14246         this.component = this.el.select('.add-on', true).first() || false;
14247         this.component = (this.component && this.component.length === 0) ? false : this.component;
14248         this.hasInput = this.component && this.inputEL().length;
14249         
14250         if (typeof(this.minViewMode === 'string')) {
14251             switch (this.minViewMode) {
14252                 case 'months':
14253                     this.minViewMode = 1;
14254                     break;
14255                 case 'years':
14256                     this.minViewMode = 2;
14257                     break;
14258                 default:
14259                     this.minViewMode = 0;
14260                     break;
14261             }
14262         }
14263         
14264         if (typeof(this.viewMode === 'string')) {
14265             switch (this.viewMode) {
14266                 case 'months':
14267                     this.viewMode = 1;
14268                     break;
14269                 case 'years':
14270                     this.viewMode = 2;
14271                     break;
14272                 default:
14273                     this.viewMode = 0;
14274                     break;
14275             }
14276         }
14277                 
14278         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
14279         
14280         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14281         
14282         this.picker().on('mousedown', this.onMousedown, this);
14283         this.picker().on('click', this.onClick, this);
14284         
14285         this.picker().addClass('datepicker-dropdown');
14286         
14287         this.startViewMode = this.viewMode;
14288         
14289         
14290         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
14291             if(!this.calendarWeeks){
14292                 v.remove();
14293                 return;
14294             };
14295             
14296             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
14297             v.attr('colspan', function(i, val){
14298                 return parseInt(val) + 1;
14299             });
14300         })
14301                         
14302         
14303         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
14304         
14305         this.setStartDate(this.startDate);
14306         this.setEndDate(this.endDate);
14307         
14308         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
14309         
14310         this.fillDow();
14311         this.fillMonths();
14312         this.update();
14313         this.showMode();
14314         
14315         if(this.isInline) {
14316             this.show();
14317         }
14318     },
14319     
14320     picker : function()
14321     {
14322         return this.el.select('.datepicker', true).first();
14323     },
14324     
14325     fillDow: function()
14326     {
14327         var dowCnt = this.weekStart;
14328         
14329         var dow = {
14330             tag: 'tr',
14331             cn: [
14332                 
14333             ]
14334         };
14335         
14336         if(this.calendarWeeks){
14337             dow.cn.push({
14338                 tag: 'th',
14339                 cls: 'cw',
14340                 html: '&nbsp;'
14341             })
14342         }
14343         
14344         while (dowCnt < this.weekStart + 7) {
14345             dow.cn.push({
14346                 tag: 'th',
14347                 cls: 'dow',
14348                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
14349             });
14350         }
14351         
14352         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
14353     },
14354     
14355     fillMonths: function()
14356     {    
14357         var i = 0
14358         var months = this.picker().select('>.datepicker-months td', true).first();
14359         
14360         months.dom.innerHTML = '';
14361         
14362         while (i < 12) {
14363             var month = {
14364                 tag: 'span',
14365                 cls: 'month',
14366                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
14367             }
14368             
14369             months.createChild(month);
14370         }
14371         
14372     },
14373     
14374     update: function()
14375     {
14376         
14377         this.date = (typeof(this.date) === 'undefined') ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
14378         
14379         if (this.date < this.startDate) {
14380             this.viewDate = new Date(this.startDate);
14381         } else if (this.date > this.endDate) {
14382             this.viewDate = new Date(this.endDate);
14383         } else {
14384             this.viewDate = new Date(this.date);
14385         }
14386         
14387         this.fill();
14388     },
14389     
14390     fill: function() 
14391     {
14392         var d = new Date(this.viewDate),
14393                 year = d.getUTCFullYear(),
14394                 month = d.getUTCMonth(),
14395                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
14396                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
14397                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
14398                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
14399                 currentDate = this.date && this.date.valueOf(),
14400                 today = this.UTCToday();
14401         
14402         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
14403         
14404 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
14405         
14406 //        this.picker.select('>tfoot th.today').
14407 //                                              .text(dates[this.language].today)
14408 //                                              .toggle(this.todayBtn !== false);
14409     
14410         this.updateNavArrows();
14411         this.fillMonths();
14412                                                 
14413         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
14414         
14415         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
14416          
14417         prevMonth.setUTCDate(day);
14418         
14419         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
14420         
14421         var nextMonth = new Date(prevMonth);
14422         
14423         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
14424         
14425         nextMonth = nextMonth.valueOf();
14426         
14427         var fillMonths = false;
14428         
14429         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
14430         
14431         while(prevMonth.valueOf() < nextMonth) {
14432             var clsName = '';
14433             
14434             if (prevMonth.getUTCDay() === this.weekStart) {
14435                 if(fillMonths){
14436                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
14437                 }
14438                     
14439                 fillMonths = {
14440                     tag: 'tr',
14441                     cn: []
14442                 };
14443                 
14444                 if(this.calendarWeeks){
14445                     // ISO 8601: First week contains first thursday.
14446                     // ISO also states week starts on Monday, but we can be more abstract here.
14447                     var
14448                     // Start of current week: based on weekstart/current date
14449                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
14450                     // Thursday of this week
14451                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
14452                     // First Thursday of year, year from thursday
14453                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
14454                     // Calendar week: ms between thursdays, div ms per day, div 7 days
14455                     calWeek =  (th - yth) / 864e5 / 7 + 1;
14456                     
14457                     fillMonths.cn.push({
14458                         tag: 'td',
14459                         cls: 'cw',
14460                         html: calWeek
14461                     });
14462                 }
14463             }
14464             
14465             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
14466                 clsName += ' old';
14467             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
14468                 clsName += ' new';
14469             }
14470             if (this.todayHighlight &&
14471                 prevMonth.getUTCFullYear() == today.getFullYear() &&
14472                 prevMonth.getUTCMonth() == today.getMonth() &&
14473                 prevMonth.getUTCDate() == today.getDate()) {
14474                 clsName += ' today';
14475             }
14476             
14477             if (currentDate && prevMonth.valueOf() === currentDate) {
14478                 clsName += ' active';
14479             }
14480             
14481             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
14482                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
14483                     clsName += ' disabled';
14484             }
14485             
14486             fillMonths.cn.push({
14487                 tag: 'td',
14488                 cls: 'day ' + clsName,
14489                 html: prevMonth.getDate()
14490             })
14491             
14492             prevMonth.setDate(prevMonth.getDate()+1);
14493         }
14494           
14495         var currentYear = this.date && this.date.getUTCFullYear();
14496         var currentMonth = this.date && this.date.getUTCMonth();
14497         
14498         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
14499         
14500         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
14501             v.removeClass('active');
14502             
14503             if(currentYear === year && k === currentMonth){
14504                 v.addClass('active');
14505             }
14506             
14507             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
14508                 v.addClass('disabled');
14509             }
14510             
14511         });
14512         
14513         
14514         year = parseInt(year/10, 10) * 10;
14515         
14516         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
14517         
14518         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
14519         
14520         year -= 1;
14521         for (var i = -1; i < 11; i++) {
14522             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
14523                 tag: 'span',
14524                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
14525                 html: year
14526             })
14527             
14528             year += 1;
14529         }
14530     },
14531     
14532     showMode: function(dir) 
14533     {
14534         if (dir) {
14535             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
14536         }
14537         Roo.each(this.picker().select('>div',true).elements, function(v){
14538             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14539             v.hide();
14540         });
14541         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
14542     },
14543     
14544     place: function()
14545     {
14546         if(this.isInline) return;
14547         
14548         this.picker().removeClass(['bottom', 'top']);
14549         
14550         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
14551             /*
14552              * place to the top of element!
14553              *
14554              */
14555             
14556             this.picker().addClass('top');
14557             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
14558             
14559             return;
14560         }
14561         
14562         this.picker().addClass('bottom');
14563         
14564         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
14565     },
14566     
14567     parseDate : function(value)
14568     {
14569         if(!value || value instanceof Date){
14570             return value;
14571         }
14572         var v = Date.parseDate(value, this.format);
14573         if (!v && this.useIso) {
14574             v = Date.parseDate(value, 'Y-m-d');
14575         }
14576         if(!v && this.altFormats){
14577             if(!this.altFormatsArray){
14578                 this.altFormatsArray = this.altFormats.split("|");
14579             }
14580             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
14581                 v = Date.parseDate(value, this.altFormatsArray[i]);
14582             }
14583         }
14584         return v;
14585     },
14586     
14587     formatDate : function(date, fmt)
14588     {
14589         return (!date || !(date instanceof Date)) ?
14590         date : date.dateFormat(fmt || this.format);
14591     },
14592     
14593     onFocus : function()
14594     {
14595         Roo.bootstrap.DateField.superclass.onFocus.call(this);
14596         this.show();
14597     },
14598     
14599     onBlur : function()
14600     {
14601         Roo.bootstrap.DateField.superclass.onBlur.call(this);
14602         
14603         var d = this.inputEl().getValue();
14604         
14605         if(d && d.length){
14606             this.setValue(d);
14607         }
14608                 
14609         this.hide();
14610     },
14611     
14612     show : function()
14613     {
14614         this.picker().show();
14615         this.update();
14616         this.place();
14617         
14618         this.fireEvent('show', this, this.date);
14619     },
14620     
14621     hide : function()
14622     {
14623         if(this.isInline) return;
14624         this.picker().hide();
14625         this.viewMode = this.startViewMode;
14626         this.showMode();
14627         
14628         this.fireEvent('hide', this, this.date);
14629         
14630     },
14631     
14632     onMousedown: function(e)
14633     {
14634         e.stopPropagation();
14635         e.preventDefault();
14636     },
14637     
14638     keyup: function(e)
14639     {
14640         Roo.bootstrap.DateField.superclass.keyup.call(this);
14641         this.update();
14642     },
14643
14644     setValue: function(v)
14645     {
14646         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
14647         
14648         var d = new Date(v);
14649         
14650         if(isNaN(d.getTime())){
14651             return;
14652         }
14653         
14654         this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
14655
14656         this.update();
14657
14658         this.fireEvent('select', this, this.date);
14659         
14660     },
14661     
14662     getValue: function()
14663     {
14664         return this.formatDate(this.date);
14665     },
14666     
14667     fireKey: function(e)
14668     {
14669         if (!this.picker().isVisible()){
14670             if (e.keyCode == 27) // allow escape to hide and re-show picker
14671                 this.show();
14672             return;
14673         }
14674         
14675         var dateChanged = false,
14676         dir, day, month,
14677         newDate, newViewDate;
14678         
14679         switch(e.keyCode){
14680             case 27: // escape
14681                 this.hide();
14682                 e.preventDefault();
14683                 break;
14684             case 37: // left
14685             case 39: // right
14686                 if (!this.keyboardNavigation) break;
14687                 dir = e.keyCode == 37 ? -1 : 1;
14688                 
14689                 if (e.ctrlKey){
14690                     newDate = this.moveYear(this.date, dir);
14691                     newViewDate = this.moveYear(this.viewDate, dir);
14692                 } else if (e.shiftKey){
14693                     newDate = this.moveMonth(this.date, dir);
14694                     newViewDate = this.moveMonth(this.viewDate, dir);
14695                 } else {
14696                     newDate = new Date(this.date);
14697                     newDate.setUTCDate(this.date.getUTCDate() + dir);
14698                     newViewDate = new Date(this.viewDate);
14699                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
14700                 }
14701                 if (this.dateWithinRange(newDate)){
14702                     this.date = newDate;
14703                     this.viewDate = newViewDate;
14704                     this.setValue(this.formatDate(this.date));
14705 //                    this.update();
14706                     e.preventDefault();
14707                     dateChanged = true;
14708                 }
14709                 break;
14710             case 38: // up
14711             case 40: // down
14712                 if (!this.keyboardNavigation) break;
14713                 dir = e.keyCode == 38 ? -1 : 1;
14714                 if (e.ctrlKey){
14715                     newDate = this.moveYear(this.date, dir);
14716                     newViewDate = this.moveYear(this.viewDate, dir);
14717                 } else if (e.shiftKey){
14718                     newDate = this.moveMonth(this.date, dir);
14719                     newViewDate = this.moveMonth(this.viewDate, dir);
14720                 } else {
14721                     newDate = new Date(this.date);
14722                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
14723                     newViewDate = new Date(this.viewDate);
14724                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
14725                 }
14726                 if (this.dateWithinRange(newDate)){
14727                     this.date = newDate;
14728                     this.viewDate = newViewDate;
14729                     this.setValue(this.formatDate(this.date));
14730 //                    this.update();
14731                     e.preventDefault();
14732                     dateChanged = true;
14733                 }
14734                 break;
14735             case 13: // enter
14736                 this.setValue(this.formatDate(this.date));
14737                 this.hide();
14738                 e.preventDefault();
14739                 break;
14740             case 9: // tab
14741                 this.setValue(this.formatDate(this.date));
14742                 this.hide();
14743                 break;
14744             case 16: // shift
14745             case 17: // ctrl
14746             case 18: // alt
14747                 break;
14748             default :
14749                 this.hide();
14750                 
14751         }
14752     },
14753     
14754     
14755     onClick: function(e) 
14756     {
14757         e.stopPropagation();
14758         e.preventDefault();
14759         
14760         var target = e.getTarget();
14761         
14762         if(target.nodeName.toLowerCase() === 'i'){
14763             target = Roo.get(target).dom.parentNode;
14764         }
14765         
14766         var nodeName = target.nodeName;
14767         var className = target.className;
14768         var html = target.innerHTML;
14769         
14770         switch(nodeName.toLowerCase()) {
14771             case 'th':
14772                 switch(className) {
14773                     case 'switch':
14774                         this.showMode(1);
14775                         break;
14776                     case 'prev':
14777                     case 'next':
14778                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
14779                         switch(this.viewMode){
14780                                 case 0:
14781                                         this.viewDate = this.moveMonth(this.viewDate, dir);
14782                                         break;
14783                                 case 1:
14784                                 case 2:
14785                                         this.viewDate = this.moveYear(this.viewDate, dir);
14786                                         break;
14787                         }
14788                         this.fill();
14789                         break;
14790                     case 'today':
14791                         var date = new Date();
14792                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
14793 //                        this.fill()
14794                         this.setValue(this.formatDate(this.date));
14795                         
14796                         this.hide();
14797                         break;
14798                 }
14799                 break;
14800             case 'span':
14801                 if (className.indexOf('disabled') === -1) {
14802                     this.viewDate.setUTCDate(1);
14803                     if (className.indexOf('month') !== -1) {
14804                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
14805                     } else {
14806                         var year = parseInt(html, 10) || 0;
14807                         this.viewDate.setUTCFullYear(year);
14808                         
14809                     }
14810                     this.showMode(-1);
14811                     this.fill();
14812                 }
14813                 break;
14814                 
14815             case 'td':
14816                 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
14817                     var day = parseInt(html, 10) || 1;
14818                     var year = this.viewDate.getUTCFullYear(),
14819                         month = this.viewDate.getUTCMonth();
14820
14821                     if (className.indexOf('old') !== -1) {
14822                         if(month === 0 ){
14823                             month = 11;
14824                             year -= 1;
14825                         }else{
14826                             month -= 1;
14827                         }
14828                     } else if (className.indexOf('new') !== -1) {
14829                         if (month == 11) {
14830                             month = 0;
14831                             year += 1;
14832                         } else {
14833                             month += 1;
14834                         }
14835                     }
14836                     this.date = this.UTCDate(year, month, day,0,0,0,0);
14837                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
14838 //                    this.fill();
14839                     this.setValue(this.formatDate(this.date));
14840                     this.hide();
14841                 }
14842                 break;
14843         }
14844     },
14845     
14846     setStartDate: function(startDate)
14847     {
14848         this.startDate = startDate || -Infinity;
14849         if (this.startDate !== -Infinity) {
14850             this.startDate = this.parseDate(this.startDate);
14851         }
14852         this.update();
14853         this.updateNavArrows();
14854     },
14855
14856     setEndDate: function(endDate)
14857     {
14858         this.endDate = endDate || Infinity;
14859         if (this.endDate !== Infinity) {
14860             this.endDate = this.parseDate(this.endDate);
14861         }
14862         this.update();
14863         this.updateNavArrows();
14864     },
14865     
14866     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
14867     {
14868         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
14869         if (typeof(this.daysOfWeekDisabled) !== 'object') {
14870             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
14871         }
14872         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
14873             return parseInt(d, 10);
14874         });
14875         this.update();
14876         this.updateNavArrows();
14877     },
14878     
14879     updateNavArrows: function() 
14880     {
14881         var d = new Date(this.viewDate),
14882         year = d.getUTCFullYear(),
14883         month = d.getUTCMonth();
14884         
14885         Roo.each(this.picker().select('.prev', true).elements, function(v){
14886             v.show();
14887             switch (this.viewMode) {
14888                 case 0:
14889
14890                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
14891                         v.hide();
14892                     }
14893                     break;
14894                 case 1:
14895                 case 2:
14896                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
14897                         v.hide();
14898                     }
14899                     break;
14900             }
14901         });
14902         
14903         Roo.each(this.picker().select('.next', true).elements, function(v){
14904             v.show();
14905             switch (this.viewMode) {
14906                 case 0:
14907
14908                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
14909                         v.hide();
14910                     }
14911                     break;
14912                 case 1:
14913                 case 2:
14914                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
14915                         v.hide();
14916                     }
14917                     break;
14918             }
14919         })
14920     },
14921     
14922     moveMonth: function(date, dir)
14923     {
14924         if (!dir) return date;
14925         var new_date = new Date(date.valueOf()),
14926         day = new_date.getUTCDate(),
14927         month = new_date.getUTCMonth(),
14928         mag = Math.abs(dir),
14929         new_month, test;
14930         dir = dir > 0 ? 1 : -1;
14931         if (mag == 1){
14932             test = dir == -1
14933             // If going back one month, make sure month is not current month
14934             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
14935             ? function(){
14936                 return new_date.getUTCMonth() == month;
14937             }
14938             // If going forward one month, make sure month is as expected
14939             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
14940             : function(){
14941                 return new_date.getUTCMonth() != new_month;
14942             };
14943             new_month = month + dir;
14944             new_date.setUTCMonth(new_month);
14945             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
14946             if (new_month < 0 || new_month > 11)
14947                 new_month = (new_month + 12) % 12;
14948         } else {
14949             // For magnitudes >1, move one month at a time...
14950             for (var i=0; i<mag; i++)
14951                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
14952                 new_date = this.moveMonth(new_date, dir);
14953             // ...then reset the day, keeping it in the new month
14954             new_month = new_date.getUTCMonth();
14955             new_date.setUTCDate(day);
14956             test = function(){
14957                 return new_month != new_date.getUTCMonth();
14958             };
14959         }
14960         // Common date-resetting loop -- if date is beyond end of month, make it
14961         // end of month
14962         while (test()){
14963             new_date.setUTCDate(--day);
14964             new_date.setUTCMonth(new_month);
14965         }
14966         return new_date;
14967     },
14968
14969     moveYear: function(date, dir)
14970     {
14971         return this.moveMonth(date, dir*12);
14972     },
14973
14974     dateWithinRange: function(date)
14975     {
14976         return date >= this.startDate && date <= this.endDate;
14977     },
14978
14979     
14980     remove: function() 
14981     {
14982         this.picker().remove();
14983     }
14984    
14985 });
14986
14987 Roo.apply(Roo.bootstrap.DateField,  {
14988     
14989     head : {
14990         tag: 'thead',
14991         cn: [
14992         {
14993             tag: 'tr',
14994             cn: [
14995             {
14996                 tag: 'th',
14997                 cls: 'prev',
14998                 html: '<i class="fa fa-arrow-left"/>'
14999             },
15000             {
15001                 tag: 'th',
15002                 cls: 'switch',
15003                 colspan: '5'
15004             },
15005             {
15006                 tag: 'th',
15007                 cls: 'next',
15008                 html: '<i class="fa fa-arrow-right"/>'
15009             }
15010
15011             ]
15012         }
15013         ]
15014     },
15015     
15016     content : {
15017         tag: 'tbody',
15018         cn: [
15019         {
15020             tag: 'tr',
15021             cn: [
15022             {
15023                 tag: 'td',
15024                 colspan: '7'
15025             }
15026             ]
15027         }
15028         ]
15029     },
15030     
15031     footer : {
15032         tag: 'tfoot',
15033         cn: [
15034         {
15035             tag: 'tr',
15036             cn: [
15037             {
15038                 tag: 'th',
15039                 colspan: '7',
15040                 cls: 'today'
15041             }
15042                     
15043             ]
15044         }
15045         ]
15046     },
15047     
15048     dates:{
15049         en: {
15050             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
15051             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
15052             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
15053             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
15054             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
15055             today: "Today"
15056         }
15057     },
15058     
15059     modes: [
15060     {
15061         clsName: 'days',
15062         navFnc: 'Month',
15063         navStep: 1
15064     },
15065     {
15066         clsName: 'months',
15067         navFnc: 'FullYear',
15068         navStep: 1
15069     },
15070     {
15071         clsName: 'years',
15072         navFnc: 'FullYear',
15073         navStep: 10
15074     }]
15075 });
15076
15077 Roo.apply(Roo.bootstrap.DateField,  {
15078   
15079     template : {
15080         tag: 'div',
15081         cls: 'datepicker dropdown-menu',
15082         cn: [
15083         {
15084             tag: 'div',
15085             cls: 'datepicker-days',
15086             cn: [
15087             {
15088                 tag: 'table',
15089                 cls: 'table-condensed',
15090                 cn:[
15091                 Roo.bootstrap.DateField.head,
15092                 {
15093                     tag: 'tbody'
15094                 },
15095                 Roo.bootstrap.DateField.footer
15096                 ]
15097             }
15098             ]
15099         },
15100         {
15101             tag: 'div',
15102             cls: 'datepicker-months',
15103             cn: [
15104             {
15105                 tag: 'table',
15106                 cls: 'table-condensed',
15107                 cn:[
15108                 Roo.bootstrap.DateField.head,
15109                 Roo.bootstrap.DateField.content,
15110                 Roo.bootstrap.DateField.footer
15111                 ]
15112             }
15113             ]
15114         },
15115         {
15116             tag: 'div',
15117             cls: 'datepicker-years',
15118             cn: [
15119             {
15120                 tag: 'table',
15121                 cls: 'table-condensed',
15122                 cn:[
15123                 Roo.bootstrap.DateField.head,
15124                 Roo.bootstrap.DateField.content,
15125                 Roo.bootstrap.DateField.footer
15126                 ]
15127             }
15128             ]
15129         }
15130         ]
15131     }
15132 });
15133
15134  
15135
15136  /*
15137  * - LGPL
15138  *
15139  * TimeField
15140  * 
15141  */
15142
15143 /**
15144  * @class Roo.bootstrap.TimeField
15145  * @extends Roo.bootstrap.Input
15146  * Bootstrap DateField class
15147  * 
15148  * 
15149  * @constructor
15150  * Create a new TimeField
15151  * @param {Object} config The config object
15152  */
15153
15154 Roo.bootstrap.TimeField = function(config){
15155     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
15156     this.addEvents({
15157             /**
15158              * @event show
15159              * Fires when this field show.
15160              * @param {Roo.bootstrap.DateField} this
15161              * @param {Mixed} date The date value
15162              */
15163             show : true,
15164             /**
15165              * @event show
15166              * Fires when this field hide.
15167              * @param {Roo.bootstrap.DateField} this
15168              * @param {Mixed} date The date value
15169              */
15170             hide : true,
15171             /**
15172              * @event select
15173              * Fires when select a date.
15174              * @param {Roo.bootstrap.DateField} this
15175              * @param {Mixed} date The date value
15176              */
15177             select : true
15178         });
15179 };
15180
15181 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
15182     
15183     /**
15184      * @cfg {String} format
15185      * The default time format string which can be overriden for localization support.  The format must be
15186      * valid according to {@link Date#parseDate} (defaults to 'H:i').
15187      */
15188     format : "H:i",
15189        
15190     onRender: function(ct, position)
15191     {
15192         
15193         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
15194                 
15195         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
15196         
15197         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15198         
15199         this.pop = this.picker().select('>.datepicker-time',true).first();
15200         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block' 
15201         
15202         this.picker().on('mousedown', this.onMousedown, this);
15203         this.picker().on('click', this.onClick, this);
15204         
15205         this.picker().addClass('datepicker-dropdown');
15206     
15207         this.fillTime();
15208         this.update();
15209             
15210         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
15211         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
15212         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
15213         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
15214         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
15215         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
15216
15217     },
15218     
15219     fireKey: function(e){
15220         if (!this.picker().isVisible()){
15221             if (e.keyCode == 27) // allow escape to hide and re-show picker
15222                 this.show();
15223             return;
15224         }
15225
15226         e.preventDefault();
15227         
15228         switch(e.keyCode){
15229             case 27: // escape
15230                 this.hide();
15231                 break;
15232             case 37: // left
15233             case 39: // right
15234                 this.onTogglePeriod();
15235                 break;
15236             case 38: // up
15237                 this.onIncrementMinutes();
15238                 break;
15239             case 40: // down
15240                 this.onDecrementMinutes();
15241                 break;
15242             case 13: // enter
15243             case 9: // tab
15244                 this.setTime();
15245                 break;
15246         }
15247     },
15248     
15249     onClick: function(e) {
15250         e.stopPropagation();
15251         e.preventDefault();
15252     },
15253     
15254     picker : function()
15255     {
15256         return this.el.select('.datepicker', true).first();
15257     },
15258     
15259     fillTime: function()
15260     {    
15261         var time = this.pop.select('tbody', true).first();
15262         
15263         time.dom.innerHTML = '';
15264         
15265         time.createChild({
15266             tag: 'tr',
15267             cn: [
15268                 {
15269                     tag: 'td',
15270                     cn: [
15271                         {
15272                             tag: 'a',
15273                             href: '#',
15274                             cls: 'btn',
15275                             cn: [
15276                                 {
15277                                     tag: 'span',
15278                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
15279                                 }
15280                             ]
15281                         } 
15282                     ]
15283                 },
15284                 {
15285                     tag: 'td',
15286                     cls: 'separator'
15287                 },
15288                 {
15289                     tag: 'td',
15290                     cn: [
15291                         {
15292                             tag: 'a',
15293                             href: '#',
15294                             cls: 'btn',
15295                             cn: [
15296                                 {
15297                                     tag: 'span',
15298                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
15299                                 }
15300                             ]
15301                         }
15302                     ]
15303                 },
15304                 {
15305                     tag: 'td',
15306                     cls: 'separator'
15307                 }
15308             ]
15309         });
15310         
15311         time.createChild({
15312             tag: 'tr',
15313             cn: [
15314                 {
15315                     tag: 'td',
15316                     cn: [
15317                         {
15318                             tag: 'span',
15319                             cls: 'timepicker-hour',
15320                             html: '00'
15321                         }  
15322                     ]
15323                 },
15324                 {
15325                     tag: 'td',
15326                     cls: 'separator',
15327                     html: ':'
15328                 },
15329                 {
15330                     tag: 'td',
15331                     cn: [
15332                         {
15333                             tag: 'span',
15334                             cls: 'timepicker-minute',
15335                             html: '00'
15336                         }  
15337                     ]
15338                 },
15339                 {
15340                     tag: 'td',
15341                     cls: 'separator'
15342                 },
15343                 {
15344                     tag: 'td',
15345                     cn: [
15346                         {
15347                             tag: 'button',
15348                             type: 'button',
15349                             cls: 'btn btn-primary period',
15350                             html: 'AM'
15351                             
15352                         }
15353                     ]
15354                 }
15355             ]
15356         });
15357         
15358         time.createChild({
15359             tag: 'tr',
15360             cn: [
15361                 {
15362                     tag: 'td',
15363                     cn: [
15364                         {
15365                             tag: 'a',
15366                             href: '#',
15367                             cls: 'btn',
15368                             cn: [
15369                                 {
15370                                     tag: 'span',
15371                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
15372                                 }
15373                             ]
15374                         }
15375                     ]
15376                 },
15377                 {
15378                     tag: 'td',
15379                     cls: 'separator'
15380                 },
15381                 {
15382                     tag: 'td',
15383                     cn: [
15384                         {
15385                             tag: 'a',
15386                             href: '#',
15387                             cls: 'btn',
15388                             cn: [
15389                                 {
15390                                     tag: 'span',
15391                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
15392                                 }
15393                             ]
15394                         }
15395                     ]
15396                 },
15397                 {
15398                     tag: 'td',
15399                     cls: 'separator'
15400                 }
15401             ]
15402         });
15403         
15404     },
15405     
15406     update: function()
15407     {
15408         
15409         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
15410         
15411         this.fill();
15412     },
15413     
15414     fill: function() 
15415     {
15416         var hours = this.time.getHours();
15417         var minutes = this.time.getMinutes();
15418         var period = 'AM';
15419         
15420         if(hours > 11){
15421             period = 'PM';
15422         }
15423         
15424         if(hours == 0){
15425             hours = 12;
15426         }
15427         
15428         
15429         if(hours > 12){
15430             hours = hours - 12;
15431         }
15432         
15433         if(hours < 10){
15434             hours = '0' + hours;
15435         }
15436         
15437         if(minutes < 10){
15438             minutes = '0' + minutes;
15439         }
15440         
15441         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
15442         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
15443         this.pop.select('button', true).first().dom.innerHTML = period;
15444         
15445     },
15446     
15447     place: function()
15448     {   
15449         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
15450         
15451         var cls = ['bottom'];
15452         
15453         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
15454             cls.pop();
15455             cls.push('top');
15456         }
15457         
15458         cls.push('right');
15459         
15460         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
15461             cls.pop();
15462             cls.push('left');
15463         }
15464         
15465         this.picker().addClass(cls.join('-'));
15466         
15467         var _this = this;
15468         
15469         Roo.each(cls, function(c){
15470             if(c == 'bottom'){
15471                 _this.picker().setTop(_this.inputEl().getHeight());
15472                 return;
15473             }
15474             if(c == 'top'){
15475                 _this.picker().setTop(0 - _this.picker().getHeight());
15476                 return;
15477             }
15478             
15479             if(c == 'left'){
15480                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
15481                 return;
15482             }
15483             if(c == 'right'){
15484                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
15485                 return;
15486             }
15487         });
15488         
15489     },
15490   
15491     onFocus : function()
15492     {
15493         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
15494         this.show();
15495     },
15496     
15497     onBlur : function()
15498     {
15499         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
15500         this.hide();
15501     },
15502     
15503     show : function()
15504     {
15505         this.picker().show();
15506         this.pop.show();
15507         this.update();
15508         this.place();
15509         
15510         this.fireEvent('show', this, this.date);
15511     },
15512     
15513     hide : function()
15514     {
15515         this.picker().hide();
15516         this.pop.hide();
15517         
15518         this.fireEvent('hide', this, this.date);
15519     },
15520     
15521     setTime : function()
15522     {
15523         this.hide();
15524         this.setValue(this.time.format(this.format));
15525         
15526         this.fireEvent('select', this, this.date);
15527         
15528         
15529     },
15530     
15531     onMousedown: function(e){
15532         e.stopPropagation();
15533         e.preventDefault();
15534     },
15535     
15536     onIncrementHours: function()
15537     {
15538         Roo.log('onIncrementHours');
15539         this.time = this.time.add(Date.HOUR, 1);
15540         this.update();
15541         
15542     },
15543     
15544     onDecrementHours: function()
15545     {
15546         Roo.log('onDecrementHours');
15547         this.time = this.time.add(Date.HOUR, -1);
15548         this.update();
15549     },
15550     
15551     onIncrementMinutes: function()
15552     {
15553         Roo.log('onIncrementMinutes');
15554         this.time = this.time.add(Date.MINUTE, 1);
15555         this.update();
15556     },
15557     
15558     onDecrementMinutes: function()
15559     {
15560         Roo.log('onDecrementMinutes');
15561         this.time = this.time.add(Date.MINUTE, -1);
15562         this.update();
15563     },
15564     
15565     onTogglePeriod: function()
15566     {
15567         Roo.log('onTogglePeriod');
15568         this.time = this.time.add(Date.HOUR, 12);
15569         this.update();
15570     }
15571     
15572    
15573 });
15574
15575 Roo.apply(Roo.bootstrap.TimeField,  {
15576     
15577     content : {
15578         tag: 'tbody',
15579         cn: [
15580             {
15581                 tag: 'tr',
15582                 cn: [
15583                 {
15584                     tag: 'td',
15585                     colspan: '7'
15586                 }
15587                 ]
15588             }
15589         ]
15590     },
15591     
15592     footer : {
15593         tag: 'tfoot',
15594         cn: [
15595             {
15596                 tag: 'tr',
15597                 cn: [
15598                 {
15599                     tag: 'th',
15600                     colspan: '7',
15601                     cls: '',
15602                     cn: [
15603                         {
15604                             tag: 'button',
15605                             cls: 'btn btn-info ok',
15606                             html: 'OK'
15607                         }
15608                     ]
15609                 }
15610
15611                 ]
15612             }
15613         ]
15614     }
15615 });
15616
15617 Roo.apply(Roo.bootstrap.TimeField,  {
15618   
15619     template : {
15620         tag: 'div',
15621         cls: 'datepicker dropdown-menu',
15622         cn: [
15623             {
15624                 tag: 'div',
15625                 cls: 'datepicker-time',
15626                 cn: [
15627                 {
15628                     tag: 'table',
15629                     cls: 'table-condensed',
15630                     cn:[
15631                     Roo.bootstrap.TimeField.content,
15632                     Roo.bootstrap.TimeField.footer
15633                     ]
15634                 }
15635                 ]
15636             }
15637         ]
15638     }
15639 });
15640
15641  
15642
15643  /*
15644  * - LGPL
15645  *
15646  * CheckBox
15647  * 
15648  */
15649
15650 /**
15651  * @class Roo.bootstrap.CheckBox
15652  * @extends Roo.bootstrap.Input
15653  * Bootstrap CheckBox class
15654  * 
15655  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
15656  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
15657  * @cfg {String} boxLabel The text that appears beside the checkbox
15658  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
15659  * @cfg {Boolean} checked initnal the element
15660  * 
15661  * 
15662  * @constructor
15663  * Create a new CheckBox
15664  * @param {Object} config The config object
15665  */
15666
15667 Roo.bootstrap.CheckBox = function(config){
15668     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
15669    
15670         this.addEvents({
15671             /**
15672             * @event check
15673             * Fires when the element is checked or unchecked.
15674             * @param {Roo.bootstrap.CheckBox} this This input
15675             * @param {Boolean} checked The new checked value
15676             */
15677            check : true
15678         });
15679 };
15680
15681 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
15682     
15683     inputType: 'checkbox',
15684     inputValue: 1,
15685     valueOff: 0,
15686     boxLabel: false,
15687     checked: false,
15688     weight : false,
15689     
15690     getAutoCreate : function()
15691     {
15692         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
15693         
15694         var id = Roo.id();
15695         
15696         var cfg = {};
15697         
15698         cfg.cls = 'form-group checkbox' //input-group
15699         
15700         
15701         
15702         
15703         var input =  {
15704             tag: 'input',
15705             id : id,
15706             type : this.inputType,
15707             value : (!this.checked) ? this.valueOff : this.inputValue,
15708             cls : 'roo-checkbox', //'form-box',
15709             placeholder : this.placeholder || ''
15710             
15711         };
15712         
15713         if (this.weight) { // Validity check?
15714             cfg.cls += " checkbox-" + this.weight;
15715         }
15716         
15717         if (this.disabled) {
15718             input.disabled=true;
15719         }
15720         
15721         if(this.checked){
15722             input.checked = this.checked;
15723         }
15724         
15725         if (this.name) {
15726             input.name = this.name;
15727         }
15728         
15729         if (this.size) {
15730             input.cls += ' input-' + this.size;
15731         }
15732         
15733         var settings=this;
15734         ['xs','sm','md','lg'].map(function(size){
15735             if (settings[size]) {
15736                 cfg.cls += ' col-' + size + '-' + settings[size];
15737             }
15738         });
15739         
15740        
15741         
15742         var inputblock = input;
15743         
15744         
15745         
15746         
15747         if (this.before || this.after) {
15748             
15749             inputblock = {
15750                 cls : 'input-group',
15751                 cn :  [] 
15752             };
15753             if (this.before) {
15754                 inputblock.cn.push({
15755                     tag :'span',
15756                     cls : 'input-group-addon',
15757                     html : this.before
15758                 });
15759             }
15760             inputblock.cn.push(input);
15761             if (this.after) {
15762                 inputblock.cn.push({
15763                     tag :'span',
15764                     cls : 'input-group-addon',
15765                     html : this.after
15766                 });
15767             }
15768             
15769         };
15770         
15771         if (align ==='left' && this.fieldLabel.length) {
15772                 Roo.log("left and has label");
15773                 cfg.cn = [
15774                     
15775                     {
15776                         tag: 'label',
15777                         'for' :  id,
15778                         cls : 'control-label col-md-' + this.labelWidth,
15779                         html : this.fieldLabel
15780                         
15781                     },
15782                     {
15783                         cls : "col-md-" + (12 - this.labelWidth), 
15784                         cn: [
15785                             inputblock
15786                         ]
15787                     }
15788                     
15789                 ];
15790         } else if ( this.fieldLabel.length) {
15791                 Roo.log(" label");
15792                 cfg.cn = [
15793                    
15794                     {
15795                         tag: this.boxLabel ? 'span' : 'label',
15796                         'for': id,
15797                         cls: 'control-label box-input-label',
15798                         //cls : 'input-group-addon',
15799                         html : this.fieldLabel
15800                         
15801                     },
15802                     
15803                     inputblock
15804                     
15805                 ];
15806
15807         } else {
15808             
15809                 Roo.log(" no label && no align");
15810                 cfg.cn = [  inputblock ] ;
15811                 
15812                 
15813         };
15814          if(this.boxLabel){
15815             cfg.cn.push( {
15816                 tag: 'label',
15817                 'for': id,
15818                 cls: 'box-label',
15819                 html: this.boxLabel
15820                 
15821             });
15822         }
15823         
15824         
15825        
15826         return cfg;
15827         
15828     },
15829     
15830     /**
15831      * return the real input element.
15832      */
15833     inputEl: function ()
15834     {
15835         return this.el.select('input.roo-checkbox',true).first();
15836     },
15837     
15838     label: function()
15839     {
15840         return this.el.select('label.control-label',true).first();
15841     },
15842     
15843     initEvents : function()
15844     {
15845 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
15846         
15847         this.inputEl().on('click', this.onClick,  this);
15848         
15849     },
15850     
15851     onClick : function()
15852     {   
15853         this.setChecked(!this.checked);
15854     },
15855     
15856     setChecked : function(state,suppressEvent)
15857     {
15858         this.checked = state;
15859         
15860         this.inputEl().dom.checked = state;
15861         
15862         if(suppressEvent !== true){
15863             this.fireEvent('check', this, state);
15864         }
15865         
15866         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
15867         
15868     },
15869     
15870     setValue : function(v,suppressEvent)
15871     {
15872         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
15873     }
15874     
15875 });
15876
15877  
15878 /*
15879  * - LGPL
15880  *
15881  * Radio
15882  * 
15883  */
15884
15885 /**
15886  * @class Roo.bootstrap.Radio
15887  * @extends Roo.bootstrap.CheckBox
15888  * Bootstrap Radio class
15889
15890  * @constructor
15891  * Create a new Radio
15892  * @param {Object} config The config object
15893  */
15894
15895 Roo.bootstrap.Radio = function(config){
15896     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
15897    
15898 };
15899
15900 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
15901     
15902     inputType: 'radio',
15903     inputValue: '',
15904     valueOff: '',
15905     
15906     getAutoCreate : function()
15907     {
15908         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
15909         
15910         var id = Roo.id();
15911         
15912         var cfg = {};
15913         
15914         cfg.cls = 'form-group radio' //input-group
15915         
15916         var input =  {
15917             tag: 'input',
15918             id : id,
15919             type : this.inputType,
15920             value : (!this.checked) ? this.valueOff : this.inputValue,
15921             cls : 'roo-radio',
15922             placeholder : this.placeholder || ''
15923             
15924         };
15925           if (this.weight) { // Validity check?
15926             cfg.cls += " radio-" + this.weight;
15927         }
15928         if (this.disabled) {
15929             input.disabled=true;
15930         }
15931         
15932         if(this.checked){
15933             input.checked = this.checked;
15934         }
15935         
15936         if (this.name) {
15937             input.name = this.name;
15938         }
15939         
15940         if (this.size) {
15941             input.cls += ' input-' + this.size;
15942         }
15943         
15944         var settings=this;
15945         ['xs','sm','md','lg'].map(function(size){
15946             if (settings[size]) {
15947                 cfg.cls += ' col-' + size + '-' + settings[size];
15948             }
15949         });
15950         
15951         var inputblock = input;
15952         
15953         if (this.before || this.after) {
15954             
15955             inputblock = {
15956                 cls : 'input-group',
15957                 cn :  [] 
15958             };
15959             if (this.before) {
15960                 inputblock.cn.push({
15961                     tag :'span',
15962                     cls : 'input-group-addon',
15963                     html : this.before
15964                 });
15965             }
15966             inputblock.cn.push(input);
15967             if (this.after) {
15968                 inputblock.cn.push({
15969                     tag :'span',
15970                     cls : 'input-group-addon',
15971                     html : this.after
15972                 });
15973             }
15974             
15975         };
15976         
15977         if (align ==='left' && this.fieldLabel.length) {
15978                 Roo.log("left and has label");
15979                 cfg.cn = [
15980                     
15981                     {
15982                         tag: 'label',
15983                         'for' :  id,
15984                         cls : 'control-label col-md-' + this.labelWidth,
15985                         html : this.fieldLabel
15986                         
15987                     },
15988                     {
15989                         cls : "col-md-" + (12 - this.labelWidth), 
15990                         cn: [
15991                             inputblock
15992                         ]
15993                     }
15994                     
15995                 ];
15996         } else if ( this.fieldLabel.length) {
15997                 Roo.log(" label");
15998                  cfg.cn = [
15999                    
16000                     {
16001                         tag: 'label',
16002                         'for': id,
16003                         cls: 'control-label box-input-label',
16004                         //cls : 'input-group-addon',
16005                         html : this.fieldLabel
16006                         
16007                     },
16008                     
16009                     inputblock
16010                     
16011                 ];
16012
16013         } else {
16014             
16015                    Roo.log(" no label && no align");
16016                 cfg.cn = [
16017                     
16018                         inputblock
16019                     
16020                 ];
16021                 
16022                 
16023         };
16024         
16025         if(this.boxLabel){
16026             cfg.cn.push({
16027                 tag: 'label',
16028                 'for': id,
16029                 cls: 'box-label',
16030                 html: this.boxLabel
16031             })
16032         }
16033         
16034         return cfg;
16035         
16036     },
16037     inputEl: function ()
16038     {
16039         return this.el.select('input.roo-radio',true).first();
16040     },
16041     onClick : function()
16042     {   
16043         this.setChecked(true);
16044     },
16045     
16046     setChecked : function(state,suppressEvent)
16047     {
16048         if(state){
16049             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
16050                 v.dom.checked = false;
16051             });
16052         }
16053         
16054         this.checked = state;
16055         this.inputEl().dom.checked = state;
16056         
16057         if(suppressEvent !== true){
16058             this.fireEvent('check', this, state);
16059         }
16060         
16061         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
16062         
16063     },
16064     
16065     getGroupValue : function()
16066     {
16067         var value = ''
16068         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
16069             if(v.dom.checked == true){
16070                 value = v.dom.value;
16071             }
16072         });
16073         
16074         return value;
16075     },
16076     
16077     /**
16078      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
16079      * @return {Mixed} value The field value
16080      */
16081     getValue : function(){
16082         return this.getGroupValue();
16083     }
16084     
16085 });
16086
16087  
16088 //<script type="text/javascript">
16089
16090 /*
16091  * Based  Ext JS Library 1.1.1
16092  * Copyright(c) 2006-2007, Ext JS, LLC.
16093  * LGPL
16094  *
16095  */
16096  
16097 /**
16098  * @class Roo.HtmlEditorCore
16099  * @extends Roo.Component
16100  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
16101  *
16102  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
16103  */
16104
16105 Roo.HtmlEditorCore = function(config){
16106     
16107     
16108     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
16109     this.addEvents({
16110         /**
16111          * @event initialize
16112          * Fires when the editor is fully initialized (including the iframe)
16113          * @param {Roo.HtmlEditorCore} this
16114          */
16115         initialize: true,
16116         /**
16117          * @event activate
16118          * Fires when the editor is first receives the focus. Any insertion must wait
16119          * until after this event.
16120          * @param {Roo.HtmlEditorCore} this
16121          */
16122         activate: true,
16123          /**
16124          * @event beforesync
16125          * Fires before the textarea is updated with content from the editor iframe. Return false
16126          * to cancel the sync.
16127          * @param {Roo.HtmlEditorCore} this
16128          * @param {String} html
16129          */
16130         beforesync: true,
16131          /**
16132          * @event beforepush
16133          * Fires before the iframe editor is updated with content from the textarea. Return false
16134          * to cancel the push.
16135          * @param {Roo.HtmlEditorCore} this
16136          * @param {String} html
16137          */
16138         beforepush: true,
16139          /**
16140          * @event sync
16141          * Fires when the textarea is updated with content from the editor iframe.
16142          * @param {Roo.HtmlEditorCore} this
16143          * @param {String} html
16144          */
16145         sync: true,
16146          /**
16147          * @event push
16148          * Fires when the iframe editor is updated with content from the textarea.
16149          * @param {Roo.HtmlEditorCore} this
16150          * @param {String} html
16151          */
16152         push: true,
16153         
16154         /**
16155          * @event editorevent
16156          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
16157          * @param {Roo.HtmlEditorCore} this
16158          */
16159         editorevent: true
16160     });
16161      
16162 };
16163
16164
16165 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
16166
16167
16168      /**
16169      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
16170      */
16171     
16172     owner : false,
16173     
16174      /**
16175      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
16176      *                        Roo.resizable.
16177      */
16178     resizable : false,
16179      /**
16180      * @cfg {Number} height (in pixels)
16181      */   
16182     height: 300,
16183    /**
16184      * @cfg {Number} width (in pixels)
16185      */   
16186     width: 500,
16187     
16188     /**
16189      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
16190      * 
16191      */
16192     stylesheets: false,
16193     
16194     // id of frame..
16195     frameId: false,
16196     
16197     // private properties
16198     validationEvent : false,
16199     deferHeight: true,
16200     initialized : false,
16201     activated : false,
16202     sourceEditMode : false,
16203     onFocus : Roo.emptyFn,
16204     iframePad:3,
16205     hideMode:'offsets',
16206     
16207     clearUp: true,
16208     
16209      
16210     
16211
16212     /**
16213      * Protected method that will not generally be called directly. It
16214      * is called when the editor initializes the iframe with HTML contents. Override this method if you
16215      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
16216      */
16217     getDocMarkup : function(){
16218         // body styles..
16219         var st = '';
16220         Roo.log(this.stylesheets);
16221         
16222         // inherit styels from page...?? 
16223         if (this.stylesheets === false) {
16224             
16225             Roo.get(document.head).select('style').each(function(node) {
16226                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16227             });
16228             
16229             Roo.get(document.head).select('link').each(function(node) { 
16230                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16231             });
16232             
16233         } else if (!this.stylesheets.length) {
16234                 // simple..
16235                 st = '<style type="text/css">' +
16236                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16237                    '</style>';
16238         } else {
16239             Roo.each(this.stylesheets, function(s) {
16240                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
16241             });
16242             
16243         }
16244         
16245         st +=  '<style type="text/css">' +
16246             'IMG { cursor: pointer } ' +
16247         '</style>';
16248
16249         
16250         return '<html><head>' + st  +
16251             //<style type="text/css">' +
16252             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16253             //'</style>' +
16254             ' </head><body class="roo-htmleditor-body"></body></html>';
16255     },
16256
16257     // private
16258     onRender : function(ct, position)
16259     {
16260         var _t = this;
16261         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
16262         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
16263         
16264         
16265         this.el.dom.style.border = '0 none';
16266         this.el.dom.setAttribute('tabIndex', -1);
16267         this.el.addClass('x-hidden hide');
16268         
16269         
16270         
16271         if(Roo.isIE){ // fix IE 1px bogus margin
16272             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
16273         }
16274        
16275         
16276         this.frameId = Roo.id();
16277         
16278          
16279         
16280         var iframe = this.owner.wrap.createChild({
16281             tag: 'iframe',
16282             cls: 'form-control', // bootstrap..
16283             id: this.frameId,
16284             name: this.frameId,
16285             frameBorder : 'no',
16286             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
16287         }, this.el
16288         );
16289         
16290         
16291         this.iframe = iframe.dom;
16292
16293          this.assignDocWin();
16294         
16295         this.doc.designMode = 'on';
16296        
16297         this.doc.open();
16298         this.doc.write(this.getDocMarkup());
16299         this.doc.close();
16300
16301         
16302         var task = { // must defer to wait for browser to be ready
16303             run : function(){
16304                 //console.log("run task?" + this.doc.readyState);
16305                 this.assignDocWin();
16306                 if(this.doc.body || this.doc.readyState == 'complete'){
16307                     try {
16308                         this.doc.designMode="on";
16309                     } catch (e) {
16310                         return;
16311                     }
16312                     Roo.TaskMgr.stop(task);
16313                     this.initEditor.defer(10, this);
16314                 }
16315             },
16316             interval : 10,
16317             duration: 10000,
16318             scope: this
16319         };
16320         Roo.TaskMgr.start(task);
16321
16322         
16323          
16324     },
16325
16326     // private
16327     onResize : function(w, h)
16328     {
16329          Roo.log('resize: ' +w + ',' + h );
16330         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
16331         if(!this.iframe){
16332             return;
16333         }
16334         if(typeof w == 'number'){
16335             
16336             this.iframe.style.width = w + 'px';
16337         }
16338         if(typeof h == 'number'){
16339             
16340             this.iframe.style.height = h + 'px';
16341             if(this.doc){
16342                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
16343             }
16344         }
16345         
16346     },
16347
16348     /**
16349      * Toggles the editor between standard and source edit mode.
16350      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
16351      */
16352     toggleSourceEdit : function(sourceEditMode){
16353         
16354         this.sourceEditMode = sourceEditMode === true;
16355         
16356         if(this.sourceEditMode){
16357  
16358             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
16359             
16360         }else{
16361             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
16362             //this.iframe.className = '';
16363             this.deferFocus();
16364         }
16365         //this.setSize(this.owner.wrap.getSize());
16366         //this.fireEvent('editmodechange', this, this.sourceEditMode);
16367     },
16368
16369     
16370   
16371
16372     /**
16373      * Protected method that will not generally be called directly. If you need/want
16374      * custom HTML cleanup, this is the method you should override.
16375      * @param {String} html The HTML to be cleaned
16376      * return {String} The cleaned HTML
16377      */
16378     cleanHtml : function(html){
16379         html = String(html);
16380         if(html.length > 5){
16381             if(Roo.isSafari){ // strip safari nonsense
16382                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
16383             }
16384         }
16385         if(html == '&nbsp;'){
16386             html = '';
16387         }
16388         return html;
16389     },
16390
16391     /**
16392      * HTML Editor -> Textarea
16393      * Protected method that will not generally be called directly. Syncs the contents
16394      * of the editor iframe with the textarea.
16395      */
16396     syncValue : function(){
16397         if(this.initialized){
16398             var bd = (this.doc.body || this.doc.documentElement);
16399             //this.cleanUpPaste(); -- this is done else where and causes havoc..
16400             var html = bd.innerHTML;
16401             if(Roo.isSafari){
16402                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
16403                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
16404                 if(m && m[1]){
16405                     html = '<div style="'+m[0]+'">' + html + '</div>';
16406                 }
16407             }
16408             html = this.cleanHtml(html);
16409             // fix up the special chars.. normaly like back quotes in word...
16410             // however we do not want to do this with chinese..
16411             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
16412                 var cc = b.charCodeAt();
16413                 if (
16414                     (cc >= 0x4E00 && cc < 0xA000 ) ||
16415                     (cc >= 0x3400 && cc < 0x4E00 ) ||
16416                     (cc >= 0xf900 && cc < 0xfb00 )
16417                 ) {
16418                         return b;
16419                 }
16420                 return "&#"+cc+";" 
16421             });
16422             if(this.owner.fireEvent('beforesync', this, html) !== false){
16423                 this.el.dom.value = html;
16424                 this.owner.fireEvent('sync', this, html);
16425             }
16426         }
16427     },
16428
16429     /**
16430      * Protected method that will not generally be called directly. Pushes the value of the textarea
16431      * into the iframe editor.
16432      */
16433     pushValue : function(){
16434         if(this.initialized){
16435             var v = this.el.dom.value.trim();
16436             
16437 //            if(v.length < 1){
16438 //                v = '&#160;';
16439 //            }
16440             
16441             if(this.owner.fireEvent('beforepush', this, v) !== false){
16442                 var d = (this.doc.body || this.doc.documentElement);
16443                 d.innerHTML = v;
16444                 this.cleanUpPaste();
16445                 this.el.dom.value = d.innerHTML;
16446                 this.owner.fireEvent('push', this, v);
16447             }
16448         }
16449     },
16450
16451     // private
16452     deferFocus : function(){
16453         this.focus.defer(10, this);
16454     },
16455
16456     // doc'ed in Field
16457     focus : function(){
16458         if(this.win && !this.sourceEditMode){
16459             this.win.focus();
16460         }else{
16461             this.el.focus();
16462         }
16463     },
16464     
16465     assignDocWin: function()
16466     {
16467         var iframe = this.iframe;
16468         
16469          if(Roo.isIE){
16470             this.doc = iframe.contentWindow.document;
16471             this.win = iframe.contentWindow;
16472         } else {
16473             if (!Roo.get(this.frameId)) {
16474                 return;
16475             }
16476             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
16477             this.win = Roo.get(this.frameId).dom.contentWindow;
16478         }
16479     },
16480     
16481     // private
16482     initEditor : function(){
16483         //console.log("INIT EDITOR");
16484         this.assignDocWin();
16485         
16486         
16487         
16488         this.doc.designMode="on";
16489         this.doc.open();
16490         this.doc.write(this.getDocMarkup());
16491         this.doc.close();
16492         
16493         var dbody = (this.doc.body || this.doc.documentElement);
16494         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
16495         // this copies styles from the containing element into thsi one..
16496         // not sure why we need all of this..
16497         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
16498         
16499         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
16500         //ss['background-attachment'] = 'fixed'; // w3c
16501         dbody.bgProperties = 'fixed'; // ie
16502         //Roo.DomHelper.applyStyles(dbody, ss);
16503         Roo.EventManager.on(this.doc, {
16504             //'mousedown': this.onEditorEvent,
16505             'mouseup': this.onEditorEvent,
16506             'dblclick': this.onEditorEvent,
16507             'click': this.onEditorEvent,
16508             'keyup': this.onEditorEvent,
16509             buffer:100,
16510             scope: this
16511         });
16512         if(Roo.isGecko){
16513             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
16514         }
16515         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
16516             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
16517         }
16518         this.initialized = true;
16519
16520         this.owner.fireEvent('initialize', this);
16521         this.pushValue();
16522     },
16523
16524     // private
16525     onDestroy : function(){
16526         
16527         
16528         
16529         if(this.rendered){
16530             
16531             //for (var i =0; i < this.toolbars.length;i++) {
16532             //    // fixme - ask toolbars for heights?
16533             //    this.toolbars[i].onDestroy();
16534            // }
16535             
16536             //this.wrap.dom.innerHTML = '';
16537             //this.wrap.remove();
16538         }
16539     },
16540
16541     // private
16542     onFirstFocus : function(){
16543         
16544         this.assignDocWin();
16545         
16546         
16547         this.activated = true;
16548          
16549     
16550         if(Roo.isGecko){ // prevent silly gecko errors
16551             this.win.focus();
16552             var s = this.win.getSelection();
16553             if(!s.focusNode || s.focusNode.nodeType != 3){
16554                 var r = s.getRangeAt(0);
16555                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
16556                 r.collapse(true);
16557                 this.deferFocus();
16558             }
16559             try{
16560                 this.execCmd('useCSS', true);
16561                 this.execCmd('styleWithCSS', false);
16562             }catch(e){}
16563         }
16564         this.owner.fireEvent('activate', this);
16565     },
16566
16567     // private
16568     adjustFont: function(btn){
16569         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
16570         //if(Roo.isSafari){ // safari
16571         //    adjust *= 2;
16572        // }
16573         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
16574         if(Roo.isSafari){ // safari
16575             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
16576             v =  (v < 10) ? 10 : v;
16577             v =  (v > 48) ? 48 : v;
16578             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
16579             
16580         }
16581         
16582         
16583         v = Math.max(1, v+adjust);
16584         
16585         this.execCmd('FontSize', v  );
16586     },
16587
16588     onEditorEvent : function(e){
16589         this.owner.fireEvent('editorevent', this, e);
16590       //  this.updateToolbar();
16591         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
16592     },
16593
16594     insertTag : function(tg)
16595     {
16596         // could be a bit smarter... -> wrap the current selected tRoo..
16597         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
16598             
16599             range = this.createRange(this.getSelection());
16600             var wrappingNode = this.doc.createElement(tg.toLowerCase());
16601             wrappingNode.appendChild(range.extractContents());
16602             range.insertNode(wrappingNode);
16603
16604             return;
16605             
16606             
16607             
16608         }
16609         this.execCmd("formatblock",   tg);
16610         
16611     },
16612     
16613     insertText : function(txt)
16614     {
16615         
16616         
16617         var range = this.createRange();
16618         range.deleteContents();
16619                //alert(Sender.getAttribute('label'));
16620                
16621         range.insertNode(this.doc.createTextNode(txt));
16622     } ,
16623     
16624      
16625
16626     /**
16627      * Executes a Midas editor command on the editor document and performs necessary focus and
16628      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
16629      * @param {String} cmd The Midas command
16630      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16631      */
16632     relayCmd : function(cmd, value){
16633         this.win.focus();
16634         this.execCmd(cmd, value);
16635         this.owner.fireEvent('editorevent', this);
16636         //this.updateToolbar();
16637         this.owner.deferFocus();
16638     },
16639
16640     /**
16641      * Executes a Midas editor command directly on the editor document.
16642      * For visual commands, you should use {@link #relayCmd} instead.
16643      * <b>This should only be called after the editor is initialized.</b>
16644      * @param {String} cmd The Midas command
16645      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16646      */
16647     execCmd : function(cmd, value){
16648         this.doc.execCommand(cmd, false, value === undefined ? null : value);
16649         this.syncValue();
16650     },
16651  
16652  
16653    
16654     /**
16655      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
16656      * to insert tRoo.
16657      * @param {String} text | dom node.. 
16658      */
16659     insertAtCursor : function(text)
16660     {
16661         
16662         
16663         
16664         if(!this.activated){
16665             return;
16666         }
16667         /*
16668         if(Roo.isIE){
16669             this.win.focus();
16670             var r = this.doc.selection.createRange();
16671             if(r){
16672                 r.collapse(true);
16673                 r.pasteHTML(text);
16674                 this.syncValue();
16675                 this.deferFocus();
16676             
16677             }
16678             return;
16679         }
16680         */
16681         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
16682             this.win.focus();
16683             
16684             
16685             // from jquery ui (MIT licenced)
16686             var range, node;
16687             var win = this.win;
16688             
16689             if (win.getSelection && win.getSelection().getRangeAt) {
16690                 range = win.getSelection().getRangeAt(0);
16691                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
16692                 range.insertNode(node);
16693             } else if (win.document.selection && win.document.selection.createRange) {
16694                 // no firefox support
16695                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
16696                 win.document.selection.createRange().pasteHTML(txt);
16697             } else {
16698                 // no firefox support
16699                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
16700                 this.execCmd('InsertHTML', txt);
16701             } 
16702             
16703             this.syncValue();
16704             
16705             this.deferFocus();
16706         }
16707     },
16708  // private
16709     mozKeyPress : function(e){
16710         if(e.ctrlKey){
16711             var c = e.getCharCode(), cmd;
16712           
16713             if(c > 0){
16714                 c = String.fromCharCode(c).toLowerCase();
16715                 switch(c){
16716                     case 'b':
16717                         cmd = 'bold';
16718                         break;
16719                     case 'i':
16720                         cmd = 'italic';
16721                         break;
16722                     
16723                     case 'u':
16724                         cmd = 'underline';
16725                         break;
16726                     
16727                     case 'v':
16728                         this.cleanUpPaste.defer(100, this);
16729                         return;
16730                         
16731                 }
16732                 if(cmd){
16733                     this.win.focus();
16734                     this.execCmd(cmd);
16735                     this.deferFocus();
16736                     e.preventDefault();
16737                 }
16738                 
16739             }
16740         }
16741     },
16742
16743     // private
16744     fixKeys : function(){ // load time branching for fastest keydown performance
16745         if(Roo.isIE){
16746             return function(e){
16747                 var k = e.getKey(), r;
16748                 if(k == e.TAB){
16749                     e.stopEvent();
16750                     r = this.doc.selection.createRange();
16751                     if(r){
16752                         r.collapse(true);
16753                         r.pasteHTML('&#160;&#160;&#160;&#160;');
16754                         this.deferFocus();
16755                     }
16756                     return;
16757                 }
16758                 
16759                 if(k == e.ENTER){
16760                     r = this.doc.selection.createRange();
16761                     if(r){
16762                         var target = r.parentElement();
16763                         if(!target || target.tagName.toLowerCase() != 'li'){
16764                             e.stopEvent();
16765                             r.pasteHTML('<br />');
16766                             r.collapse(false);
16767                             r.select();
16768                         }
16769                     }
16770                 }
16771                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
16772                     this.cleanUpPaste.defer(100, this);
16773                     return;
16774                 }
16775                 
16776                 
16777             };
16778         }else if(Roo.isOpera){
16779             return function(e){
16780                 var k = e.getKey();
16781                 if(k == e.TAB){
16782                     e.stopEvent();
16783                     this.win.focus();
16784                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
16785                     this.deferFocus();
16786                 }
16787                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
16788                     this.cleanUpPaste.defer(100, this);
16789                     return;
16790                 }
16791                 
16792             };
16793         }else if(Roo.isSafari){
16794             return function(e){
16795                 var k = e.getKey();
16796                 
16797                 if(k == e.TAB){
16798                     e.stopEvent();
16799                     this.execCmd('InsertText','\t');
16800                     this.deferFocus();
16801                     return;
16802                 }
16803                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
16804                     this.cleanUpPaste.defer(100, this);
16805                     return;
16806                 }
16807                 
16808              };
16809         }
16810     }(),
16811     
16812     getAllAncestors: function()
16813     {
16814         var p = this.getSelectedNode();
16815         var a = [];
16816         if (!p) {
16817             a.push(p); // push blank onto stack..
16818             p = this.getParentElement();
16819         }
16820         
16821         
16822         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
16823             a.push(p);
16824             p = p.parentNode;
16825         }
16826         a.push(this.doc.body);
16827         return a;
16828     },
16829     lastSel : false,
16830     lastSelNode : false,
16831     
16832     
16833     getSelection : function() 
16834     {
16835         this.assignDocWin();
16836         return Roo.isIE ? this.doc.selection : this.win.getSelection();
16837     },
16838     
16839     getSelectedNode: function() 
16840     {
16841         // this may only work on Gecko!!!
16842         
16843         // should we cache this!!!!
16844         
16845         
16846         
16847          
16848         var range = this.createRange(this.getSelection()).cloneRange();
16849         
16850         if (Roo.isIE) {
16851             var parent = range.parentElement();
16852             while (true) {
16853                 var testRange = range.duplicate();
16854                 testRange.moveToElementText(parent);
16855                 if (testRange.inRange(range)) {
16856                     break;
16857                 }
16858                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
16859                     break;
16860                 }
16861                 parent = parent.parentElement;
16862             }
16863             return parent;
16864         }
16865         
16866         // is ancestor a text element.
16867         var ac =  range.commonAncestorContainer;
16868         if (ac.nodeType == 3) {
16869             ac = ac.parentNode;
16870         }
16871         
16872         var ar = ac.childNodes;
16873          
16874         var nodes = [];
16875         var other_nodes = [];
16876         var has_other_nodes = false;
16877         for (var i=0;i<ar.length;i++) {
16878             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
16879                 continue;
16880             }
16881             // fullly contained node.
16882             
16883             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
16884                 nodes.push(ar[i]);
16885                 continue;
16886             }
16887             
16888             // probably selected..
16889             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
16890                 other_nodes.push(ar[i]);
16891                 continue;
16892             }
16893             // outer..
16894             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
16895                 continue;
16896             }
16897             
16898             
16899             has_other_nodes = true;
16900         }
16901         if (!nodes.length && other_nodes.length) {
16902             nodes= other_nodes;
16903         }
16904         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
16905             return false;
16906         }
16907         
16908         return nodes[0];
16909     },
16910     createRange: function(sel)
16911     {
16912         // this has strange effects when using with 
16913         // top toolbar - not sure if it's a great idea.
16914         //this.editor.contentWindow.focus();
16915         if (typeof sel != "undefined") {
16916             try {
16917                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
16918             } catch(e) {
16919                 return this.doc.createRange();
16920             }
16921         } else {
16922             return this.doc.createRange();
16923         }
16924     },
16925     getParentElement: function()
16926     {
16927         
16928         this.assignDocWin();
16929         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
16930         
16931         var range = this.createRange(sel);
16932          
16933         try {
16934             var p = range.commonAncestorContainer;
16935             while (p.nodeType == 3) { // text node
16936                 p = p.parentNode;
16937             }
16938             return p;
16939         } catch (e) {
16940             return null;
16941         }
16942     
16943     },
16944     /***
16945      *
16946      * Range intersection.. the hard stuff...
16947      *  '-1' = before
16948      *  '0' = hits..
16949      *  '1' = after.
16950      *         [ -- selected range --- ]
16951      *   [fail]                        [fail]
16952      *
16953      *    basically..
16954      *      if end is before start or  hits it. fail.
16955      *      if start is after end or hits it fail.
16956      *
16957      *   if either hits (but other is outside. - then it's not 
16958      *   
16959      *    
16960      **/
16961     
16962     
16963     // @see http://www.thismuchiknow.co.uk/?p=64.
16964     rangeIntersectsNode : function(range, node)
16965     {
16966         var nodeRange = node.ownerDocument.createRange();
16967         try {
16968             nodeRange.selectNode(node);
16969         } catch (e) {
16970             nodeRange.selectNodeContents(node);
16971         }
16972     
16973         var rangeStartRange = range.cloneRange();
16974         rangeStartRange.collapse(true);
16975     
16976         var rangeEndRange = range.cloneRange();
16977         rangeEndRange.collapse(false);
16978     
16979         var nodeStartRange = nodeRange.cloneRange();
16980         nodeStartRange.collapse(true);
16981     
16982         var nodeEndRange = nodeRange.cloneRange();
16983         nodeEndRange.collapse(false);
16984     
16985         return rangeStartRange.compareBoundaryPoints(
16986                  Range.START_TO_START, nodeEndRange) == -1 &&
16987                rangeEndRange.compareBoundaryPoints(
16988                  Range.START_TO_START, nodeStartRange) == 1;
16989         
16990          
16991     },
16992     rangeCompareNode : function(range, node)
16993     {
16994         var nodeRange = node.ownerDocument.createRange();
16995         try {
16996             nodeRange.selectNode(node);
16997         } catch (e) {
16998             nodeRange.selectNodeContents(node);
16999         }
17000         
17001         
17002         range.collapse(true);
17003     
17004         nodeRange.collapse(true);
17005      
17006         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
17007         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
17008          
17009         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
17010         
17011         var nodeIsBefore   =  ss == 1;
17012         var nodeIsAfter    = ee == -1;
17013         
17014         if (nodeIsBefore && nodeIsAfter)
17015             return 0; // outer
17016         if (!nodeIsBefore && nodeIsAfter)
17017             return 1; //right trailed.
17018         
17019         if (nodeIsBefore && !nodeIsAfter)
17020             return 2;  // left trailed.
17021         // fully contined.
17022         return 3;
17023     },
17024
17025     // private? - in a new class?
17026     cleanUpPaste :  function()
17027     {
17028         // cleans up the whole document..
17029         Roo.log('cleanuppaste');
17030         
17031         this.cleanUpChildren(this.doc.body);
17032         var clean = this.cleanWordChars(this.doc.body.innerHTML);
17033         if (clean != this.doc.body.innerHTML) {
17034             this.doc.body.innerHTML = clean;
17035         }
17036         
17037     },
17038     
17039     cleanWordChars : function(input) {// change the chars to hex code
17040         var he = Roo.HtmlEditorCore;
17041         
17042         var output = input;
17043         Roo.each(he.swapCodes, function(sw) { 
17044             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
17045             
17046             output = output.replace(swapper, sw[1]);
17047         });
17048         
17049         return output;
17050     },
17051     
17052     
17053     cleanUpChildren : function (n)
17054     {
17055         if (!n.childNodes.length) {
17056             return;
17057         }
17058         for (var i = n.childNodes.length-1; i > -1 ; i--) {
17059            this.cleanUpChild(n.childNodes[i]);
17060         }
17061     },
17062     
17063     
17064         
17065     
17066     cleanUpChild : function (node)
17067     {
17068         var ed = this;
17069         //console.log(node);
17070         if (node.nodeName == "#text") {
17071             // clean up silly Windows -- stuff?
17072             return; 
17073         }
17074         if (node.nodeName == "#comment") {
17075             node.parentNode.removeChild(node);
17076             // clean up silly Windows -- stuff?
17077             return; 
17078         }
17079         
17080         if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1 && this.clearUp) {
17081             // remove node.
17082             node.parentNode.removeChild(node);
17083             return;
17084             
17085         }
17086         
17087         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
17088         
17089         // remove <a name=....> as rendering on yahoo mailer is borked with this.
17090         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
17091         
17092         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
17093         //    remove_keep_children = true;
17094         //}
17095         
17096         if (remove_keep_children) {
17097             this.cleanUpChildren(node);
17098             // inserts everything just before this node...
17099             while (node.childNodes.length) {
17100                 var cn = node.childNodes[0];
17101                 node.removeChild(cn);
17102                 node.parentNode.insertBefore(cn, node);
17103             }
17104             node.parentNode.removeChild(node);
17105             return;
17106         }
17107         
17108         if (!node.attributes || !node.attributes.length) {
17109             this.cleanUpChildren(node);
17110             return;
17111         }
17112         
17113         function cleanAttr(n,v)
17114         {
17115             
17116             if (v.match(/^\./) || v.match(/^\//)) {
17117                 return;
17118             }
17119             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
17120                 return;
17121             }
17122             if (v.match(/^#/)) {
17123                 return;
17124             }
17125 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
17126             node.removeAttribute(n);
17127             
17128         }
17129         
17130         function cleanStyle(n,v)
17131         {
17132             if (v.match(/expression/)) { //XSS?? should we even bother..
17133                 node.removeAttribute(n);
17134                 return;
17135             }
17136             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
17137             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
17138             
17139             
17140             var parts = v.split(/;/);
17141             var clean = [];
17142             
17143             Roo.each(parts, function(p) {
17144                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
17145                 if (!p.length) {
17146                     return true;
17147                 }
17148                 var l = p.split(':').shift().replace(/\s+/g,'');
17149                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
17150                 
17151                 if ( cblack.indexOf(l) > -1) {
17152 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17153                     //node.removeAttribute(n);
17154                     return true;
17155                 }
17156                 //Roo.log()
17157                 // only allow 'c whitelisted system attributes'
17158                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
17159 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17160                     //node.removeAttribute(n);
17161                     return true;
17162                 }
17163                 
17164                 
17165                  
17166                 
17167                 clean.push(p);
17168                 return true;
17169             });
17170             if (clean.length) { 
17171                 node.setAttribute(n, clean.join(';'));
17172             } else {
17173                 node.removeAttribute(n);
17174             }
17175             
17176         }
17177         
17178         
17179         for (var i = node.attributes.length-1; i > -1 ; i--) {
17180             var a = node.attributes[i];
17181             //console.log(a);
17182             
17183             if (a.name.toLowerCase().substr(0,2)=='on')  {
17184                 node.removeAttribute(a.name);
17185                 continue;
17186             }
17187             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
17188                 node.removeAttribute(a.name);
17189                 continue;
17190             }
17191             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
17192                 cleanAttr(a.name,a.value); // fixme..
17193                 continue;
17194             }
17195             if (a.name == 'style') {
17196                 cleanStyle(a.name,a.value);
17197                 continue;
17198             }
17199             /// clean up MS crap..
17200             // tecnically this should be a list of valid class'es..
17201             
17202             
17203             if (a.name == 'class') {
17204                 if (a.value.match(/^Mso/)) {
17205                     node.className = '';
17206                 }
17207                 
17208                 if (a.value.match(/body/)) {
17209                     node.className = '';
17210                 }
17211                 continue;
17212             }
17213             
17214             // style cleanup!?
17215             // class cleanup?
17216             
17217         }
17218         
17219         
17220         this.cleanUpChildren(node);
17221         
17222         
17223     },
17224     /**
17225      * Clean up MS wordisms...
17226      */
17227     cleanWord : function(node)
17228     {
17229         var _t = this;
17230         var cleanWordChildren = function()
17231         {
17232             if (!node.childNodes.length) {
17233                 return;
17234             }
17235             for (var i = node.childNodes.length-1; i > -1 ; i--) {
17236                _t.cleanWord(node.childNodes[i]);
17237             }
17238         }
17239         
17240         
17241         if (!node) {
17242             this.cleanWord(this.doc.body);
17243             return;
17244         }
17245         if (node.nodeName == "#text") {
17246             // clean up silly Windows -- stuff?
17247             return; 
17248         }
17249         if (node.nodeName == "#comment") {
17250             node.parentNode.removeChild(node);
17251             // clean up silly Windows -- stuff?
17252             return; 
17253         }
17254         
17255         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
17256             node.parentNode.removeChild(node);
17257             return;
17258         }
17259         
17260         // remove - but keep children..
17261         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
17262             while (node.childNodes.length) {
17263                 var cn = node.childNodes[0];
17264                 node.removeChild(cn);
17265                 node.parentNode.insertBefore(cn, node);
17266             }
17267             node.parentNode.removeChild(node);
17268             cleanWordChildren();
17269             return;
17270         }
17271         // clean styles
17272         if (node.className.length) {
17273             
17274             var cn = node.className.split(/\W+/);
17275             var cna = [];
17276             Roo.each(cn, function(cls) {
17277                 if (cls.match(/Mso[a-zA-Z]+/)) {
17278                     return;
17279                 }
17280                 cna.push(cls);
17281             });
17282             node.className = cna.length ? cna.join(' ') : '';
17283             if (!cna.length) {
17284                 node.removeAttribute("class");
17285             }
17286         }
17287         
17288         if (node.hasAttribute("lang")) {
17289             node.removeAttribute("lang");
17290         }
17291         
17292         if (node.hasAttribute("style")) {
17293             
17294             var styles = node.getAttribute("style").split(";");
17295             var nstyle = [];
17296             Roo.each(styles, function(s) {
17297                 if (!s.match(/:/)) {
17298                     return;
17299                 }
17300                 var kv = s.split(":");
17301                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
17302                     return;
17303                 }
17304                 // what ever is left... we allow.
17305                 nstyle.push(s);
17306             });
17307             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
17308             if (!nstyle.length) {
17309                 node.removeAttribute('style');
17310             }
17311         }
17312         
17313         cleanWordChildren();
17314         
17315         
17316     },
17317     domToHTML : function(currentElement, depth, nopadtext) {
17318         
17319             depth = depth || 0;
17320             nopadtext = nopadtext || false;
17321         
17322             if (!currentElement) {
17323                 return this.domToHTML(this.doc.body);
17324             }
17325             
17326             //Roo.log(currentElement);
17327             var j;
17328             var allText = false;
17329             var nodeName = currentElement.nodeName;
17330             var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
17331             
17332             if  (nodeName == '#text') {
17333                 return currentElement.nodeValue;
17334             }
17335             
17336             
17337             var ret = '';
17338             if (nodeName != 'BODY') {
17339                  
17340                 var i = 0;
17341                 // Prints the node tagName, such as <A>, <IMG>, etc
17342                 if (tagName) {
17343                     var attr = [];
17344                     for(i = 0; i < currentElement.attributes.length;i++) {
17345                         // quoting?
17346                         var aname = currentElement.attributes.item(i).name;
17347                         if (!currentElement.attributes.item(i).value.length) {
17348                             continue;
17349                         }
17350                         attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
17351                     }
17352                     
17353                     ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
17354                 } 
17355                 else {
17356                     
17357                     // eack
17358                 }
17359             } else {
17360                 tagName = false;
17361             }
17362             if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
17363                 return ret;
17364             }
17365             if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
17366                 nopadtext = true;
17367             }
17368             
17369             
17370             // Traverse the tree
17371             i = 0;
17372             var currentElementChild = currentElement.childNodes.item(i);
17373             var allText = true;
17374             var innerHTML  = '';
17375             lastnode = '';
17376             while (currentElementChild) {
17377                 // Formatting code (indent the tree so it looks nice on the screen)
17378                 var nopad = nopadtext;
17379                 if (lastnode == 'SPAN') {
17380                     nopad  = true;
17381                 }
17382                 // text
17383                 if  (currentElementChild.nodeName == '#text') {
17384                     var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
17385                     if (!nopad && toadd.length > 80) {
17386                         innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
17387                     }
17388                     innerHTML  += toadd;
17389                     
17390                     i++;
17391                     currentElementChild = currentElement.childNodes.item(i);
17392                     lastNode = '';
17393                     continue;
17394                 }
17395                 allText = false;
17396                 
17397                 innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
17398                     
17399                 // Recursively traverse the tree structure of the child node
17400                 innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
17401                 lastnode = currentElementChild.nodeName;
17402                 i++;
17403                 currentElementChild=currentElement.childNodes.item(i);
17404             }
17405             
17406             ret += innerHTML;
17407             
17408             if (!allText) {
17409                     // The remaining code is mostly for formatting the tree
17410                 ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
17411             }
17412             
17413             
17414             if (tagName) {
17415                 ret+= "</"+tagName+">";
17416             }
17417             return ret;
17418             
17419         }
17420     
17421     // hide stuff that is not compatible
17422     /**
17423      * @event blur
17424      * @hide
17425      */
17426     /**
17427      * @event change
17428      * @hide
17429      */
17430     /**
17431      * @event focus
17432      * @hide
17433      */
17434     /**
17435      * @event specialkey
17436      * @hide
17437      */
17438     /**
17439      * @cfg {String} fieldClass @hide
17440      */
17441     /**
17442      * @cfg {String} focusClass @hide
17443      */
17444     /**
17445      * @cfg {String} autoCreate @hide
17446      */
17447     /**
17448      * @cfg {String} inputType @hide
17449      */
17450     /**
17451      * @cfg {String} invalidClass @hide
17452      */
17453     /**
17454      * @cfg {String} invalidText @hide
17455      */
17456     /**
17457      * @cfg {String} msgFx @hide
17458      */
17459     /**
17460      * @cfg {String} validateOnBlur @hide
17461      */
17462 });
17463
17464 Roo.HtmlEditorCore.white = [
17465         'area', 'br', 'img', 'input', 'hr', 'wbr',
17466         
17467        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
17468        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
17469        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
17470        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
17471        'table',   'ul',         'xmp', 
17472        
17473        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
17474       'thead',   'tr', 
17475      
17476       'dir', 'menu', 'ol', 'ul', 'dl',
17477        
17478       'embed',  'object'
17479 ];
17480
17481
17482 Roo.HtmlEditorCore.black = [
17483     //    'embed',  'object', // enable - backend responsiblity to clean thiese
17484         'applet', // 
17485         'base',   'basefont', 'bgsound', 'blink',  'body', 
17486         'frame',  'frameset', 'head',    'html',   'ilayer', 
17487         'iframe', 'layer',  'link',     'meta',    'object',   
17488         'script', 'style' ,'title',  'xml' // clean later..
17489 ];
17490 Roo.HtmlEditorCore.clean = [
17491     'script', 'style', 'title', 'xml'
17492 ];
17493 Roo.HtmlEditorCore.remove = [
17494     'font'
17495 ];
17496 // attributes..
17497
17498 Roo.HtmlEditorCore.ablack = [
17499     'on'
17500 ];
17501     
17502 Roo.HtmlEditorCore.aclean = [ 
17503     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
17504 ];
17505
17506 // protocols..
17507 Roo.HtmlEditorCore.pwhite= [
17508         'http',  'https',  'mailto'
17509 ];
17510
17511 // white listed style attributes.
17512 Roo.HtmlEditorCore.cwhite= [
17513       //  'text-align', /// default is to allow most things..
17514       
17515          
17516 //        'font-size'//??
17517 ];
17518
17519 // black listed style attributes.
17520 Roo.HtmlEditorCore.cblack= [
17521       //  'font-size' -- this can be set by the project 
17522 ];
17523
17524
17525 Roo.HtmlEditorCore.swapCodes   =[ 
17526     [    8211, "--" ], 
17527     [    8212, "--" ], 
17528     [    8216,  "'" ],  
17529     [    8217, "'" ],  
17530     [    8220, '"' ],  
17531     [    8221, '"' ],  
17532     [    8226, "*" ],  
17533     [    8230, "..." ]
17534 ]; 
17535
17536     /*
17537  * - LGPL
17538  *
17539  * HtmlEditor
17540  * 
17541  */
17542
17543 /**
17544  * @class Roo.bootstrap.HtmlEditor
17545  * @extends Roo.bootstrap.TextArea
17546  * Bootstrap HtmlEditor class
17547
17548  * @constructor
17549  * Create a new HtmlEditor
17550  * @param {Object} config The config object
17551  */
17552
17553 Roo.bootstrap.HtmlEditor = function(config){
17554     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
17555     if (!this.toolbars) {
17556         this.toolbars = [];
17557     }
17558     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
17559     this.addEvents({
17560             /**
17561              * @event initialize
17562              * Fires when the editor is fully initialized (including the iframe)
17563              * @param {HtmlEditor} this
17564              */
17565             initialize: true,
17566             /**
17567              * @event activate
17568              * Fires when the editor is first receives the focus. Any insertion must wait
17569              * until after this event.
17570              * @param {HtmlEditor} this
17571              */
17572             activate: true,
17573              /**
17574              * @event beforesync
17575              * Fires before the textarea is updated with content from the editor iframe. Return false
17576              * to cancel the sync.
17577              * @param {HtmlEditor} this
17578              * @param {String} html
17579              */
17580             beforesync: true,
17581              /**
17582              * @event beforepush
17583              * Fires before the iframe editor is updated with content from the textarea. Return false
17584              * to cancel the push.
17585              * @param {HtmlEditor} this
17586              * @param {String} html
17587              */
17588             beforepush: true,
17589              /**
17590              * @event sync
17591              * Fires when the textarea is updated with content from the editor iframe.
17592              * @param {HtmlEditor} this
17593              * @param {String} html
17594              */
17595             sync: true,
17596              /**
17597              * @event push
17598              * Fires when the iframe editor is updated with content from the textarea.
17599              * @param {HtmlEditor} this
17600              * @param {String} html
17601              */
17602             push: true,
17603              /**
17604              * @event editmodechange
17605              * Fires when the editor switches edit modes
17606              * @param {HtmlEditor} this
17607              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
17608              */
17609             editmodechange: true,
17610             /**
17611              * @event editorevent
17612              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
17613              * @param {HtmlEditor} this
17614              */
17615             editorevent: true,
17616             /**
17617              * @event firstfocus
17618              * Fires when on first focus - needed by toolbars..
17619              * @param {HtmlEditor} this
17620              */
17621             firstfocus: true,
17622             /**
17623              * @event autosave
17624              * Auto save the htmlEditor value as a file into Events
17625              * @param {HtmlEditor} this
17626              */
17627             autosave: true,
17628             /**
17629              * @event savedpreview
17630              * preview the saved version of htmlEditor
17631              * @param {HtmlEditor} this
17632              */
17633             savedpreview: true
17634         });
17635 };
17636
17637
17638 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
17639     
17640     
17641       /**
17642      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
17643      */
17644     toolbars : false,
17645    
17646      /**
17647      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
17648      *                        Roo.resizable.
17649      */
17650     resizable : false,
17651      /**
17652      * @cfg {Number} height (in pixels)
17653      */   
17654     height: 300,
17655    /**
17656      * @cfg {Number} width (in pixels)
17657      */   
17658     width: false,
17659     
17660     /**
17661      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
17662      * 
17663      */
17664     stylesheets: false,
17665     
17666     // id of frame..
17667     frameId: false,
17668     
17669     // private properties
17670     validationEvent : false,
17671     deferHeight: true,
17672     initialized : false,
17673     activated : false,
17674     
17675     onFocus : Roo.emptyFn,
17676     iframePad:3,
17677     hideMode:'offsets',
17678     
17679     
17680     tbContainer : false,
17681     
17682     toolbarContainer :function() {
17683         return this.wrap.select('.x-html-editor-tb',true).first();
17684     },
17685
17686     /**
17687      * Protected method that will not generally be called directly. It
17688      * is called when the editor creates its toolbar. Override this method if you need to
17689      * add custom toolbar buttons.
17690      * @param {HtmlEditor} editor
17691      */
17692     createToolbar : function(){
17693         
17694         Roo.log("create toolbars");
17695         
17696         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
17697         this.toolbars[0].render(this.toolbarContainer());
17698         
17699         return;
17700         
17701 //        if (!editor.toolbars || !editor.toolbars.length) {
17702 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
17703 //        }
17704 //        
17705 //        for (var i =0 ; i < editor.toolbars.length;i++) {
17706 //            editor.toolbars[i] = Roo.factory(
17707 //                    typeof(editor.toolbars[i]) == 'string' ?
17708 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
17709 //                Roo.bootstrap.HtmlEditor);
17710 //            editor.toolbars[i].init(editor);
17711 //        }
17712     },
17713
17714      
17715     // private
17716     onRender : function(ct, position)
17717     {
17718        // Roo.log("Call onRender: " + this.xtype);
17719         var _t = this;
17720         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
17721       
17722         this.wrap = this.inputEl().wrap({
17723             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
17724         });
17725         
17726         this.editorcore.onRender(ct, position);
17727          
17728         if (this.resizable) {
17729             this.resizeEl = new Roo.Resizable(this.wrap, {
17730                 pinned : true,
17731                 wrap: true,
17732                 dynamic : true,
17733                 minHeight : this.height,
17734                 height: this.height,
17735                 handles : this.resizable,
17736                 width: this.width,
17737                 listeners : {
17738                     resize : function(r, w, h) {
17739                         _t.onResize(w,h); // -something
17740                     }
17741                 }
17742             });
17743             
17744         }
17745         this.createToolbar(this);
17746        
17747         
17748         if(!this.width && this.resizable){
17749             this.setSize(this.wrap.getSize());
17750         }
17751         if (this.resizeEl) {
17752             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
17753             // should trigger onReize..
17754         }
17755         
17756     },
17757
17758     // private
17759     onResize : function(w, h)
17760     {
17761         Roo.log('resize: ' +w + ',' + h );
17762         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
17763         var ew = false;
17764         var eh = false;
17765         
17766         if(this.inputEl() ){
17767             if(typeof w == 'number'){
17768                 var aw = w - this.wrap.getFrameWidth('lr');
17769                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
17770                 ew = aw;
17771             }
17772             if(typeof h == 'number'){
17773                  var tbh = -11;  // fixme it needs to tool bar size!
17774                 for (var i =0; i < this.toolbars.length;i++) {
17775                     // fixme - ask toolbars for heights?
17776                     tbh += this.toolbars[i].el.getHeight();
17777                     //if (this.toolbars[i].footer) {
17778                     //    tbh += this.toolbars[i].footer.el.getHeight();
17779                     //}
17780                 }
17781               
17782                 
17783                 
17784                 
17785                 
17786                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
17787                 ah -= 5; // knock a few pixes off for look..
17788                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
17789                 var eh = ah;
17790             }
17791         }
17792         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
17793         this.editorcore.onResize(ew,eh);
17794         
17795     },
17796
17797     /**
17798      * Toggles the editor between standard and source edit mode.
17799      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
17800      */
17801     toggleSourceEdit : function(sourceEditMode)
17802     {
17803         this.editorcore.toggleSourceEdit(sourceEditMode);
17804         
17805         if(this.editorcore.sourceEditMode){
17806             Roo.log('editor - showing textarea');
17807             
17808 //            Roo.log('in');
17809 //            Roo.log(this.syncValue());
17810             this.syncValue();
17811             this.inputEl().removeClass(['hide', 'x-hidden']);
17812             this.inputEl().dom.removeAttribute('tabIndex');
17813             this.inputEl().focus();
17814         }else{
17815             Roo.log('editor - hiding textarea');
17816 //            Roo.log('out')
17817 //            Roo.log(this.pushValue()); 
17818             this.pushValue();
17819             
17820             this.inputEl().addClass(['hide', 'x-hidden']);
17821             this.inputEl().dom.setAttribute('tabIndex', -1);
17822             //this.deferFocus();
17823         }
17824          
17825         if(this.resizable){
17826             this.setSize(this.wrap.getSize());
17827         }
17828         
17829         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
17830     },
17831  
17832     // private (for BoxComponent)
17833     adjustSize : Roo.BoxComponent.prototype.adjustSize,
17834
17835     // private (for BoxComponent)
17836     getResizeEl : function(){
17837         return this.wrap;
17838     },
17839
17840     // private (for BoxComponent)
17841     getPositionEl : function(){
17842         return this.wrap;
17843     },
17844
17845     // private
17846     initEvents : function(){
17847         this.originalValue = this.getValue();
17848     },
17849
17850 //    /**
17851 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
17852 //     * @method
17853 //     */
17854 //    markInvalid : Roo.emptyFn,
17855 //    /**
17856 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
17857 //     * @method
17858 //     */
17859 //    clearInvalid : Roo.emptyFn,
17860
17861     setValue : function(v){
17862         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
17863         this.editorcore.pushValue();
17864     },
17865
17866      
17867     // private
17868     deferFocus : function(){
17869         this.focus.defer(10, this);
17870     },
17871
17872     // doc'ed in Field
17873     focus : function(){
17874         this.editorcore.focus();
17875         
17876     },
17877       
17878
17879     // private
17880     onDestroy : function(){
17881         
17882         
17883         
17884         if(this.rendered){
17885             
17886             for (var i =0; i < this.toolbars.length;i++) {
17887                 // fixme - ask toolbars for heights?
17888                 this.toolbars[i].onDestroy();
17889             }
17890             
17891             this.wrap.dom.innerHTML = '';
17892             this.wrap.remove();
17893         }
17894     },
17895
17896     // private
17897     onFirstFocus : function(){
17898         //Roo.log("onFirstFocus");
17899         this.editorcore.onFirstFocus();
17900          for (var i =0; i < this.toolbars.length;i++) {
17901             this.toolbars[i].onFirstFocus();
17902         }
17903         
17904     },
17905     
17906     // private
17907     syncValue : function()
17908     {   
17909         this.editorcore.syncValue();
17910     },
17911     
17912     pushValue : function()
17913     {   
17914         this.editorcore.pushValue();
17915     }
17916      
17917     
17918     // hide stuff that is not compatible
17919     /**
17920      * @event blur
17921      * @hide
17922      */
17923     /**
17924      * @event change
17925      * @hide
17926      */
17927     /**
17928      * @event focus
17929      * @hide
17930      */
17931     /**
17932      * @event specialkey
17933      * @hide
17934      */
17935     /**
17936      * @cfg {String} fieldClass @hide
17937      */
17938     /**
17939      * @cfg {String} focusClass @hide
17940      */
17941     /**
17942      * @cfg {String} autoCreate @hide
17943      */
17944     /**
17945      * @cfg {String} inputType @hide
17946      */
17947     /**
17948      * @cfg {String} invalidClass @hide
17949      */
17950     /**
17951      * @cfg {String} invalidText @hide
17952      */
17953     /**
17954      * @cfg {String} msgFx @hide
17955      */
17956     /**
17957      * @cfg {String} validateOnBlur @hide
17958      */
17959 });
17960  
17961     
17962    
17963    
17964    
17965       
17966 Roo.namespace('Roo.bootstrap.htmleditor');
17967 /**
17968  * @class Roo.bootstrap.HtmlEditorToolbar1
17969  * Basic Toolbar
17970  * 
17971  * Usage:
17972  *
17973  new Roo.bootstrap.HtmlEditor({
17974     ....
17975     toolbars : [
17976         new Roo.bootstrap.HtmlEditorToolbar1({
17977             disable : { fonts: 1 , format: 1, ..., ... , ...],
17978             btns : [ .... ]
17979         })
17980     }
17981      
17982  * 
17983  * @cfg {Object} disable List of elements to disable..
17984  * @cfg {Array} btns List of additional buttons.
17985  * 
17986  * 
17987  * NEEDS Extra CSS? 
17988  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
17989  */
17990  
17991 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
17992 {
17993     
17994     Roo.apply(this, config);
17995     
17996     // default disabled, based on 'good practice'..
17997     this.disable = this.disable || {};
17998     Roo.applyIf(this.disable, {
17999         fontSize : true,
18000         colors : true,
18001         specialElements : true
18002     });
18003     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
18004     
18005     this.editor = config.editor;
18006     this.editorcore = config.editor.editorcore;
18007     
18008     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
18009     
18010     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
18011     // dont call parent... till later.
18012 }
18013 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
18014      
18015     bar : true,
18016     
18017     editor : false,
18018     editorcore : false,
18019     
18020     
18021     formats : [
18022         "p" ,  
18023         "h1","h2","h3","h4","h5","h6", 
18024         "pre", "code", 
18025         "abbr", "acronym", "address", "cite", "samp", "var",
18026         'div','span'
18027     ],
18028     
18029     onRender : function(ct, position)
18030     {
18031        // Roo.log("Call onRender: " + this.xtype);
18032         
18033        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
18034        Roo.log(this.el);
18035        this.el.dom.style.marginBottom = '0';
18036        var _this = this;
18037        var editorcore = this.editorcore;
18038        var editor= this.editor;
18039        
18040        var children = [];
18041        var btn = function(id,cmd , toggle, handler){
18042        
18043             var  event = toggle ? 'toggle' : 'click';
18044        
18045             var a = {
18046                 size : 'sm',
18047                 xtype: 'Button',
18048                 xns: Roo.bootstrap,
18049                 glyphicon : id,
18050                 cmd : id || cmd,
18051                 enableToggle:toggle !== false,
18052                 //html : 'submit'
18053                 pressed : toggle ? false : null,
18054                 listeners : {}
18055             }
18056             a.listeners[toggle ? 'toggle' : 'click'] = function() {
18057                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
18058             }
18059             children.push(a);
18060             return a;
18061        }
18062         
18063         var style = {
18064                 xtype: 'Button',
18065                 size : 'sm',
18066                 xns: Roo.bootstrap,
18067                 glyphicon : 'font',
18068                 //html : 'submit'
18069                 menu : {
18070                     xtype: 'Menu',
18071                     xns: Roo.bootstrap,
18072                     items:  []
18073                 }
18074         };
18075         Roo.each(this.formats, function(f) {
18076             style.menu.items.push({
18077                 xtype :'MenuItem',
18078                 xns: Roo.bootstrap,
18079                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
18080                 tagname : f,
18081                 listeners : {
18082                     click : function()
18083                     {
18084                         editorcore.insertTag(this.tagname);
18085                         editor.focus();
18086                     }
18087                 }
18088                 
18089             });
18090         });
18091          children.push(style);   
18092             
18093             
18094         btn('bold',false,true);
18095         btn('italic',false,true);
18096         btn('align-left', 'justifyleft',true);
18097         btn('align-center', 'justifycenter',true);
18098         btn('align-right' , 'justifyright',true);
18099         btn('link', false, false, function(btn) {
18100             //Roo.log("create link?");
18101             var url = prompt(this.createLinkText, this.defaultLinkValue);
18102             if(url && url != 'http:/'+'/'){
18103                 this.editorcore.relayCmd('createlink', url);
18104             }
18105         }),
18106         btn('list','insertunorderedlist',true);
18107         btn('pencil', false,true, function(btn){
18108                 Roo.log(this);
18109                 
18110                 this.toggleSourceEdit(btn.pressed);
18111         });
18112         /*
18113         var cog = {
18114                 xtype: 'Button',
18115                 size : 'sm',
18116                 xns: Roo.bootstrap,
18117                 glyphicon : 'cog',
18118                 //html : 'submit'
18119                 menu : {
18120                     xtype: 'Menu',
18121                     xns: Roo.bootstrap,
18122                     items:  []
18123                 }
18124         };
18125         
18126         cog.menu.items.push({
18127             xtype :'MenuItem',
18128             xns: Roo.bootstrap,
18129             html : Clean styles,
18130             tagname : f,
18131             listeners : {
18132                 click : function()
18133                 {
18134                     editorcore.insertTag(this.tagname);
18135                     editor.focus();
18136                 }
18137             }
18138             
18139         });
18140        */
18141         
18142          
18143        this.xtype = 'NavSimplebar';
18144         
18145         for(var i=0;i< children.length;i++) {
18146             
18147             this.buttons.add(this.addxtypeChild(children[i]));
18148             
18149         }
18150         
18151         editor.on('editorevent', this.updateToolbar, this);
18152     },
18153     onBtnClick : function(id)
18154     {
18155        this.editorcore.relayCmd(id);
18156        this.editorcore.focus();
18157     },
18158     
18159     /**
18160      * Protected method that will not generally be called directly. It triggers
18161      * a toolbar update by reading the markup state of the current selection in the editor.
18162      */
18163     updateToolbar: function(){
18164
18165         if(!this.editorcore.activated){
18166             this.editor.onFirstFocus(); // is this neeed?
18167             return;
18168         }
18169
18170         var btns = this.buttons; 
18171         var doc = this.editorcore.doc;
18172         btns.get('bold').setActive(doc.queryCommandState('bold'));
18173         btns.get('italic').setActive(doc.queryCommandState('italic'));
18174         //btns.get('underline').setActive(doc.queryCommandState('underline'));
18175         
18176         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
18177         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
18178         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
18179         
18180         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
18181         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
18182          /*
18183         
18184         var ans = this.editorcore.getAllAncestors();
18185         if (this.formatCombo) {
18186             
18187             
18188             var store = this.formatCombo.store;
18189             this.formatCombo.setValue("");
18190             for (var i =0; i < ans.length;i++) {
18191                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
18192                     // select it..
18193                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
18194                     break;
18195                 }
18196             }
18197         }
18198         
18199         
18200         
18201         // hides menus... - so this cant be on a menu...
18202         Roo.bootstrap.MenuMgr.hideAll();
18203         */
18204         Roo.bootstrap.MenuMgr.hideAll();
18205         //this.editorsyncValue();
18206     },
18207     onFirstFocus: function() {
18208         this.buttons.each(function(item){
18209            item.enable();
18210         });
18211     },
18212     toggleSourceEdit : function(sourceEditMode){
18213         
18214           
18215         if(sourceEditMode){
18216             Roo.log("disabling buttons");
18217            this.buttons.each( function(item){
18218                 if(item.cmd != 'pencil'){
18219                     item.disable();
18220                 }
18221             });
18222           
18223         }else{
18224             Roo.log("enabling buttons");
18225             if(this.editorcore.initialized){
18226                 this.buttons.each( function(item){
18227                     item.enable();
18228                 });
18229             }
18230             
18231         }
18232         Roo.log("calling toggole on editor");
18233         // tell the editor that it's been pressed..
18234         this.editor.toggleSourceEdit(sourceEditMode);
18235        
18236     }
18237 });
18238
18239
18240
18241
18242
18243 /**
18244  * @class Roo.bootstrap.Table.AbstractSelectionModel
18245  * @extends Roo.util.Observable
18246  * Abstract base class for grid SelectionModels.  It provides the interface that should be
18247  * implemented by descendant classes.  This class should not be directly instantiated.
18248  * @constructor
18249  */
18250 Roo.bootstrap.Table.AbstractSelectionModel = function(){
18251     this.locked = false;
18252     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
18253 };
18254
18255
18256 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
18257     /** @ignore Called by the grid automatically. Do not call directly. */
18258     init : function(grid){
18259         this.grid = grid;
18260         this.initEvents();
18261     },
18262
18263     /**
18264      * Locks the selections.
18265      */
18266     lock : function(){
18267         this.locked = true;
18268     },
18269
18270     /**
18271      * Unlocks the selections.
18272      */
18273     unlock : function(){
18274         this.locked = false;
18275     },
18276
18277     /**
18278      * Returns true if the selections are locked.
18279      * @return {Boolean}
18280      */
18281     isLocked : function(){
18282         return this.locked;
18283     }
18284 });
18285 /**
18286  * @extends Roo.bootstrap.Table.AbstractSelectionModel
18287  * @class Roo.bootstrap.Table.RowSelectionModel
18288  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
18289  * It supports multiple selections and keyboard selection/navigation. 
18290  * @constructor
18291  * @param {Object} config
18292  */
18293
18294 Roo.bootstrap.Table.RowSelectionModel = function(config){
18295     Roo.apply(this, config);
18296     this.selections = new Roo.util.MixedCollection(false, function(o){
18297         return o.id;
18298     });
18299
18300     this.last = false;
18301     this.lastActive = false;
18302
18303     this.addEvents({
18304         /**
18305              * @event selectionchange
18306              * Fires when the selection changes
18307              * @param {SelectionModel} this
18308              */
18309             "selectionchange" : true,
18310         /**
18311              * @event afterselectionchange
18312              * Fires after the selection changes (eg. by key press or clicking)
18313              * @param {SelectionModel} this
18314              */
18315             "afterselectionchange" : true,
18316         /**
18317              * @event beforerowselect
18318              * Fires when a row is selected being selected, return false to cancel.
18319              * @param {SelectionModel} this
18320              * @param {Number} rowIndex The selected index
18321              * @param {Boolean} keepExisting False if other selections will be cleared
18322              */
18323             "beforerowselect" : true,
18324         /**
18325              * @event rowselect
18326              * Fires when a row is selected.
18327              * @param {SelectionModel} this
18328              * @param {Number} rowIndex The selected index
18329              * @param {Roo.data.Record} r The record
18330              */
18331             "rowselect" : true,
18332         /**
18333              * @event rowdeselect
18334              * Fires when a row is deselected.
18335              * @param {SelectionModel} this
18336              * @param {Number} rowIndex The selected index
18337              */
18338         "rowdeselect" : true
18339     });
18340     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
18341     this.locked = false;
18342 };
18343
18344 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
18345     /**
18346      * @cfg {Boolean} singleSelect
18347      * True to allow selection of only one row at a time (defaults to false)
18348      */
18349     singleSelect : false,
18350
18351     // private
18352     initEvents : function(){
18353
18354         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
18355             this.grid.on("mousedown", this.handleMouseDown, this);
18356         }else{ // allow click to work like normal
18357             this.grid.on("rowclick", this.handleDragableRowClick, this);
18358         }
18359
18360         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
18361             "up" : function(e){
18362                 if(!e.shiftKey){
18363                     this.selectPrevious(e.shiftKey);
18364                 }else if(this.last !== false && this.lastActive !== false){
18365                     var last = this.last;
18366                     this.selectRange(this.last,  this.lastActive-1);
18367                     this.grid.getView().focusRow(this.lastActive);
18368                     if(last !== false){
18369                         this.last = last;
18370                     }
18371                 }else{
18372                     this.selectFirstRow();
18373                 }
18374                 this.fireEvent("afterselectionchange", this);
18375             },
18376             "down" : function(e){
18377                 if(!e.shiftKey){
18378                     this.selectNext(e.shiftKey);
18379                 }else if(this.last !== false && this.lastActive !== false){
18380                     var last = this.last;
18381                     this.selectRange(this.last,  this.lastActive+1);
18382                     this.grid.getView().focusRow(this.lastActive);
18383                     if(last !== false){
18384                         this.last = last;
18385                     }
18386                 }else{
18387                     this.selectFirstRow();
18388                 }
18389                 this.fireEvent("afterselectionchange", this);
18390             },
18391             scope: this
18392         });
18393
18394         var view = this.grid.view;
18395         view.on("refresh", this.onRefresh, this);
18396         view.on("rowupdated", this.onRowUpdated, this);
18397         view.on("rowremoved", this.onRemove, this);
18398     },
18399
18400     // private
18401     onRefresh : function(){
18402         var ds = this.grid.dataSource, i, v = this.grid.view;
18403         var s = this.selections;
18404         s.each(function(r){
18405             if((i = ds.indexOfId(r.id)) != -1){
18406                 v.onRowSelect(i);
18407             }else{
18408                 s.remove(r);
18409             }
18410         });
18411     },
18412
18413     // private
18414     onRemove : function(v, index, r){
18415         this.selections.remove(r);
18416     },
18417
18418     // private
18419     onRowUpdated : function(v, index, r){
18420         if(this.isSelected(r)){
18421             v.onRowSelect(index);
18422         }
18423     },
18424
18425     /**
18426      * Select records.
18427      * @param {Array} records The records to select
18428      * @param {Boolean} keepExisting (optional) True to keep existing selections
18429      */
18430     selectRecords : function(records, keepExisting){
18431         if(!keepExisting){
18432             this.clearSelections();
18433         }
18434         var ds = this.grid.dataSource;
18435         for(var i = 0, len = records.length; i < len; i++){
18436             this.selectRow(ds.indexOf(records[i]), true);
18437         }
18438     },
18439
18440     /**
18441      * Gets the number of selected rows.
18442      * @return {Number}
18443      */
18444     getCount : function(){
18445         return this.selections.length;
18446     },
18447
18448     /**
18449      * Selects the first row in the grid.
18450      */
18451     selectFirstRow : function(){
18452         this.selectRow(0);
18453     },
18454
18455     /**
18456      * Select the last row.
18457      * @param {Boolean} keepExisting (optional) True to keep existing selections
18458      */
18459     selectLastRow : function(keepExisting){
18460         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
18461     },
18462
18463     /**
18464      * Selects the row immediately following the last selected row.
18465      * @param {Boolean} keepExisting (optional) True to keep existing selections
18466      */
18467     selectNext : function(keepExisting){
18468         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
18469             this.selectRow(this.last+1, keepExisting);
18470             this.grid.getView().focusRow(this.last);
18471         }
18472     },
18473
18474     /**
18475      * Selects the row that precedes the last selected row.
18476      * @param {Boolean} keepExisting (optional) True to keep existing selections
18477      */
18478     selectPrevious : function(keepExisting){
18479         if(this.last){
18480             this.selectRow(this.last-1, keepExisting);
18481             this.grid.getView().focusRow(this.last);
18482         }
18483     },
18484
18485     /**
18486      * Returns the selected records
18487      * @return {Array} Array of selected records
18488      */
18489     getSelections : function(){
18490         return [].concat(this.selections.items);
18491     },
18492
18493     /**
18494      * Returns the first selected record.
18495      * @return {Record}
18496      */
18497     getSelected : function(){
18498         return this.selections.itemAt(0);
18499     },
18500
18501
18502     /**
18503      * Clears all selections.
18504      */
18505     clearSelections : function(fast){
18506         if(this.locked) return;
18507         if(fast !== true){
18508             var ds = this.grid.dataSource;
18509             var s = this.selections;
18510             s.each(function(r){
18511                 this.deselectRow(ds.indexOfId(r.id));
18512             }, this);
18513             s.clear();
18514         }else{
18515             this.selections.clear();
18516         }
18517         this.last = false;
18518     },
18519
18520
18521     /**
18522      * Selects all rows.
18523      */
18524     selectAll : function(){
18525         if(this.locked) return;
18526         this.selections.clear();
18527         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
18528             this.selectRow(i, true);
18529         }
18530     },
18531
18532     /**
18533      * Returns True if there is a selection.
18534      * @return {Boolean}
18535      */
18536     hasSelection : function(){
18537         return this.selections.length > 0;
18538     },
18539
18540     /**
18541      * Returns True if the specified row is selected.
18542      * @param {Number/Record} record The record or index of the record to check
18543      * @return {Boolean}
18544      */
18545     isSelected : function(index){
18546         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
18547         return (r && this.selections.key(r.id) ? true : false);
18548     },
18549
18550     /**
18551      * Returns True if the specified record id is selected.
18552      * @param {String} id The id of record to check
18553      * @return {Boolean}
18554      */
18555     isIdSelected : function(id){
18556         return (this.selections.key(id) ? true : false);
18557     },
18558
18559     // private
18560     handleMouseDown : function(e, t){
18561         var view = this.grid.getView(), rowIndex;
18562         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
18563             return;
18564         };
18565         if(e.shiftKey && this.last !== false){
18566             var last = this.last;
18567             this.selectRange(last, rowIndex, e.ctrlKey);
18568             this.last = last; // reset the last
18569             view.focusRow(rowIndex);
18570         }else{
18571             var isSelected = this.isSelected(rowIndex);
18572             if(e.button !== 0 && isSelected){
18573                 view.focusRow(rowIndex);
18574             }else if(e.ctrlKey && isSelected){
18575                 this.deselectRow(rowIndex);
18576             }else if(!isSelected){
18577                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
18578                 view.focusRow(rowIndex);
18579             }
18580         }
18581         this.fireEvent("afterselectionchange", this);
18582     },
18583     // private
18584     handleDragableRowClick :  function(grid, rowIndex, e) 
18585     {
18586         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
18587             this.selectRow(rowIndex, false);
18588             grid.view.focusRow(rowIndex);
18589              this.fireEvent("afterselectionchange", this);
18590         }
18591     },
18592     
18593     /**
18594      * Selects multiple rows.
18595      * @param {Array} rows Array of the indexes of the row to select
18596      * @param {Boolean} keepExisting (optional) True to keep existing selections
18597      */
18598     selectRows : function(rows, keepExisting){
18599         if(!keepExisting){
18600             this.clearSelections();
18601         }
18602         for(var i = 0, len = rows.length; i < len; i++){
18603             this.selectRow(rows[i], true);
18604         }
18605     },
18606
18607     /**
18608      * Selects a range of rows. All rows in between startRow and endRow are also selected.
18609      * @param {Number} startRow The index of the first row in the range
18610      * @param {Number} endRow The index of the last row in the range
18611      * @param {Boolean} keepExisting (optional) True to retain existing selections
18612      */
18613     selectRange : function(startRow, endRow, keepExisting){
18614         if(this.locked) return;
18615         if(!keepExisting){
18616             this.clearSelections();
18617         }
18618         if(startRow <= endRow){
18619             for(var i = startRow; i <= endRow; i++){
18620                 this.selectRow(i, true);
18621             }
18622         }else{
18623             for(var i = startRow; i >= endRow; i--){
18624                 this.selectRow(i, true);
18625             }
18626         }
18627     },
18628
18629     /**
18630      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
18631      * @param {Number} startRow The index of the first row in the range
18632      * @param {Number} endRow The index of the last row in the range
18633      */
18634     deselectRange : function(startRow, endRow, preventViewNotify){
18635         if(this.locked) return;
18636         for(var i = startRow; i <= endRow; i++){
18637             this.deselectRow(i, preventViewNotify);
18638         }
18639     },
18640
18641     /**
18642      * Selects a row.
18643      * @param {Number} row The index of the row to select
18644      * @param {Boolean} keepExisting (optional) True to keep existing selections
18645      */
18646     selectRow : function(index, keepExisting, preventViewNotify){
18647         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
18648         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
18649             if(!keepExisting || this.singleSelect){
18650                 this.clearSelections();
18651             }
18652             var r = this.grid.dataSource.getAt(index);
18653             this.selections.add(r);
18654             this.last = this.lastActive = index;
18655             if(!preventViewNotify){
18656                 this.grid.getView().onRowSelect(index);
18657             }
18658             this.fireEvent("rowselect", this, index, r);
18659             this.fireEvent("selectionchange", this);
18660         }
18661     },
18662
18663     /**
18664      * Deselects a row.
18665      * @param {Number} row The index of the row to deselect
18666      */
18667     deselectRow : function(index, preventViewNotify){
18668         if(this.locked) return;
18669         if(this.last == index){
18670             this.last = false;
18671         }
18672         if(this.lastActive == index){
18673             this.lastActive = false;
18674         }
18675         var r = this.grid.dataSource.getAt(index);
18676         this.selections.remove(r);
18677         if(!preventViewNotify){
18678             this.grid.getView().onRowDeselect(index);
18679         }
18680         this.fireEvent("rowdeselect", this, index);
18681         this.fireEvent("selectionchange", this);
18682     },
18683
18684     // private
18685     restoreLast : function(){
18686         if(this._last){
18687             this.last = this._last;
18688         }
18689     },
18690
18691     // private
18692     acceptsNav : function(row, col, cm){
18693         return !cm.isHidden(col) && cm.isCellEditable(col, row);
18694     },
18695
18696     // private
18697     onEditorKey : function(field, e){
18698         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
18699         if(k == e.TAB){
18700             e.stopEvent();
18701             ed.completeEdit();
18702             if(e.shiftKey){
18703                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
18704             }else{
18705                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
18706             }
18707         }else if(k == e.ENTER && !e.ctrlKey){
18708             e.stopEvent();
18709             ed.completeEdit();
18710             if(e.shiftKey){
18711                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
18712             }else{
18713                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
18714             }
18715         }else if(k == e.ESC){
18716             ed.cancelEdit();
18717         }
18718         if(newCell){
18719             g.startEditing(newCell[0], newCell[1]);
18720         }
18721     }
18722 });/*
18723  * Based on:
18724  * Ext JS Library 1.1.1
18725  * Copyright(c) 2006-2007, Ext JS, LLC.
18726  *
18727  * Originally Released Under LGPL - original licence link has changed is not relivant.
18728  *
18729  * Fork - LGPL
18730  * <script type="text/javascript">
18731  */
18732  
18733 /**
18734  * @class Roo.bootstrap.PagingToolbar
18735  * @extends Roo.Row
18736  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
18737  * @constructor
18738  * Create a new PagingToolbar
18739  * @param {Object} config The config object
18740  */
18741 Roo.bootstrap.PagingToolbar = function(config)
18742 {
18743     // old args format still supported... - xtype is prefered..
18744         // created from xtype...
18745     var ds = config.dataSource;
18746     this.toolbarItems = [];
18747     if (config.items) {
18748         this.toolbarItems = config.items;
18749 //        config.items = [];
18750     }
18751     
18752     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
18753     this.ds = ds;
18754     this.cursor = 0;
18755     if (ds) { 
18756         this.bind(ds);
18757     }
18758     
18759     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
18760     
18761 };
18762
18763 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
18764     /**
18765      * @cfg {Roo.data.Store} dataSource
18766      * The underlying data store providing the paged data
18767      */
18768     /**
18769      * @cfg {String/HTMLElement/Element} container
18770      * container The id or element that will contain the toolbar
18771      */
18772     /**
18773      * @cfg {Boolean} displayInfo
18774      * True to display the displayMsg (defaults to false)
18775      */
18776     /**
18777      * @cfg {Number} pageSize
18778      * The number of records to display per page (defaults to 20)
18779      */
18780     pageSize: 20,
18781     /**
18782      * @cfg {String} displayMsg
18783      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
18784      */
18785     displayMsg : 'Displaying {0} - {1} of {2}',
18786     /**
18787      * @cfg {String} emptyMsg
18788      * The message to display when no records are found (defaults to "No data to display")
18789      */
18790     emptyMsg : 'No data to display',
18791     /**
18792      * Customizable piece of the default paging text (defaults to "Page")
18793      * @type String
18794      */
18795     beforePageText : "Page",
18796     /**
18797      * Customizable piece of the default paging text (defaults to "of %0")
18798      * @type String
18799      */
18800     afterPageText : "of {0}",
18801     /**
18802      * Customizable piece of the default paging text (defaults to "First Page")
18803      * @type String
18804      */
18805     firstText : "First Page",
18806     /**
18807      * Customizable piece of the default paging text (defaults to "Previous Page")
18808      * @type String
18809      */
18810     prevText : "Previous Page",
18811     /**
18812      * Customizable piece of the default paging text (defaults to "Next Page")
18813      * @type String
18814      */
18815     nextText : "Next Page",
18816     /**
18817      * Customizable piece of the default paging text (defaults to "Last Page")
18818      * @type String
18819      */
18820     lastText : "Last Page",
18821     /**
18822      * Customizable piece of the default paging text (defaults to "Refresh")
18823      * @type String
18824      */
18825     refreshText : "Refresh",
18826
18827     buttons : false,
18828     // private
18829     onRender : function(ct, position) 
18830     {
18831         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
18832         this.navgroup.parentId = this.id;
18833         this.navgroup.onRender(this.el, null);
18834         // add the buttons to the navgroup
18835         
18836         if(this.displayInfo){
18837             Roo.log(this.el.select('ul.navbar-nav',true).first());
18838             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
18839             this.displayEl = this.el.select('.x-paging-info', true).first();
18840 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
18841 //            this.displayEl = navel.el.select('span',true).first();
18842         }
18843         
18844         var _this = this;
18845         
18846         if(this.buttons){
18847             Roo.each(_this.buttons, function(e){
18848                Roo.factory(e).onRender(_this.el, null);
18849             });
18850         }
18851             
18852         Roo.each(_this.toolbarItems, function(e) {
18853             _this.navgroup.addItem(e);
18854         });
18855         
18856         this.first = this.navgroup.addItem({
18857             tooltip: this.firstText,
18858             cls: "prev",
18859             icon : 'fa fa-backward',
18860             disabled: true,
18861             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
18862         });
18863         
18864         this.prev =  this.navgroup.addItem({
18865             tooltip: this.prevText,
18866             cls: "prev",
18867             icon : 'fa fa-step-backward',
18868             disabled: true,
18869             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
18870         });
18871     //this.addSeparator();
18872         
18873         
18874         var field = this.navgroup.addItem( {
18875             tagtype : 'span',
18876             cls : 'x-paging-position',
18877             
18878             html : this.beforePageText  +
18879                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
18880                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
18881          } ); //?? escaped?
18882         
18883         this.field = field.el.select('input', true).first();
18884         this.field.on("keydown", this.onPagingKeydown, this);
18885         this.field.on("focus", function(){this.dom.select();});
18886     
18887     
18888         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
18889         //this.field.setHeight(18);
18890         //this.addSeparator();
18891         this.next = this.navgroup.addItem({
18892             tooltip: this.nextText,
18893             cls: "next",
18894             html : ' <i class="fa fa-step-forward">',
18895             disabled: true,
18896             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
18897         });
18898         this.last = this.navgroup.addItem({
18899             tooltip: this.lastText,
18900             icon : 'fa fa-forward',
18901             cls: "next",
18902             disabled: true,
18903             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
18904         });
18905     //this.addSeparator();
18906         this.loading = this.navgroup.addItem({
18907             tooltip: this.refreshText,
18908             icon: 'fa fa-refresh',
18909             
18910             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
18911         });
18912
18913     },
18914
18915     // private
18916     updateInfo : function(){
18917         if(this.displayEl){
18918             var count = this.ds.getCount();
18919             var msg = count == 0 ?
18920                 this.emptyMsg :
18921                 String.format(
18922                     this.displayMsg,
18923                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
18924                 );
18925             this.displayEl.update(msg);
18926         }
18927     },
18928
18929     // private
18930     onLoad : function(ds, r, o){
18931        this.cursor = o.params ? o.params.start : 0;
18932        var d = this.getPageData(),
18933             ap = d.activePage,
18934             ps = d.pages;
18935         
18936        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
18937        this.field.dom.value = ap;
18938        this.first.setDisabled(ap == 1);
18939        this.prev.setDisabled(ap == 1);
18940        this.next.setDisabled(ap == ps);
18941        this.last.setDisabled(ap == ps);
18942        this.loading.enable();
18943        this.updateInfo();
18944     },
18945
18946     // private
18947     getPageData : function(){
18948         var total = this.ds.getTotalCount();
18949         return {
18950             total : total,
18951             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
18952             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
18953         };
18954     },
18955
18956     // private
18957     onLoadError : function(){
18958         this.loading.enable();
18959     },
18960
18961     // private
18962     onPagingKeydown : function(e){
18963         var k = e.getKey();
18964         var d = this.getPageData();
18965         if(k == e.RETURN){
18966             var v = this.field.dom.value, pageNum;
18967             if(!v || isNaN(pageNum = parseInt(v, 10))){
18968                 this.field.dom.value = d.activePage;
18969                 return;
18970             }
18971             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
18972             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
18973             e.stopEvent();
18974         }
18975         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))
18976         {
18977           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
18978           this.field.dom.value = pageNum;
18979           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
18980           e.stopEvent();
18981         }
18982         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
18983         {
18984           var v = this.field.dom.value, pageNum; 
18985           var increment = (e.shiftKey) ? 10 : 1;
18986           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
18987             increment *= -1;
18988           if(!v || isNaN(pageNum = parseInt(v, 10))) {
18989             this.field.dom.value = d.activePage;
18990             return;
18991           }
18992           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
18993           {
18994             this.field.dom.value = parseInt(v, 10) + increment;
18995             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
18996             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
18997           }
18998           e.stopEvent();
18999         }
19000     },
19001
19002     // private
19003     beforeLoad : function(){
19004         if(this.loading){
19005             this.loading.disable();
19006         }
19007     },
19008
19009     // private
19010     onClick : function(which){
19011         var ds = this.ds;
19012         if (!ds) {
19013             return;
19014         }
19015         switch(which){
19016             case "first":
19017                 ds.load({params:{start: 0, limit: this.pageSize}});
19018             break;
19019             case "prev":
19020                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
19021             break;
19022             case "next":
19023                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
19024             break;
19025             case "last":
19026                 var total = ds.getTotalCount();
19027                 var extra = total % this.pageSize;
19028                 var lastStart = extra ? (total - extra) : total-this.pageSize;
19029                 ds.load({params:{start: lastStart, limit: this.pageSize}});
19030             break;
19031             case "refresh":
19032                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
19033             break;
19034         }
19035     },
19036
19037     /**
19038      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
19039      * @param {Roo.data.Store} store The data store to unbind
19040      */
19041     unbind : function(ds){
19042         ds.un("beforeload", this.beforeLoad, this);
19043         ds.un("load", this.onLoad, this);
19044         ds.un("loadexception", this.onLoadError, this);
19045         ds.un("remove", this.updateInfo, this);
19046         ds.un("add", this.updateInfo, this);
19047         this.ds = undefined;
19048     },
19049
19050     /**
19051      * Binds the paging toolbar to the specified {@link Roo.data.Store}
19052      * @param {Roo.data.Store} store The data store to bind
19053      */
19054     bind : function(ds){
19055         ds.on("beforeload", this.beforeLoad, this);
19056         ds.on("load", this.onLoad, this);
19057         ds.on("loadexception", this.onLoadError, this);
19058         ds.on("remove", this.updateInfo, this);
19059         ds.on("add", this.updateInfo, this);
19060         this.ds = ds;
19061     }
19062 });/*
19063  * - LGPL
19064  *
19065  * element
19066  * 
19067  */
19068
19069 /**
19070  * @class Roo.bootstrap.MessageBar
19071  * @extends Roo.bootstrap.Component
19072  * Bootstrap MessageBar class
19073  * @cfg {String} html contents of the MessageBar
19074  * @cfg {String} weight (info | success | warning | danger) default info
19075  * @cfg {String} beforeClass insert the bar before the given class
19076  * @cfg {Boolean} closable (true | false) default false
19077  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
19078  * 
19079  * @constructor
19080  * Create a new Element
19081  * @param {Object} config The config object
19082  */
19083
19084 Roo.bootstrap.MessageBar = function(config){
19085     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
19086 };
19087
19088 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
19089     
19090     html: '',
19091     weight: 'info',
19092     closable: false,
19093     fixed: false,
19094     beforeClass: 'bootstrap-sticky-wrap',
19095     
19096     getAutoCreate : function(){
19097         
19098         var cfg = {
19099             tag: 'div',
19100             cls: 'alert alert-dismissable alert-' + this.weight,
19101             cn: [
19102                 {
19103                     tag: 'span',
19104                     cls: 'message',
19105                     html: this.html || ''
19106                 }
19107             ]
19108         }
19109         
19110         if(this.fixed){
19111             cfg.cls += ' alert-messages-fixed';
19112         }
19113         
19114         if(this.closable){
19115             cfg.cn.push({
19116                 tag: 'button',
19117                 cls: 'close',
19118                 html: 'x'
19119             });
19120         }
19121         
19122         return cfg;
19123     },
19124     
19125     onRender : function(ct, position)
19126     {
19127         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19128         
19129         if(!this.el){
19130             var cfg = Roo.apply({},  this.getAutoCreate());
19131             cfg.id = Roo.id();
19132             
19133             if (this.cls) {
19134                 cfg.cls += ' ' + this.cls;
19135             }
19136             if (this.style) {
19137                 cfg.style = this.style;
19138             }
19139             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
19140             
19141             this.el.setVisibilityMode(Roo.Element.DISPLAY);
19142         }
19143         
19144         this.el.select('>button.close').on('click', this.hide, this);
19145         
19146     },
19147     
19148     show : function()
19149     {
19150         if (!this.rendered) {
19151             this.render();
19152         }
19153         
19154         this.el.show();
19155         
19156         this.fireEvent('show', this);
19157         
19158     },
19159     
19160     hide : function()
19161     {
19162         if (!this.rendered) {
19163             this.render();
19164         }
19165         
19166         this.el.hide();
19167         
19168         this.fireEvent('hide', this);
19169     },
19170     
19171     update : function()
19172     {
19173 //        var e = this.el.dom.firstChild;
19174 //        
19175 //        if(this.closable){
19176 //            e = e.nextSibling;
19177 //        }
19178 //        
19179 //        e.data = this.html || '';
19180
19181         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
19182     }
19183    
19184 });
19185
19186  
19187
19188      /*
19189  * - LGPL
19190  *
19191  * Graph
19192  * 
19193  */
19194
19195
19196 /**
19197  * @class Roo.bootstrap.Graph
19198  * @extends Roo.bootstrap.Component
19199  * Bootstrap Graph class
19200 > Prameters
19201  -sm {number} sm 4
19202  -md {number} md 5
19203  @cfg {String} graphtype  bar | vbar | pie
19204  @cfg {number} g_x coodinator | centre x (pie)
19205  @cfg {number} g_y coodinator | centre y (pie)
19206  @cfg {number} g_r radius (pie)
19207  @cfg {number} g_height height of the chart (respected by all elements in the set)
19208  @cfg {number} g_width width of the chart (respected by all elements in the set)
19209  @cfg {Object} title The title of the chart
19210     
19211  -{Array}  values
19212  -opts (object) options for the chart 
19213      o {
19214      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
19215      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
19216      o vgutter (number)
19217      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.
19218      o stacked (boolean) whether or not to tread values as in a stacked bar chart
19219      o to
19220      o stretch (boolean)
19221      o }
19222  -opts (object) options for the pie
19223      o{
19224      o cut
19225      o startAngle (number)
19226      o endAngle (number)
19227      } 
19228  *
19229  * @constructor
19230  * Create a new Input
19231  * @param {Object} config The config object
19232  */
19233
19234 Roo.bootstrap.Graph = function(config){
19235     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
19236     
19237     this.addEvents({
19238         // img events
19239         /**
19240          * @event click
19241          * The img click event for the img.
19242          * @param {Roo.EventObject} e
19243          */
19244         "click" : true
19245     });
19246 };
19247
19248 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
19249     
19250     sm: 4,
19251     md: 5,
19252     graphtype: 'bar',
19253     g_height: 250,
19254     g_width: 400,
19255     g_x: 50,
19256     g_y: 50,
19257     g_r: 30,
19258     opts:{
19259         //g_colors: this.colors,
19260         g_type: 'soft',
19261         g_gutter: '20%'
19262
19263     },
19264     title : false,
19265
19266     getAutoCreate : function(){
19267         
19268         var cfg = {
19269             tag: 'div',
19270             html : null
19271         }
19272         
19273         
19274         return  cfg;
19275     },
19276
19277     onRender : function(ct,position){
19278         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
19279         this.raphael = Raphael(this.el.dom);
19280         
19281                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19282                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19283                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19284                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
19285                 /*
19286                 r.text(160, 10, "Single Series Chart").attr(txtattr);
19287                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
19288                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
19289                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
19290                 
19291                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
19292                 r.barchart(330, 10, 300, 220, data1);
19293                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
19294                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
19295                 */
19296                 
19297                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19298                 // r.barchart(30, 30, 560, 250,  xdata, {
19299                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
19300                 //     axis : "0 0 1 1",
19301                 //     axisxlabels :  xdata
19302                 //     //yvalues : cols,
19303                    
19304                 // });
19305 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19306 //        
19307 //        this.load(null,xdata,{
19308 //                axis : "0 0 1 1",
19309 //                axisxlabels :  xdata
19310 //                });
19311
19312     },
19313
19314     load : function(graphtype,xdata,opts){
19315         this.raphael.clear();
19316         if(!graphtype) {
19317             graphtype = this.graphtype;
19318         }
19319         if(!opts){
19320             opts = this.opts;
19321         }
19322         var r = this.raphael,
19323             fin = function () {
19324                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
19325             },
19326             fout = function () {
19327                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
19328             },
19329             pfin = function() {
19330                 this.sector.stop();
19331                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
19332
19333                 if (this.label) {
19334                     this.label[0].stop();
19335                     this.label[0].attr({ r: 7.5 });
19336                     this.label[1].attr({ "font-weight": 800 });
19337                 }
19338             },
19339             pfout = function() {
19340                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
19341
19342                 if (this.label) {
19343                     this.label[0].animate({ r: 5 }, 500, "bounce");
19344                     this.label[1].attr({ "font-weight": 400 });
19345                 }
19346             };
19347
19348         switch(graphtype){
19349             case 'bar':
19350                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
19351                 break;
19352             case 'hbar':
19353                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
19354                 break;
19355             case 'pie':
19356 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
19357 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
19358 //            
19359                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
19360                 
19361                 break;
19362
19363         }
19364         
19365         if(this.title){
19366             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
19367         }
19368         
19369     },
19370     
19371     setTitle: function(o)
19372     {
19373         this.title = o;
19374     },
19375     
19376     initEvents: function() {
19377         
19378         if(!this.href){
19379             this.el.on('click', this.onClick, this);
19380         }
19381     },
19382     
19383     onClick : function(e)
19384     {
19385         Roo.log('img onclick');
19386         this.fireEvent('click', this, e);
19387     }
19388    
19389 });
19390
19391  
19392 /*
19393  * - LGPL
19394  *
19395  * numberBox
19396  * 
19397  */
19398 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19399
19400 /**
19401  * @class Roo.bootstrap.dash.NumberBox
19402  * @extends Roo.bootstrap.Component
19403  * Bootstrap NumberBox class
19404  * @cfg {String} bgcolor Box background color, such as (aqua | green | yellow | red etc..) Default aqua
19405  * @cfg {String} headline Box headline
19406  * @cfg {String} content Box content
19407  * @cfg {String} icon Box icon
19408  * @cfg {String} footer Footer text
19409  * @cfg {String} fhref Footer href
19410  * 
19411  * @constructor
19412  * Create a new NumberBox
19413  * @param {Object} config The config object
19414  */
19415
19416
19417 Roo.bootstrap.dash.NumberBox = function(config){
19418     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
19419     
19420 };
19421
19422 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
19423     
19424     bgcolor : 'aqua',
19425     headline : '',
19426     content : '',
19427     icon : '',
19428     footer : '',
19429     fhref : '',
19430     ficon : '',
19431     
19432     getAutoCreate : function(){
19433         
19434         var cfg = {
19435             tag : 'div',
19436             cls : 'small-box bg-' + this.bgcolor,
19437             cn : [
19438                 {
19439                     tag : 'div',
19440                     cls : 'inner',
19441                     cn :[
19442                         {
19443                             tag : 'h3',
19444                             cls : 'roo-headline',
19445                             html : this.headline
19446                         },
19447                         {
19448                             tag : 'p',
19449                             cls : 'roo-content',
19450                             html : this.content
19451                         }
19452                     ]
19453                 }
19454             ]
19455         }
19456         
19457         if(this.icon){
19458             cfg.cn.push({
19459                 tag : 'div',
19460                 cls : 'icon',
19461                 cn :[
19462                     {
19463                         tag : 'i',
19464                         cls : 'ion ' + this.icon
19465                     }
19466                 ]
19467             });
19468         }
19469         
19470         if(this.footer){
19471             var footer = {
19472                 tag : 'a',
19473                 cls : 'small-box-footer',
19474                 href : this.fhref || '#',
19475                 html : this.footer
19476             };
19477             
19478             cfg.cn.push(footer);
19479             
19480         }
19481         
19482         return  cfg;
19483     },
19484
19485     onRender : function(ct,position){
19486         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
19487
19488
19489        
19490                 
19491     },
19492
19493     setHeadline: function (value)
19494     {
19495         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
19496     },
19497     
19498     setFooter: function (value, href)
19499     {
19500         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
19501         
19502         if(href){
19503             this.el.select('a.small-box-footer',true).first().attr('href', href);
19504         }
19505         
19506     },
19507
19508     setContent: function (value)
19509     {
19510         this.el.select('.roo-content',true).first().dom.innerHTML = value;
19511     },
19512
19513     initEvents: function() 
19514     {   
19515         
19516     }
19517     
19518 });
19519
19520  
19521 /*
19522  * - LGPL
19523  *
19524  * TabBox
19525  * 
19526  */
19527 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19528
19529 /**
19530  * @class Roo.bootstrap.dash.TabBox
19531  * @extends Roo.bootstrap.Component
19532  * Bootstrap TabBox class
19533  * @cfg {String} title Title of the TabBox
19534  * @cfg {String} icon Icon of the TabBox
19535  * 
19536  * @constructor
19537  * Create a new TabBox
19538  * @param {Object} config The config object
19539  */
19540
19541
19542 Roo.bootstrap.dash.TabBox = function(config){
19543     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
19544     this.addEvents({
19545         // raw events
19546         /**
19547          * @event addpane
19548          * When a pane is added
19549          * @param {Roo.bootstrap.dash.TabPane} pane
19550          */
19551         "addpane" : true
19552          
19553     });
19554 };
19555
19556 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
19557
19558     title : '',
19559     icon : false,
19560     
19561     getChildContainer : function()
19562     {
19563         return this.el.select('.tab-content', true).first();
19564     },
19565     
19566     getAutoCreate : function(){
19567         
19568         var header = {
19569             tag: 'li',
19570             cls: 'pull-left header',
19571             html: this.title,
19572             cn : []
19573         };
19574         
19575         if(this.icon){
19576             header.cn.push({
19577                 tag: 'i',
19578                 cls: 'fa ' + this.icon
19579             });
19580         }
19581         
19582         
19583         var cfg = {
19584             tag: 'div',
19585             cls: 'nav-tabs-custom',
19586             cn: [
19587                 {
19588                     tag: 'ul',
19589                     cls: 'nav nav-tabs pull-right',
19590                     cn: [
19591                         header
19592                     ]
19593                 },
19594                 {
19595                     tag: 'div',
19596                     cls: 'tab-content no-padding',
19597                     cn: []
19598                 }
19599             ]
19600         }
19601
19602         return  cfg;
19603     },
19604     initEvents : function()
19605     {
19606         //Roo.log('add add pane handler');
19607         this.on('addpane', this.onAddPane, this);
19608     },
19609      /**
19610      * Updates the box title
19611      * @param {String} html to set the title to.
19612      */
19613     setTitle : function(value)
19614     {
19615         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
19616     },
19617     onAddPane : function(pane)
19618     {
19619         //Roo.log('addpane');
19620         //Roo.log(pane);
19621         // tabs are rendere left to right..
19622         var ctr = this.el.select('.nav-tabs', true).first();
19623          
19624          
19625         var existing = ctr.select('.nav-tab',true);
19626         var qty = existing.getCount();;
19627         
19628         
19629         var tab = ctr.createChild({
19630             tag : 'li',
19631             cls : 'nav-tab' + (qty ? '' : ' active'),
19632             cn : [
19633                 {
19634                     tag : 'a',
19635                     href:'#',
19636                     html : pane.title
19637                 }
19638             ]
19639         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
19640         pane.tab = tab;
19641         
19642         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
19643         if (!qty) {
19644             pane.el.addClass('active');
19645         }
19646         
19647                 
19648     },
19649     onTabClick : function(ev,un,ob,pane)
19650     {
19651         //Roo.log('tab - prev default');
19652         ev.preventDefault();
19653         
19654         
19655         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
19656         pane.tab.addClass('active');
19657         //Roo.log(pane.title);
19658         this.getChildContainer().select('.tab-pane',true).removeClass('active');
19659         // technically we should have a deactivate event.. but maybe add later.
19660         // and it should not de-activate the selected tab...
19661         
19662         pane.el.addClass('active');
19663         pane.fireEvent('activate');
19664         
19665         
19666     }
19667     
19668     
19669 });
19670
19671  
19672 /*
19673  * - LGPL
19674  *
19675  * Tab pane
19676  * 
19677  */
19678 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19679 /**
19680  * @class Roo.bootstrap.TabPane
19681  * @extends Roo.bootstrap.Component
19682  * Bootstrap TabPane class
19683  * @cfg {Boolean} active (false | true) Default false
19684  * @cfg {String} title title of panel
19685
19686  * 
19687  * @constructor
19688  * Create a new TabPane
19689  * @param {Object} config The config object
19690  */
19691
19692 Roo.bootstrap.dash.TabPane = function(config){
19693     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
19694     
19695 };
19696
19697 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
19698     
19699     active : false,
19700     title : '',
19701     
19702     // the tabBox that this is attached to.
19703     tab : false,
19704      
19705     getAutoCreate : function() 
19706     {
19707         var cfg = {
19708             tag: 'div',
19709             cls: 'tab-pane'
19710         }
19711         
19712         if(this.active){
19713             cfg.cls += ' active';
19714         }
19715         
19716         return cfg;
19717     },
19718     initEvents  : function()
19719     {
19720         //Roo.log('trigger add pane handler');
19721         this.parent().fireEvent('addpane', this)
19722     },
19723     
19724      /**
19725      * Updates the tab title 
19726      * @param {String} html to set the title to.
19727      */
19728     setTitle: function(str)
19729     {
19730         if (!this.tab) {
19731             return;
19732         }
19733         this.title = str;
19734         this.tab.select('a'.true).first().dom.innerHTML = str;
19735         
19736     }
19737     
19738     
19739     
19740 });
19741
19742  
19743
19744
19745  /*
19746  * - LGPL
19747  *
19748  * menu
19749  * 
19750  */
19751 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
19752
19753 /**
19754  * @class Roo.bootstrap.menu.Menu
19755  * @extends Roo.bootstrap.Component
19756  * Bootstrap Menu class - container for Menu
19757  * @cfg {String} html Text of the menu
19758  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
19759  * @cfg {String} icon Font awesome icon
19760  * @cfg {String} pos Menu align to (top | bottom) default bottom
19761  * 
19762  * 
19763  * @constructor
19764  * Create a new Menu
19765  * @param {Object} config The config object
19766  */
19767
19768
19769 Roo.bootstrap.menu.Menu = function(config){
19770     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
19771     
19772     this.addEvents({
19773         /**
19774          * @event beforeshow
19775          * Fires before this menu is displayed
19776          * @param {Roo.bootstrap.menu.Menu} this
19777          */
19778         beforeshow : true,
19779         /**
19780          * @event beforehide
19781          * Fires before this menu is hidden
19782          * @param {Roo.bootstrap.menu.Menu} this
19783          */
19784         beforehide : true,
19785         /**
19786          * @event show
19787          * Fires after this menu is displayed
19788          * @param {Roo.bootstrap.menu.Menu} this
19789          */
19790         show : true,
19791         /**
19792          * @event hide
19793          * Fires after this menu is hidden
19794          * @param {Roo.bootstrap.menu.Menu} this
19795          */
19796         hide : true,
19797         /**
19798          * @event click
19799          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19800          * @param {Roo.bootstrap.menu.Menu} this
19801          * @param {Roo.EventObject} e
19802          */
19803         click : true
19804     });
19805     
19806 };
19807
19808 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
19809     
19810     submenu : false,
19811     html : '',
19812     weight : 'default',
19813     icon : false,
19814     pos : 'bottom',
19815     
19816     
19817     getChildContainer : function() {
19818         if(this.isSubMenu){
19819             return this.el;
19820         }
19821         
19822         return this.el.select('ul.dropdown-menu', true).first();  
19823     },
19824     
19825     getAutoCreate : function()
19826     {
19827         var text = [
19828             {
19829                 tag : 'span',
19830                 cls : 'roo-menu-text',
19831                 html : this.html
19832             }
19833         ];
19834         
19835         if(this.icon){
19836             text.unshift({
19837                 tag : 'i',
19838                 cls : 'fa ' + this.icon
19839             })
19840         }
19841         
19842         
19843         var cfg = {
19844             tag : 'div',
19845             cls : 'btn-group',
19846             cn : [
19847                 {
19848                     tag : 'button',
19849                     cls : 'dropdown-button btn btn-' + this.weight,
19850                     cn : text
19851                 },
19852                 {
19853                     tag : 'button',
19854                     cls : 'dropdown-toggle btn btn-' + this.weight,
19855                     cn : [
19856                         {
19857                             tag : 'span',
19858                             cls : 'caret'
19859                         }
19860                     ]
19861                 },
19862                 {
19863                     tag : 'ul',
19864                     cls : 'dropdown-menu'
19865                 }
19866             ]
19867             
19868         };
19869         
19870         if(this.pos == 'top'){
19871             cfg.cls += ' dropup';
19872         }
19873         
19874         if(this.isSubMenu){
19875             cfg = {
19876                 tag : 'ul',
19877                 cls : 'dropdown-menu'
19878             }
19879         }
19880         
19881         return cfg;
19882     },
19883     
19884     onRender : function(ct, position)
19885     {
19886         this.isSubMenu = ct.hasClass('dropdown-submenu');
19887         
19888         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
19889     },
19890     
19891     initEvents : function() 
19892     {
19893         if(this.isSubMenu){
19894             return;
19895         }
19896         
19897         this.hidden = true;
19898         
19899         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
19900         this.triggerEl.on('click', this.onTriggerPress, this);
19901         
19902         this.buttonEl = this.el.select('button.dropdown-button', true).first();
19903         this.buttonEl.on('click', this.onClick, this);
19904         
19905     },
19906     
19907     list : function()
19908     {
19909         if(this.isSubMenu){
19910             return this.el;
19911         }
19912         
19913         return this.el.select('ul.dropdown-menu', true).first();
19914     },
19915     
19916     onClick : function(e)
19917     {
19918         this.fireEvent("click", this, e);
19919     },
19920     
19921     onTriggerPress  : function(e)
19922     {   
19923         if (this.isVisible()) {
19924             this.hide();
19925         } else {
19926             this.show();
19927         }
19928     },
19929     
19930     isVisible : function(){
19931         return !this.hidden;
19932     },
19933     
19934     show : function()
19935     {
19936         this.fireEvent("beforeshow", this);
19937         
19938         this.hidden = false;
19939         this.el.addClass('open');
19940         
19941         Roo.get(document).on("mouseup", this.onMouseUp, this);
19942         
19943         this.fireEvent("show", this);
19944         
19945         
19946     },
19947     
19948     hide : function()
19949     {
19950         this.fireEvent("beforehide", this);
19951         
19952         this.hidden = true;
19953         this.el.removeClass('open');
19954         
19955         Roo.get(document).un("mouseup", this.onMouseUp);
19956         
19957         this.fireEvent("hide", this);
19958     },
19959     
19960     onMouseUp : function()
19961     {
19962         this.hide();
19963     }
19964     
19965 });
19966
19967  
19968  /*
19969  * - LGPL
19970  *
19971  * menu item
19972  * 
19973  */
19974 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
19975
19976 /**
19977  * @class Roo.bootstrap.menu.Item
19978  * @extends Roo.bootstrap.Component
19979  * Bootstrap MenuItem class
19980  * @cfg {Boolean} submenu (true | false) default false
19981  * @cfg {String} html text of the item
19982  * @cfg {String} href the link
19983  * @cfg {Boolean} disable (true | false) default false
19984  * @cfg {Boolean} preventDefault (true | false) default true
19985  * @cfg {String} icon Font awesome icon
19986  * @cfg {String} pos Submenu align to (left | right) default right 
19987  * 
19988  * 
19989  * @constructor
19990  * Create a new Item
19991  * @param {Object} config The config object
19992  */
19993
19994
19995 Roo.bootstrap.menu.Item = function(config){
19996     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
19997     this.addEvents({
19998         /**
19999          * @event mouseover
20000          * Fires when the mouse is hovering over this menu
20001          * @param {Roo.bootstrap.menu.Item} this
20002          * @param {Roo.EventObject} e
20003          */
20004         mouseover : true,
20005         /**
20006          * @event mouseout
20007          * Fires when the mouse exits this menu
20008          * @param {Roo.bootstrap.menu.Item} this
20009          * @param {Roo.EventObject} e
20010          */
20011         mouseout : true,
20012         // raw events
20013         /**
20014          * @event click
20015          * The raw click event for the entire grid.
20016          * @param {Roo.EventObject} e
20017          */
20018         click : true
20019     });
20020 };
20021
20022 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
20023     
20024     submenu : false,
20025     href : '',
20026     html : '',
20027     preventDefault: true,
20028     disable : false,
20029     icon : false,
20030     pos : 'right',
20031     
20032     getAutoCreate : function()
20033     {
20034         var text = [
20035             {
20036                 tag : 'span',
20037                 cls : 'roo-menu-item-text',
20038                 html : this.html
20039             }
20040         ];
20041         
20042         if(this.icon){
20043             text.unshift({
20044                 tag : 'i',
20045                 cls : 'fa ' + this.icon
20046             })
20047         }
20048         
20049         var cfg = {
20050             tag : 'li',
20051             cn : [
20052                 {
20053                     tag : 'a',
20054                     href : this.href || '#',
20055                     cn : text
20056                 }
20057             ]
20058         };
20059         
20060         if(this.disable){
20061             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
20062         }
20063         
20064         if(this.submenu){
20065             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
20066             
20067             if(this.pos == 'left'){
20068                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
20069             }
20070         }
20071         
20072         return cfg;
20073     },
20074     
20075     initEvents : function() 
20076     {
20077         this.el.on('mouseover', this.onMouseOver, this);
20078         this.el.on('mouseout', this.onMouseOut, this);
20079         
20080         this.el.select('a', true).first().on('click', this.onClick, this);
20081         
20082     },
20083     
20084     onClick : function(e)
20085     {
20086         if(this.preventDefault){
20087             e.preventDefault();
20088         }
20089         
20090         this.fireEvent("click", this, e);
20091     },
20092     
20093     onMouseOver : function(e)
20094     {
20095         if(this.submenu && this.pos == 'left'){
20096             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
20097         }
20098         
20099         this.fireEvent("mouseover", this, e);
20100     },
20101     
20102     onMouseOut : function(e)
20103     {
20104         this.fireEvent("mouseout", this, e);
20105     }
20106 });
20107
20108  
20109
20110  /*
20111  * - LGPL
20112  *
20113  * menu separator
20114  * 
20115  */
20116 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20117
20118 /**
20119  * @class Roo.bootstrap.menu.Separator
20120  * @extends Roo.bootstrap.Component
20121  * Bootstrap Separator class
20122  * 
20123  * @constructor
20124  * Create a new Separator
20125  * @param {Object} config The config object
20126  */
20127
20128
20129 Roo.bootstrap.menu.Separator = function(config){
20130     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
20131 };
20132
20133 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
20134     
20135     getAutoCreate : function(){
20136         var cfg = {
20137             tag : 'li',
20138             cls: 'divider'
20139         };
20140         
20141         return cfg;
20142     }
20143    
20144 });
20145
20146  
20147
20148