Roo/form/ComboBoxArray.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 or 0 for hidden
774  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
775  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
776  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
777  * @cfg {Boolean} hidden (true|false) hide the element
778  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
779  * @cfg {String} fa (ban|check|...) font awesome icon
780  * @cfg {String} icon (info-sign|check|...) glyphicon name
781
782  * @cfg {String} html content of column.
783  * 
784  * @constructor
785  * Create a new Column
786  * @param {Object} config The config object
787  */
788
789 Roo.bootstrap.Column = function(config){
790     Roo.bootstrap.Column.superclass.constructor.call(this, config);
791 };
792
793 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
794     
795     xs: false,
796     sm: false,
797     md: false,
798     lg: false,
799     html: '',
800     offset: 0,
801     alert: false,
802     fa: false,
803     icon : false,
804     hidden : false,
805     
806     getAutoCreate : function(){
807         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
808         
809         cfg = {
810             tag: 'div',
811             cls: 'column'
812         };
813         
814         var settings=this;
815         ['xs','sm','md','lg'].map(function(size){
816             Roo.log( size + ':' + settings[size]);
817             if (settings[size] === false) {
818                 return;
819             }
820             Roo.log(settings[size]);
821             if (!settings[size]) { // 0 = hidden
822                 cfg.cls += ' hidden-' + size;
823                 return;
824             }
825             cfg.cls += ' col-' + size + '-' + settings[size];
826             
827         });
828         
829         if (this.hidden) {
830             cfg.cls += ' hidden';
831         }
832         
833         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
834             cfg.cls +=' alert alert-' + this.alert;
835         }
836         
837         
838         if (this.html.length) {
839             cfg.html = this.html;
840         }
841         if (this.fa) {
842             cfg.html = '<i class="fa fa-'+this.fa + '"></i>' + (cfg.html || '');
843         }
844         if (this.icon) {
845             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + + (cfg.html || '')
846         }
847         
848         return cfg;
849     }
850    
851 });
852
853  
854
855  /*
856  * - LGPL
857  *
858  * page container.
859  * 
860  */
861
862
863 /**
864  * @class Roo.bootstrap.Container
865  * @extends Roo.bootstrap.Component
866  * Bootstrap Container class
867  * @cfg {Boolean} jumbotron is it a jumbotron element
868  * @cfg {String} html content of element
869  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
870  * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
871  * @cfg {String} header content of header (for panel)
872  * @cfg {String} footer content of footer (for panel)
873  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
874  * @cfg {String} tag (header|aside|section) type of HTML tag.
875  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
876  * @cfg {String} fa (ban|check|...) font awesome icon
877  * @cfg {String} icon (info-sign|check|...) glyphicon name
878  * @cfg {Boolean} hidden (true|false) hide the element
879
880  *     
881  * @constructor
882  * Create a new Container
883  * @param {Object} config The config object
884  */
885
886 Roo.bootstrap.Container = function(config){
887     Roo.bootstrap.Container.superclass.constructor.call(this, config);
888 };
889
890 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
891     
892     jumbotron : false,
893     well: '',
894     panel : '',
895     header: '',
896     footer : '',
897     sticky: '',
898     tag : false,
899     alert : false,
900     fa: false,
901     icon : false,
902   
903      
904     getChildContainer : function() {
905         
906         if(!this.el){
907             return false;
908         }
909         
910         if (this.panel.length) {
911             return this.el.select('.panel-body',true).first();
912         }
913         
914         return this.el;
915     },
916     
917     
918     getAutoCreate : function(){
919         
920         var cfg = {
921             tag : this.tag || 'div',
922             html : '',
923             cls : ''
924         };
925         if (this.jumbotron) {
926             cfg.cls = 'jumbotron';
927         }
928         
929         
930         
931         // - this is applied by the parent..
932         //if (this.cls) {
933         //    cfg.cls = this.cls + '';
934         //}
935         
936         if (this.sticky.length) {
937             
938             var bd = Roo.get(document.body);
939             if (!bd.hasClass('bootstrap-sticky')) {
940                 bd.addClass('bootstrap-sticky');
941                 Roo.select('html',true).setStyle('height', '100%');
942             }
943              
944             cfg.cls += 'bootstrap-sticky-' + this.sticky;
945         }
946         
947         
948         if (this.well.length) {
949             switch (this.well) {
950                 case 'lg':
951                 case 'sm':
952                     cfg.cls +=' well well-' +this.well;
953                     break;
954                 default:
955                     cfg.cls +=' well';
956                     break;
957             }
958         }
959         
960         if (this.hidden) {
961             cfg.cls += ' hidden';
962         }
963         
964         
965         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
966             cfg.cls +=' alert alert-' + this.alert;
967         }
968         
969         var body = cfg;
970         
971         if (this.panel.length) {
972             cfg.cls += ' panel panel-' + this.panel;
973             cfg.cn = [];
974             if (this.header.length) {
975                 cfg.cn.push({
976                     
977                     cls : 'panel-heading',
978                     cn : [{
979                         tag: 'h3',
980                         cls : 'panel-title',
981                         html : this.header
982                     }]
983                     
984                 });
985             }
986             body = false;
987             cfg.cn.push({
988                 cls : 'panel-body',
989                 html : this.html
990             });
991             
992             
993             if (this.footer.length) {
994                 cfg.cn.push({
995                     cls : 'panel-footer',
996                     html : this.footer
997                     
998                 });
999             }
1000             
1001         }
1002         
1003         if (body) {
1004             body.html = this.html || cfg.html;
1005             // prefix with the icons..
1006             if (this.fa) {
1007                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1008             }
1009             if (this.icon) {
1010                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1011             }
1012             
1013             
1014         }
1015         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1016             cfg.cls =  'container';
1017         }
1018         
1019         return cfg;
1020     },
1021     
1022     titleEl : function()
1023     {
1024         if(!this.el || !this.panel.length || !this.header.length){
1025             return;
1026         }
1027         
1028         return this.el.select('.panel-title',true).first();
1029     },
1030     
1031     setTitle : function(v)
1032     {
1033         var titleEl = this.titleEl();
1034         
1035         if(!titleEl){
1036             return;
1037         }
1038         
1039         titleEl.dom.innerHTML = v;
1040     },
1041     
1042     getTitle : function()
1043     {
1044         
1045         var titleEl = this.titleEl();
1046         
1047         if(!titleEl){
1048             return '';
1049         }
1050         
1051         return titleEl.dom.innerHTML;
1052     }
1053    
1054 });
1055
1056  /*
1057  * - LGPL
1058  *
1059  * image
1060  * 
1061  */
1062
1063
1064 /**
1065  * @class Roo.bootstrap.Img
1066  * @extends Roo.bootstrap.Component
1067  * Bootstrap Img class
1068  * @cfg {Boolean} imgResponsive false | true
1069  * @cfg {String} border rounded | circle | thumbnail
1070  * @cfg {String} src image source
1071  * @cfg {String} alt image alternative text
1072  * @cfg {String} href a tag href
1073  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1074  * 
1075  * @constructor
1076  * Create a new Input
1077  * @param {Object} config The config object
1078  */
1079
1080 Roo.bootstrap.Img = function(config){
1081     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1082     
1083     this.addEvents({
1084         // img events
1085         /**
1086          * @event click
1087          * The img click event for the img.
1088          * @param {Roo.EventObject} e
1089          */
1090         "click" : true
1091     });
1092 };
1093
1094 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1095     
1096     imgResponsive: true,
1097     border: '',
1098     src: '',
1099     href: false,
1100     target: false,
1101
1102     getAutoCreate : function(){
1103         
1104         var cfg = {
1105             tag: 'img',
1106             cls: (this.imgResponsive) ? 'img-responsive' : '',
1107             html : null
1108         }
1109         
1110         cfg.html = this.html || cfg.html;
1111         
1112         cfg.src = this.src || cfg.src;
1113         
1114         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1115             cfg.cls += ' img-' + this.border;
1116         }
1117         
1118         if(this.alt){
1119             cfg.alt = this.alt;
1120         }
1121         
1122         if(this.href){
1123             var a = {
1124                 tag: 'a',
1125                 href: this.href,
1126                 cn: [
1127                     cfg
1128                 ]
1129             }
1130             
1131             if(this.target){
1132                 a.target = this.target;
1133             }
1134             
1135         }
1136         
1137         
1138         return (this.href) ? a : cfg;
1139     },
1140     
1141     initEvents: function() {
1142         
1143         if(!this.href){
1144             this.el.on('click', this.onClick, this);
1145         }
1146     },
1147     
1148     onClick : function(e)
1149     {
1150         Roo.log('img onclick');
1151         this.fireEvent('click', this, e);
1152     }
1153    
1154 });
1155
1156  /*
1157  * - LGPL
1158  *
1159  * image
1160  * 
1161  */
1162
1163
1164 /**
1165  * @class Roo.bootstrap.Link
1166  * @extends Roo.bootstrap.Component
1167  * Bootstrap Link Class
1168  * @cfg {String} alt image alternative text
1169  * @cfg {String} href a tag href
1170  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1171  * @cfg {String} html the content of the link.
1172  * @cfg {Boolean} preventDefault (true | false) default false
1173
1174  * 
1175  * @constructor
1176  * Create a new Input
1177  * @param {Object} config The config object
1178  */
1179
1180 Roo.bootstrap.Link = function(config){
1181     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1182     
1183     this.addEvents({
1184         // img events
1185         /**
1186          * @event click
1187          * The img click event for the img.
1188          * @param {Roo.EventObject} e
1189          */
1190         "click" : true
1191     });
1192 };
1193
1194 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1195     
1196     href: false,
1197     target: false,
1198     preventDefault: false,
1199
1200     getAutoCreate : function(){
1201         
1202         var cfg = {
1203             tag: 'a',
1204             html : this.html || 'html-missing'
1205         }
1206         
1207         
1208         if(this.alt){
1209             cfg.alt = this.alt;
1210         }
1211         cfg.href = this.href || '#';
1212         if(this.target){
1213             cfg.target = this.target;
1214         }
1215         
1216         return cfg;
1217     },
1218     
1219     initEvents: function() {
1220         
1221         if(!this.href || this.preventDefault){
1222             this.el.on('click', this.onClick, this);
1223         }
1224     },
1225     
1226     onClick : function(e)
1227     {
1228         if(this.preventDefault){
1229             e.preventDefault();
1230         }
1231         //Roo.log('img onclick');
1232         this.fireEvent('click', this, e);
1233     }
1234    
1235 });
1236
1237  /*
1238  * - LGPL
1239  *
1240  * header
1241  * 
1242  */
1243
1244 /**
1245  * @class Roo.bootstrap.Header
1246  * @extends Roo.bootstrap.Component
1247  * Bootstrap Header class
1248  * @cfg {String} html content of header
1249  * @cfg {Number} level (1|2|3|4|5|6) default 1
1250  * 
1251  * @constructor
1252  * Create a new Header
1253  * @param {Object} config The config object
1254  */
1255
1256
1257 Roo.bootstrap.Header  = function(config){
1258     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1259 };
1260
1261 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1262     
1263     //href : false,
1264     html : false,
1265     level : 1,
1266     
1267     
1268     
1269     getAutoCreate : function(){
1270         
1271         var cfg = {
1272             tag: 'h' + (1 *this.level),
1273             html: this.html || 'fill in html'
1274         } ;
1275         
1276         return cfg;
1277     }
1278    
1279 });
1280
1281  
1282
1283  /*
1284  * Based on:
1285  * Ext JS Library 1.1.1
1286  * Copyright(c) 2006-2007, Ext JS, LLC.
1287  *
1288  * Originally Released Under LGPL - original licence link has changed is not relivant.
1289  *
1290  * Fork - LGPL
1291  * <script type="text/javascript">
1292  */
1293  
1294 /**
1295  * @class Roo.bootstrap.MenuMgr
1296  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1297  * @singleton
1298  */
1299 Roo.bootstrap.MenuMgr = function(){
1300    var menus, active, groups = {}, attached = false, lastShow = new Date();
1301
1302    // private - called when first menu is created
1303    function init(){
1304        menus = {};
1305        active = new Roo.util.MixedCollection();
1306        Roo.get(document).addKeyListener(27, function(){
1307            if(active.length > 0){
1308                hideAll();
1309            }
1310        });
1311    }
1312
1313    // private
1314    function hideAll(){
1315        if(active && active.length > 0){
1316            var c = active.clone();
1317            c.each(function(m){
1318                m.hide();
1319            });
1320        }
1321    }
1322
1323    // private
1324    function onHide(m){
1325        active.remove(m);
1326        if(active.length < 1){
1327            Roo.get(document).un("mouseup", onMouseDown);
1328             
1329            attached = false;
1330        }
1331    }
1332
1333    // private
1334    function onShow(m){
1335        var last = active.last();
1336        lastShow = new Date();
1337        active.add(m);
1338        if(!attached){
1339           Roo.get(document).on("mouseup", onMouseDown);
1340            
1341            attached = true;
1342        }
1343        if(m.parentMenu){
1344           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1345           m.parentMenu.activeChild = m;
1346        }else if(last && last.isVisible()){
1347           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1348        }
1349    }
1350
1351    // private
1352    function onBeforeHide(m){
1353        if(m.activeChild){
1354            m.activeChild.hide();
1355        }
1356        if(m.autoHideTimer){
1357            clearTimeout(m.autoHideTimer);
1358            delete m.autoHideTimer;
1359        }
1360    }
1361
1362    // private
1363    function onBeforeShow(m){
1364        var pm = m.parentMenu;
1365        if(!pm && !m.allowOtherMenus){
1366            hideAll();
1367        }else if(pm && pm.activeChild && active != m){
1368            pm.activeChild.hide();
1369        }
1370    }
1371
1372    // private
1373    function onMouseDown(e){
1374         Roo.log("on MouseDown");
1375         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
1376            hideAll();
1377         }
1378         
1379         
1380    }
1381
1382    // private
1383    function onBeforeCheck(mi, state){
1384        if(state){
1385            var g = groups[mi.group];
1386            for(var i = 0, l = g.length; i < l; i++){
1387                if(g[i] != mi){
1388                    g[i].setChecked(false);
1389                }
1390            }
1391        }
1392    }
1393
1394    return {
1395
1396        /**
1397         * Hides all menus that are currently visible
1398         */
1399        hideAll : function(){
1400             hideAll();  
1401        },
1402
1403        // private
1404        register : function(menu){
1405            if(!menus){
1406                init();
1407            }
1408            menus[menu.id] = menu;
1409            menu.on("beforehide", onBeforeHide);
1410            menu.on("hide", onHide);
1411            menu.on("beforeshow", onBeforeShow);
1412            menu.on("show", onShow);
1413            var g = menu.group;
1414            if(g && menu.events["checkchange"]){
1415                if(!groups[g]){
1416                    groups[g] = [];
1417                }
1418                groups[g].push(menu);
1419                menu.on("checkchange", onCheck);
1420            }
1421        },
1422
1423         /**
1424          * Returns a {@link Roo.menu.Menu} object
1425          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1426          * be used to generate and return a new Menu instance.
1427          */
1428        get : function(menu){
1429            if(typeof menu == "string"){ // menu id
1430                return menus[menu];
1431            }else if(menu.events){  // menu instance
1432                return menu;
1433            }
1434            /*else if(typeof menu.length == 'number'){ // array of menu items?
1435                return new Roo.bootstrap.Menu({items:menu});
1436            }else{ // otherwise, must be a config
1437                return new Roo.bootstrap.Menu(menu);
1438            }
1439            */
1440            return false;
1441        },
1442
1443        // private
1444        unregister : function(menu){
1445            delete menus[menu.id];
1446            menu.un("beforehide", onBeforeHide);
1447            menu.un("hide", onHide);
1448            menu.un("beforeshow", onBeforeShow);
1449            menu.un("show", onShow);
1450            var g = menu.group;
1451            if(g && menu.events["checkchange"]){
1452                groups[g].remove(menu);
1453                menu.un("checkchange", onCheck);
1454            }
1455        },
1456
1457        // private
1458        registerCheckable : function(menuItem){
1459            var g = menuItem.group;
1460            if(g){
1461                if(!groups[g]){
1462                    groups[g] = [];
1463                }
1464                groups[g].push(menuItem);
1465                menuItem.on("beforecheckchange", onBeforeCheck);
1466            }
1467        },
1468
1469        // private
1470        unregisterCheckable : function(menuItem){
1471            var g = menuItem.group;
1472            if(g){
1473                groups[g].remove(menuItem);
1474                menuItem.un("beforecheckchange", onBeforeCheck);
1475            }
1476        }
1477    };
1478 }();/*
1479  * - LGPL
1480  *
1481  * menu
1482  * 
1483  */
1484
1485 /**
1486  * @class Roo.bootstrap.Menu
1487  * @extends Roo.bootstrap.Component
1488  * Bootstrap Menu class - container for MenuItems
1489  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1490  * 
1491  * @constructor
1492  * Create a new Menu
1493  * @param {Object} config The config object
1494  */
1495
1496
1497 Roo.bootstrap.Menu = function(config){
1498     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1499     if (this.registerMenu) {
1500         Roo.bootstrap.MenuMgr.register(this);
1501     }
1502     this.addEvents({
1503         /**
1504          * @event beforeshow
1505          * Fires before this menu is displayed
1506          * @param {Roo.menu.Menu} this
1507          */
1508         beforeshow : true,
1509         /**
1510          * @event beforehide
1511          * Fires before this menu is hidden
1512          * @param {Roo.menu.Menu} this
1513          */
1514         beforehide : true,
1515         /**
1516          * @event show
1517          * Fires after this menu is displayed
1518          * @param {Roo.menu.Menu} this
1519          */
1520         show : true,
1521         /**
1522          * @event hide
1523          * Fires after this menu is hidden
1524          * @param {Roo.menu.Menu} this
1525          */
1526         hide : true,
1527         /**
1528          * @event click
1529          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1530          * @param {Roo.menu.Menu} this
1531          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1532          * @param {Roo.EventObject} e
1533          */
1534         click : true,
1535         /**
1536          * @event mouseover
1537          * Fires when the mouse is hovering over this menu
1538          * @param {Roo.menu.Menu} this
1539          * @param {Roo.EventObject} e
1540          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1541          */
1542         mouseover : true,
1543         /**
1544          * @event mouseout
1545          * Fires when the mouse exits this menu
1546          * @param {Roo.menu.Menu} this
1547          * @param {Roo.EventObject} e
1548          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1549          */
1550         mouseout : true,
1551         /**
1552          * @event itemclick
1553          * Fires when a menu item contained in this menu is clicked
1554          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1555          * @param {Roo.EventObject} e
1556          */
1557         itemclick: true
1558     });
1559     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1560 };
1561
1562 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1563     
1564    /// html : false,
1565     //align : '',
1566     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1567     type: false,
1568     /**
1569      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1570      */
1571     registerMenu : true,
1572     
1573     menuItems :false, // stores the menu items..
1574     
1575     hidden:true,
1576     
1577     parentMenu : false,
1578     
1579     getChildContainer : function() {
1580         return this.el;  
1581     },
1582     
1583     getAutoCreate : function(){
1584          
1585         //if (['right'].indexOf(this.align)!==-1) {
1586         //    cfg.cn[1].cls += ' pull-right'
1587         //}
1588         
1589         
1590         var cfg = {
1591             tag : 'ul',
1592             cls : 'dropdown-menu' ,
1593             style : 'z-index:1000'
1594             
1595         }
1596         
1597         if (this.type === 'submenu') {
1598             cfg.cls = 'submenu active';
1599         }
1600         if (this.type === 'treeview') {
1601             cfg.cls = 'treeview-menu';
1602         }
1603         
1604         return cfg;
1605     },
1606     initEvents : function() {
1607         
1608        // Roo.log("ADD event");
1609        // Roo.log(this.triggerEl.dom);
1610         this.triggerEl.on('click', this.onTriggerPress, this);
1611         this.triggerEl.addClass('dropdown-toggle');
1612         this.el.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
1613
1614         this.el.on("mouseover", this.onMouseOver, this);
1615         this.el.on("mouseout", this.onMouseOut, this);
1616         
1617         
1618     },
1619     findTargetItem : function(e){
1620         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
1621         if(!t){
1622             return false;
1623         }
1624         //Roo.log(t);         Roo.log(t.id);
1625         if(t && t.id){
1626             //Roo.log(this.menuitems);
1627             return this.menuitems.get(t.id);
1628             
1629             //return this.items.get(t.menuItemId);
1630         }
1631         
1632         return false;
1633     },
1634     onClick : function(e){
1635         Roo.log("menu.onClick");
1636         var t = this.findTargetItem(e);
1637         if(!t){
1638             return;
1639         }
1640         Roo.log(e);
1641         /*
1642         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
1643             if(t == this.activeItem && t.shouldDeactivate(e)){
1644                 this.activeItem.deactivate();
1645                 delete this.activeItem;
1646                 return;
1647             }
1648             if(t.canActivate){
1649                 this.setActiveItem(t, true);
1650             }
1651             return;
1652             
1653             
1654         }
1655         */
1656         Roo.log('pass click event');
1657         
1658         t.onClick(e);
1659         
1660         this.fireEvent("click", this, t, e);
1661         
1662         this.hide();
1663     },
1664      onMouseOver : function(e){
1665         var t  = this.findTargetItem(e);
1666         //Roo.log(t);
1667         //if(t){
1668         //    if(t.canActivate && !t.disabled){
1669         //        this.setActiveItem(t, true);
1670         //    }
1671         //}
1672         
1673         this.fireEvent("mouseover", this, e, t);
1674     },
1675     isVisible : function(){
1676         return !this.hidden;
1677     },
1678      onMouseOut : function(e){
1679         var t  = this.findTargetItem(e);
1680         
1681         //if(t ){
1682         //    if(t == this.activeItem && t.shouldDeactivate(e)){
1683         //        this.activeItem.deactivate();
1684         //        delete this.activeItem;
1685         //    }
1686         //}
1687         this.fireEvent("mouseout", this, e, t);
1688     },
1689     
1690     
1691     /**
1692      * Displays this menu relative to another element
1693      * @param {String/HTMLElement/Roo.Element} element The element to align to
1694      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
1695      * the element (defaults to this.defaultAlign)
1696      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1697      */
1698     show : function(el, pos, parentMenu){
1699         this.parentMenu = parentMenu;
1700         if(!this.el){
1701             this.render();
1702         }
1703         this.fireEvent("beforeshow", this);
1704         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
1705     },
1706      /**
1707      * Displays this menu at a specific xy position
1708      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
1709      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1710      */
1711     showAt : function(xy, parentMenu, /* private: */_e){
1712         this.parentMenu = parentMenu;
1713         if(!this.el){
1714             this.render();
1715         }
1716         if(_e !== false){
1717             this.fireEvent("beforeshow", this);
1718             
1719             //xy = this.el.adjustForConstraints(xy);
1720         }
1721         //this.el.setXY(xy);
1722         //this.el.show();
1723         this.hideMenuItems();
1724         this.hidden = false;
1725         this.triggerEl.addClass('open');
1726         this.focus();
1727         this.fireEvent("show", this);
1728     },
1729     
1730     focus : function(){
1731         return;
1732         if(!this.hidden){
1733             this.doFocus.defer(50, this);
1734         }
1735     },
1736
1737     doFocus : function(){
1738         if(!this.hidden){
1739             this.focusEl.focus();
1740         }
1741     },
1742
1743     /**
1744      * Hides this menu and optionally all parent menus
1745      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
1746      */
1747     hide : function(deep){
1748         
1749         this.hideMenuItems();
1750         if(this.el && this.isVisible()){
1751             this.fireEvent("beforehide", this);
1752             if(this.activeItem){
1753                 this.activeItem.deactivate();
1754                 this.activeItem = null;
1755             }
1756             this.triggerEl.removeClass('open');;
1757             this.hidden = true;
1758             this.fireEvent("hide", this);
1759         }
1760         if(deep === true && this.parentMenu){
1761             this.parentMenu.hide(true);
1762         }
1763     },
1764     
1765     onTriggerPress  : function(e)
1766     {
1767         
1768         Roo.log('trigger press');
1769         //Roo.log(e.getTarget());
1770        // Roo.log(this.triggerEl.dom);
1771         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
1772             return;
1773         }
1774         if (this.isVisible()) {
1775             Roo.log('hide');
1776             this.hide();
1777         } else {
1778             this.show(this.triggerEl, false, false);
1779         }
1780         
1781         
1782     },
1783     
1784          
1785        
1786     
1787     hideMenuItems : function()
1788     {
1789         //$(backdrop).remove()
1790         Roo.select('.open',true).each(function(aa) {
1791             
1792             aa.removeClass('open');
1793           //var parent = getParent($(this))
1794           //var relatedTarget = { relatedTarget: this }
1795           
1796            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
1797           //if (e.isDefaultPrevented()) return
1798            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
1799         })
1800     },
1801     addxtypeChild : function (tree, cntr) {
1802         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
1803           
1804         this.menuitems.add(comp);
1805         return comp;
1806
1807     },
1808     getEl : function()
1809     {
1810         Roo.log(this.el);
1811         return this.el;
1812     }
1813 });
1814
1815  
1816  /*
1817  * - LGPL
1818  *
1819  * menu item
1820  * 
1821  */
1822
1823
1824 /**
1825  * @class Roo.bootstrap.MenuItem
1826  * @extends Roo.bootstrap.Component
1827  * Bootstrap MenuItem class
1828  * @cfg {String} html the menu label
1829  * @cfg {String} href the link
1830  * @cfg {Boolean} preventDefault (true | false) default true
1831  * 
1832  * 
1833  * @constructor
1834  * Create a new MenuItem
1835  * @param {Object} config The config object
1836  */
1837
1838
1839 Roo.bootstrap.MenuItem = function(config){
1840     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
1841     this.addEvents({
1842         // raw events
1843         /**
1844          * @event click
1845          * The raw click event for the entire grid.
1846          * @param {Roo.EventObject} e
1847          */
1848         "click" : true
1849     });
1850 };
1851
1852 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
1853     
1854     href : false,
1855     html : false,
1856     preventDefault: true,
1857     
1858     getAutoCreate : function(){
1859         var cfg= {
1860             tag: 'li',
1861             cls: 'dropdown-menu-item',
1862             cn: [
1863                     {
1864                         tag : 'a',
1865                         href : '#',
1866                         html : 'Link'
1867                     }
1868                 ]
1869         };
1870         if (this.parent().type == 'treeview') {
1871             cfg.cls = 'treeview-menu';
1872         }
1873         
1874         cfg.cn[0].href = this.href || cfg.cn[0].href ;
1875         cfg.cn[0].html = this.html || cfg.cn[0].html ;
1876         return cfg;
1877     },
1878     
1879     initEvents: function() {
1880         
1881         //this.el.select('a').on('click', this.onClick, this);
1882         
1883     },
1884     onClick : function(e)
1885     {
1886         Roo.log('item on click ');
1887         //if(this.preventDefault){
1888         //    e.preventDefault();
1889         //}
1890         //this.parent().hideMenuItems();
1891         
1892         this.fireEvent('click', this, e);
1893     },
1894     getEl : function()
1895     {
1896         return this.el;
1897     }
1898 });
1899
1900  
1901
1902  /*
1903  * - LGPL
1904  *
1905  * menu separator
1906  * 
1907  */
1908
1909
1910 /**
1911  * @class Roo.bootstrap.MenuSeparator
1912  * @extends Roo.bootstrap.Component
1913  * Bootstrap MenuSeparator class
1914  * 
1915  * @constructor
1916  * Create a new MenuItem
1917  * @param {Object} config The config object
1918  */
1919
1920
1921 Roo.bootstrap.MenuSeparator = function(config){
1922     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
1923 };
1924
1925 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
1926     
1927     getAutoCreate : function(){
1928         var cfg = {
1929             cls: 'divider',
1930             tag : 'li'
1931         };
1932         
1933         return cfg;
1934     }
1935    
1936 });
1937
1938  
1939
1940  
1941 /*
1942 <div class="modal fade">
1943   <div class="modal-dialog">
1944     <div class="modal-content">
1945       <div class="modal-header">
1946         <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
1947         <h4 class="modal-title">Modal title</h4>
1948       </div>
1949       <div class="modal-body">
1950         <p>One fine body&hellip;</p>
1951       </div>
1952       <div class="modal-footer">
1953         <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
1954         <button type="button" class="btn btn-primary">Save changes</button>
1955       </div>
1956     </div><!-- /.modal-content -->
1957   </div><!-- /.modal-dialog -->
1958 </div><!-- /.modal -->
1959 */
1960 /*
1961  * - LGPL
1962  *
1963  * page contgainer.
1964  * 
1965  */
1966
1967 /**
1968  * @class Roo.bootstrap.Modal
1969  * @extends Roo.bootstrap.Component
1970  * Bootstrap Modal class
1971  * @cfg {String} title Title of dialog
1972  * @cfg {Boolean} specificTitle (true|false) default false
1973  * @cfg {Array} buttons Array of buttons or standard button set..
1974  * @cfg {String} buttonPosition (left|right|center) default right
1975  * 
1976  * @constructor
1977  * Create a new Modal Dialog
1978  * @param {Object} config The config object
1979  */
1980
1981 Roo.bootstrap.Modal = function(config){
1982     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
1983     this.addEvents({
1984         // raw events
1985         /**
1986          * @event btnclick
1987          * The raw btnclick event for the button
1988          * @param {Roo.EventObject} e
1989          */
1990         "btnclick" : true
1991     });
1992     this.buttons = this.buttons || [];
1993 };
1994
1995 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
1996     
1997     title : 'test dialog',
1998    
1999     buttons : false,
2000     
2001     // set on load...
2002     body:  false,
2003     
2004     specificTitle: false,
2005     
2006     buttonPosition: 'right',
2007     
2008     onRender : function(ct, position)
2009     {
2010         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2011      
2012         if(!this.el){
2013             var cfg = Roo.apply({},  this.getAutoCreate());
2014             cfg.id = Roo.id();
2015             //if(!cfg.name){
2016             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2017             //}
2018             //if (!cfg.name.length) {
2019             //    delete cfg.name;
2020            // }
2021             if (this.cls) {
2022                 cfg.cls += ' ' + this.cls;
2023             }
2024             if (this.style) {
2025                 cfg.style = this.style;
2026             }
2027             this.el = Roo.get(document.body).createChild(cfg, position);
2028         }
2029         //var type = this.el.dom.type;
2030         
2031         if(this.tabIndex !== undefined){
2032             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2033         }
2034         
2035         
2036         
2037         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2038         this.maskEl.enableDisplayMode("block");
2039         this.maskEl.hide();
2040         //this.el.addClass("x-dlg-modal");
2041     
2042         if (this.buttons.length) {
2043             Roo.each(this.buttons, function(bb) {
2044                 b = Roo.apply({}, bb);
2045                 b.xns = b.xns || Roo.bootstrap;
2046                 b.xtype = b.xtype || 'Button';
2047                 if (typeof(b.listeners) == 'undefined') {
2048                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2049                 }
2050                 
2051                 var btn = Roo.factory(b);
2052                 
2053                 btn.onRender(this.el.select('.modal-footer div').first());
2054                 
2055             },this);
2056         }
2057         // render the children.
2058         var nitems = [];
2059         
2060         if(typeof(this.items) != 'undefined'){
2061             var items = this.items;
2062             delete this.items;
2063
2064             for(var i =0;i < items.length;i++) {
2065                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2066             }
2067         }
2068         
2069         this.items = nitems;
2070         
2071         this.body = this.el.select('.modal-body',true).first();
2072         this.close = this.el.select('.modal-header .close', true).first();
2073         this.footer = this.el.select('.modal-footer',true).first();
2074         this.initEvents();
2075         //this.el.addClass([this.fieldClass, this.cls]);
2076         
2077     },
2078     getAutoCreate : function(){
2079         
2080         
2081         var bdy = {
2082                 cls : 'modal-body',
2083                 html : this.html || ''
2084         };
2085         
2086         var title = {
2087             tag: 'h4',
2088             cls : 'modal-title',
2089             html : this.title
2090         };
2091         
2092         if(this.specificTitle){
2093             title = this.title;
2094         };
2095         
2096         return modal = {
2097             cls: "modal fade",
2098             style : 'display: none',
2099             cn : [
2100                 {
2101                     cls: "modal-dialog",
2102                     cn : [
2103                         {
2104                             cls : "modal-content",
2105                             cn : [
2106                                 {
2107                                     cls : 'modal-header',
2108                                     cn : [
2109                                         {
2110                                             tag: 'button',
2111                                             cls : 'close',
2112                                             html : '&times'
2113                                         },
2114                                         title
2115                                     ]
2116                                 },
2117                                 bdy,
2118                                 {
2119                                     cls : 'modal-footer',
2120                                     cn : [
2121                                         {
2122                                             tag: 'div',
2123                                             cls: 'btn-' + this.buttonPosition
2124                                         }
2125                                     ]
2126                                     
2127                                 }
2128                                 
2129                                 
2130                             ]
2131                             
2132                         }
2133                     ]
2134                         
2135                 }
2136             ]
2137             
2138             
2139         };
2140           
2141     },
2142     getChildContainer : function() {
2143          
2144          return this.el.select('.modal-body',true).first();
2145         
2146     },
2147     getButtonContainer : function() {
2148          return this.el.select('.modal-footer div',true).first();
2149         
2150     },
2151     initEvents : function()
2152     {
2153         this.el.select('.modal-header .close').on('click', this.hide, this);
2154 //        
2155 //        this.addxtype(this);
2156     },
2157     show : function() {
2158         
2159         if (!this.rendered) {
2160             this.render();
2161         }
2162        
2163         this.el.addClass('on');
2164         this.el.removeClass('fade');
2165         this.el.setStyle('display', 'block');
2166         Roo.get(document.body).addClass("x-body-masked");
2167         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2168         this.maskEl.show();
2169         this.el.setStyle('zIndex', '10001');
2170         this.fireEvent('show', this);
2171         
2172         
2173     },
2174     hide : function()
2175     {
2176         Roo.log('Modal hide?!');
2177         this.maskEl.hide();
2178         Roo.get(document.body).removeClass("x-body-masked");
2179         this.el.removeClass('on');
2180         this.el.addClass('fade');
2181         this.el.setStyle('display', 'none');
2182         this.fireEvent('hide', this);
2183     },
2184     
2185     addButton : function(str, cb)
2186     {
2187          
2188         
2189         var b = Roo.apply({}, { html : str } );
2190         b.xns = b.xns || Roo.bootstrap;
2191         b.xtype = b.xtype || 'Button';
2192         if (typeof(b.listeners) == 'undefined') {
2193             b.listeners = { click : cb.createDelegate(this)  };
2194         }
2195         
2196         var btn = Roo.factory(b);
2197            
2198         btn.onRender(this.el.select('.modal-footer div').first());
2199         
2200         return btn;   
2201        
2202     },
2203     
2204     setDefaultButton : function(btn)
2205     {
2206         //this.el.select('.modal-footer').()
2207     },
2208     resizeTo: function(w,h)
2209     {
2210         // skip..
2211     },
2212     setContentSize  : function(w, h)
2213     {
2214         
2215     },
2216     onButtonClick: function(btn,e)
2217     {
2218         //Roo.log([a,b,c]);
2219         this.fireEvent('btnclick', btn.name, e);
2220     },
2221     setTitle: function(str) {
2222         this.el.select('.modal-title',true).first().dom.innerHTML = str;
2223         
2224     }
2225 });
2226
2227
2228 Roo.apply(Roo.bootstrap.Modal,  {
2229     /**
2230          * Button config that displays a single OK button
2231          * @type Object
2232          */
2233         OK :  [{
2234             name : 'ok',
2235             weight : 'primary',
2236             html : 'OK'
2237         }], 
2238         /**
2239          * Button config that displays Yes and No buttons
2240          * @type Object
2241          */
2242         YESNO : [
2243             {
2244                 name  : 'no',
2245                 html : 'No'
2246             },
2247             {
2248                 name  :'yes',
2249                 weight : 'primary',
2250                 html : 'Yes'
2251             }
2252         ],
2253         
2254         /**
2255          * Button config that displays OK and Cancel buttons
2256          * @type Object
2257          */
2258         OKCANCEL : [
2259             {
2260                name : 'cancel',
2261                 html : 'Cancel'
2262             },
2263             {
2264                 name : 'ok',
2265                 weight : 'primary',
2266                 html : 'OK'
2267             }
2268         ],
2269         /**
2270          * Button config that displays Yes, No and Cancel buttons
2271          * @type Object
2272          */
2273         YESNOCANCEL : [
2274             {
2275                 name : 'yes',
2276                 weight : 'primary',
2277                 html : 'Yes'
2278             },
2279             {
2280                 name : 'no',
2281                 html : 'No'
2282             },
2283             {
2284                 name : 'cancel',
2285                 html : 'Cancel'
2286             }
2287         ]
2288 });
2289  /*
2290  * - LGPL
2291  *
2292  * messagebox - can be used as a replace
2293  * 
2294  */
2295 /**
2296  * @class Roo.MessageBox
2297  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2298  * Example usage:
2299  *<pre><code>
2300 // Basic alert:
2301 Roo.Msg.alert('Status', 'Changes saved successfully.');
2302
2303 // Prompt for user data:
2304 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2305     if (btn == 'ok'){
2306         // process text value...
2307     }
2308 });
2309
2310 // Show a dialog using config options:
2311 Roo.Msg.show({
2312    title:'Save Changes?',
2313    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2314    buttons: Roo.Msg.YESNOCANCEL,
2315    fn: processResult,
2316    animEl: 'elId'
2317 });
2318 </code></pre>
2319  * @singleton
2320  */
2321 Roo.bootstrap.MessageBox = function(){
2322     var dlg, opt, mask, waitTimer;
2323     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2324     var buttons, activeTextEl, bwidth;
2325
2326     
2327     // private
2328     var handleButton = function(button){
2329         dlg.hide();
2330         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2331     };
2332
2333     // private
2334     var handleHide = function(){
2335         if(opt && opt.cls){
2336             dlg.el.removeClass(opt.cls);
2337         }
2338         //if(waitTimer){
2339         //    Roo.TaskMgr.stop(waitTimer);
2340         //    waitTimer = null;
2341         //}
2342     };
2343
2344     // private
2345     var updateButtons = function(b){
2346         var width = 0;
2347         if(!b){
2348             buttons["ok"].hide();
2349             buttons["cancel"].hide();
2350             buttons["yes"].hide();
2351             buttons["no"].hide();
2352             //dlg.footer.dom.style.display = 'none';
2353             return width;
2354         }
2355         dlg.footer.dom.style.display = '';
2356         for(var k in buttons){
2357             if(typeof buttons[k] != "function"){
2358                 if(b[k]){
2359                     buttons[k].show();
2360                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2361                     width += buttons[k].el.getWidth()+15;
2362                 }else{
2363                     buttons[k].hide();
2364                 }
2365             }
2366         }
2367         return width;
2368     };
2369
2370     // private
2371     var handleEsc = function(d, k, e){
2372         if(opt && opt.closable !== false){
2373             dlg.hide();
2374         }
2375         if(e){
2376             e.stopEvent();
2377         }
2378     };
2379
2380     return {
2381         /**
2382          * Returns a reference to the underlying {@link Roo.BasicDialog} element
2383          * @return {Roo.BasicDialog} The BasicDialog element
2384          */
2385         getDialog : function(){
2386            if(!dlg){
2387                 dlg = new Roo.bootstrap.Modal( {
2388                     //draggable: true,
2389                     //resizable:false,
2390                     //constraintoviewport:false,
2391                     //fixedcenter:true,
2392                     //collapsible : false,
2393                     //shim:true,
2394                     //modal: true,
2395                   //  width:400,
2396                   //  height:100,
2397                     //buttonAlign:"center",
2398                     closeClick : function(){
2399                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2400                             handleButton("no");
2401                         }else{
2402                             handleButton("cancel");
2403                         }
2404                     }
2405                 });
2406                 dlg.render();
2407                 dlg.on("hide", handleHide);
2408                 mask = dlg.mask;
2409                 //dlg.addKeyListener(27, handleEsc);
2410                 buttons = {};
2411                 this.buttons = buttons;
2412                 var bt = this.buttonText;
2413                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2414                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2415                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2416                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2417                 Roo.log(buttons)
2418                 bodyEl = dlg.body.createChild({
2419
2420                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2421                         '<textarea class="roo-mb-textarea"></textarea>' +
2422                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
2423                 });
2424                 msgEl = bodyEl.dom.firstChild;
2425                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2426                 textboxEl.enableDisplayMode();
2427                 textboxEl.addKeyListener([10,13], function(){
2428                     if(dlg.isVisible() && opt && opt.buttons){
2429                         if(opt.buttons.ok){
2430                             handleButton("ok");
2431                         }else if(opt.buttons.yes){
2432                             handleButton("yes");
2433                         }
2434                     }
2435                 });
2436                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2437                 textareaEl.enableDisplayMode();
2438                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2439                 progressEl.enableDisplayMode();
2440                 var pf = progressEl.dom.firstChild;
2441                 if (pf) {
2442                     pp = Roo.get(pf.firstChild);
2443                     pp.setHeight(pf.offsetHeight);
2444                 }
2445                 
2446             }
2447             return dlg;
2448         },
2449
2450         /**
2451          * Updates the message box body text
2452          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2453          * the XHTML-compliant non-breaking space character '&amp;#160;')
2454          * @return {Roo.MessageBox} This message box
2455          */
2456         updateText : function(text){
2457             if(!dlg.isVisible() && !opt.width){
2458                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2459             }
2460             msgEl.innerHTML = text || '&#160;';
2461       
2462             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2463             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2464             var w = Math.max(
2465                     Math.min(opt.width || cw , this.maxWidth), 
2466                     Math.max(opt.minWidth || this.minWidth, bwidth)
2467             );
2468             if(opt.prompt){
2469                 activeTextEl.setWidth(w);
2470             }
2471             if(dlg.isVisible()){
2472                 dlg.fixedcenter = false;
2473             }
2474             // to big, make it scroll. = But as usual stupid IE does not support
2475             // !important..
2476             
2477             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2478                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2479                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2480             } else {
2481                 bodyEl.dom.style.height = '';
2482                 bodyEl.dom.style.overflowY = '';
2483             }
2484             if (cw > w) {
2485                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2486             } else {
2487                 bodyEl.dom.style.overflowX = '';
2488             }
2489             
2490             dlg.setContentSize(w, bodyEl.getHeight());
2491             if(dlg.isVisible()){
2492                 dlg.fixedcenter = true;
2493             }
2494             return this;
2495         },
2496
2497         /**
2498          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
2499          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2500          * @param {Number} value Any number between 0 and 1 (e.g., .5)
2501          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2502          * @return {Roo.MessageBox} This message box
2503          */
2504         updateProgress : function(value, text){
2505             if(text){
2506                 this.updateText(text);
2507             }
2508             if (pp) { // weird bug on my firefox - for some reason this is not defined
2509                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2510             }
2511             return this;
2512         },        
2513
2514         /**
2515          * Returns true if the message box is currently displayed
2516          * @return {Boolean} True if the message box is visible, else false
2517          */
2518         isVisible : function(){
2519             return dlg && dlg.isVisible();  
2520         },
2521
2522         /**
2523          * Hides the message box if it is displayed
2524          */
2525         hide : function(){
2526             if(this.isVisible()){
2527                 dlg.hide();
2528             }  
2529         },
2530
2531         /**
2532          * Displays a new message box, or reinitializes an existing message box, based on the config options
2533          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2534          * The following config object properties are supported:
2535          * <pre>
2536 Property    Type             Description
2537 ----------  ---------------  ------------------------------------------------------------------------------------
2538 animEl            String/Element   An id or Element from which the message box should animate as it opens and
2539                                    closes (defaults to undefined)
2540 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
2541                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
2542 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
2543                                    progress and wait dialogs will ignore this property and always hide the
2544                                    close button as they can only be closed programmatically.
2545 cls               String           A custom CSS class to apply to the message box element
2546 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
2547                                    displayed (defaults to 75)
2548 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
2549                                    function will be btn (the name of the button that was clicked, if applicable,
2550                                    e.g. "ok"), and text (the value of the active text field, if applicable).
2551                                    Progress and wait dialogs will ignore this option since they do not respond to
2552                                    user actions and can only be closed programmatically, so any required function
2553                                    should be called by the same code after it closes the dialog.
2554 icon              String           A CSS class that provides a background image to be used as an icon for
2555                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
2556 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
2557 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
2558 modal             Boolean          False to allow user interaction with the page while the message box is
2559                                    displayed (defaults to true)
2560 msg               String           A string that will replace the existing message box body text (defaults
2561                                    to the XHTML-compliant non-breaking space character '&#160;')
2562 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
2563 progress          Boolean          True to display a progress bar (defaults to false)
2564 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
2565 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
2566 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
2567 title             String           The title text
2568 value             String           The string value to set into the active textbox element if displayed
2569 wait              Boolean          True to display a progress bar (defaults to false)
2570 width             Number           The width of the dialog in pixels
2571 </pre>
2572          *
2573          * Example usage:
2574          * <pre><code>
2575 Roo.Msg.show({
2576    title: 'Address',
2577    msg: 'Please enter your address:',
2578    width: 300,
2579    buttons: Roo.MessageBox.OKCANCEL,
2580    multiline: true,
2581    fn: saveAddress,
2582    animEl: 'addAddressBtn'
2583 });
2584 </code></pre>
2585          * @param {Object} config Configuration options
2586          * @return {Roo.MessageBox} This message box
2587          */
2588         show : function(options)
2589         {
2590             
2591             // this causes nightmares if you show one dialog after another
2592             // especially on callbacks..
2593              
2594             if(this.isVisible()){
2595                 
2596                 this.hide();
2597                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
2598                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
2599                 Roo.log("New Dialog Message:" +  options.msg )
2600                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
2601                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
2602                 
2603             }
2604             var d = this.getDialog();
2605             opt = options;
2606             d.setTitle(opt.title || "&#160;");
2607             d.close.setDisplayed(opt.closable !== false);
2608             activeTextEl = textboxEl;
2609             opt.prompt = opt.prompt || (opt.multiline ? true : false);
2610             if(opt.prompt){
2611                 if(opt.multiline){
2612                     textboxEl.hide();
2613                     textareaEl.show();
2614                     textareaEl.setHeight(typeof opt.multiline == "number" ?
2615                         opt.multiline : this.defaultTextHeight);
2616                     activeTextEl = textareaEl;
2617                 }else{
2618                     textboxEl.show();
2619                     textareaEl.hide();
2620                 }
2621             }else{
2622                 textboxEl.hide();
2623                 textareaEl.hide();
2624             }
2625             progressEl.setDisplayed(opt.progress === true);
2626             this.updateProgress(0);
2627             activeTextEl.dom.value = opt.value || "";
2628             if(opt.prompt){
2629                 dlg.setDefaultButton(activeTextEl);
2630             }else{
2631                 var bs = opt.buttons;
2632                 var db = null;
2633                 if(bs && bs.ok){
2634                     db = buttons["ok"];
2635                 }else if(bs && bs.yes){
2636                     db = buttons["yes"];
2637                 }
2638                 dlg.setDefaultButton(db);
2639             }
2640             bwidth = updateButtons(opt.buttons);
2641             this.updateText(opt.msg);
2642             if(opt.cls){
2643                 d.el.addClass(opt.cls);
2644             }
2645             d.proxyDrag = opt.proxyDrag === true;
2646             d.modal = opt.modal !== false;
2647             d.mask = opt.modal !== false ? mask : false;
2648             if(!d.isVisible()){
2649                 // force it to the end of the z-index stack so it gets a cursor in FF
2650                 document.body.appendChild(dlg.el.dom);
2651                 d.animateTarget = null;
2652                 d.show(options.animEl);
2653             }
2654             return this;
2655         },
2656
2657         /**
2658          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
2659          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
2660          * and closing the message box when the process is complete.
2661          * @param {String} title The title bar text
2662          * @param {String} msg The message box body text
2663          * @return {Roo.MessageBox} This message box
2664          */
2665         progress : function(title, msg){
2666             this.show({
2667                 title : title,
2668                 msg : msg,
2669                 buttons: false,
2670                 progress:true,
2671                 closable:false,
2672                 minWidth: this.minProgressWidth,
2673                 modal : true
2674             });
2675             return this;
2676         },
2677
2678         /**
2679          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
2680          * If a callback function is passed it will be called after the user clicks the button, and the
2681          * id of the button that was clicked will be passed as the only parameter to the callback
2682          * (could also be the top-right close button).
2683          * @param {String} title The title bar text
2684          * @param {String} msg The message box body text
2685          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2686          * @param {Object} scope (optional) The scope of the callback function
2687          * @return {Roo.MessageBox} This message box
2688          */
2689         alert : function(title, msg, fn, scope){
2690             this.show({
2691                 title : title,
2692                 msg : msg,
2693                 buttons: this.OK,
2694                 fn: fn,
2695                 scope : scope,
2696                 modal : true
2697             });
2698             return this;
2699         },
2700
2701         /**
2702          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
2703          * interaction while waiting for a long-running process to complete that does not have defined intervals.
2704          * You are responsible for closing the message box when the process is complete.
2705          * @param {String} msg The message box body text
2706          * @param {String} title (optional) The title bar text
2707          * @return {Roo.MessageBox} This message box
2708          */
2709         wait : function(msg, title){
2710             this.show({
2711                 title : title,
2712                 msg : msg,
2713                 buttons: false,
2714                 closable:false,
2715                 progress:true,
2716                 modal:true,
2717                 width:300,
2718                 wait:true
2719             });
2720             waitTimer = Roo.TaskMgr.start({
2721                 run: function(i){
2722                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
2723                 },
2724                 interval: 1000
2725             });
2726             return this;
2727         },
2728
2729         /**
2730          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
2731          * If a callback function is passed it will be called after the user clicks either button, and the id of the
2732          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
2733          * @param {String} title The title bar text
2734          * @param {String} msg The message box body text
2735          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2736          * @param {Object} scope (optional) The scope of the callback function
2737          * @return {Roo.MessageBox} This message box
2738          */
2739         confirm : function(title, msg, fn, scope){
2740             this.show({
2741                 title : title,
2742                 msg : msg,
2743                 buttons: this.YESNO,
2744                 fn: fn,
2745                 scope : scope,
2746                 modal : true
2747             });
2748             return this;
2749         },
2750
2751         /**
2752          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
2753          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
2754          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
2755          * (could also be the top-right close button) and the text that was entered will be passed as the two
2756          * parameters to the callback.
2757          * @param {String} title The title bar text
2758          * @param {String} msg The message box body text
2759          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2760          * @param {Object} scope (optional) The scope of the callback function
2761          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
2762          * property, or the height in pixels to create the textbox (defaults to false / single-line)
2763          * @return {Roo.MessageBox} This message box
2764          */
2765         prompt : function(title, msg, fn, scope, multiline){
2766             this.show({
2767                 title : title,
2768                 msg : msg,
2769                 buttons: this.OKCANCEL,
2770                 fn: fn,
2771                 minWidth:250,
2772                 scope : scope,
2773                 prompt:true,
2774                 multiline: multiline,
2775                 modal : true
2776             });
2777             return this;
2778         },
2779
2780         /**
2781          * Button config that displays a single OK button
2782          * @type Object
2783          */
2784         OK : {ok:true},
2785         /**
2786          * Button config that displays Yes and No buttons
2787          * @type Object
2788          */
2789         YESNO : {yes:true, no:true},
2790         /**
2791          * Button config that displays OK and Cancel buttons
2792          * @type Object
2793          */
2794         OKCANCEL : {ok:true, cancel:true},
2795         /**
2796          * Button config that displays Yes, No and Cancel buttons
2797          * @type Object
2798          */
2799         YESNOCANCEL : {yes:true, no:true, cancel:true},
2800
2801         /**
2802          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
2803          * @type Number
2804          */
2805         defaultTextHeight : 75,
2806         /**
2807          * The maximum width in pixels of the message box (defaults to 600)
2808          * @type Number
2809          */
2810         maxWidth : 600,
2811         /**
2812          * The minimum width in pixels of the message box (defaults to 100)
2813          * @type Number
2814          */
2815         minWidth : 100,
2816         /**
2817          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
2818          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
2819          * @type Number
2820          */
2821         minProgressWidth : 250,
2822         /**
2823          * An object containing the default button text strings that can be overriden for localized language support.
2824          * Supported properties are: ok, cancel, yes and no.
2825          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
2826          * @type Object
2827          */
2828         buttonText : {
2829             ok : "OK",
2830             cancel : "Cancel",
2831             yes : "Yes",
2832             no : "No"
2833         }
2834     };
2835 }();
2836
2837 /**
2838  * Shorthand for {@link Roo.MessageBox}
2839  */
2840 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox 
2841 Roo.Msg = Roo.Msg || Roo.MessageBox;
2842 /*
2843  * - LGPL
2844  *
2845  * navbar
2846  * 
2847  */
2848
2849 /**
2850  * @class Roo.bootstrap.Navbar
2851  * @extends Roo.bootstrap.Component
2852  * Bootstrap Navbar class
2853
2854  * @constructor
2855  * Create a new Navbar
2856  * @param {Object} config The config object
2857  */
2858
2859
2860 Roo.bootstrap.Navbar = function(config){
2861     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
2862     
2863 };
2864
2865 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
2866     
2867     
2868    
2869     // private
2870     navItems : false,
2871     loadMask : false,
2872     
2873     
2874     getAutoCreate : function(){
2875         
2876         
2877         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
2878         
2879     },
2880     
2881     initEvents :function ()
2882     {
2883         //Roo.log(this.el.select('.navbar-toggle',true));
2884         this.el.select('.navbar-toggle',true).on('click', function() {
2885            // Roo.log('click');
2886             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
2887         }, this);
2888         
2889         var mark = {
2890             tag: "div",
2891             cls:"x-dlg-mask"
2892         }
2893         
2894         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
2895         
2896         var size = this.el.getSize();
2897         this.maskEl.setSize(size.width, size.height);
2898         this.maskEl.enableDisplayMode("block");
2899         this.maskEl.hide();
2900         
2901         if(this.loadMask){
2902             this.maskEl.show();
2903         }
2904     },
2905     
2906     
2907     getChildContainer : function()
2908     {
2909         if (this.el.select('.collapse').getCount()) {
2910             return this.el.select('.collapse',true).first();
2911         }
2912         
2913         return this.el;
2914     },
2915     
2916     mask : function()
2917     {
2918         this.maskEl.show();
2919     },
2920     
2921     unmask : function()
2922     {
2923         this.maskEl.hide();
2924     } 
2925     
2926     
2927     
2928     
2929 });
2930
2931
2932
2933  
2934
2935  /*
2936  * - LGPL
2937  *
2938  * navbar
2939  * 
2940  */
2941
2942 /**
2943  * @class Roo.bootstrap.NavSimplebar
2944  * @extends Roo.bootstrap.Navbar
2945  * Bootstrap Sidebar class
2946  *
2947  * @cfg {Boolean} inverse is inverted color
2948  * 
2949  * @cfg {String} type (nav | pills | tabs)
2950  * @cfg {Boolean} arrangement stacked | justified
2951  * @cfg {String} align (left | right) alignment
2952  * 
2953  * @cfg {Boolean} main (true|false) main nav bar? default false
2954  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
2955  * 
2956  * @cfg {String} tag (header|footer|nav|div) default is nav 
2957
2958  * 
2959  * 
2960  * 
2961  * @constructor
2962  * Create a new Sidebar
2963  * @param {Object} config The config object
2964  */
2965
2966
2967 Roo.bootstrap.NavSimplebar = function(config){
2968     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
2969 };
2970
2971 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
2972     
2973     inverse: false,
2974     
2975     type: false,
2976     arrangement: '',
2977     align : false,
2978     
2979     
2980     
2981     main : false,
2982     
2983     
2984     tag : false,
2985     
2986     
2987     getAutoCreate : function(){
2988         
2989         
2990         var cfg = {
2991             tag : this.tag || 'div',
2992             cls : 'navbar'
2993         };
2994           
2995         
2996         cfg.cn = [
2997             {
2998                 cls: 'nav',
2999                 tag : 'ul'
3000             }
3001         ];
3002         
3003          
3004         this.type = this.type || 'nav';
3005         if (['tabs','pills'].indexOf(this.type)!==-1) {
3006             cfg.cn[0].cls += ' nav-' + this.type
3007         
3008         
3009         } else {
3010             if (this.type!=='nav') {
3011                 Roo.log('nav type must be nav/tabs/pills')
3012             }
3013             cfg.cn[0].cls += ' navbar-nav'
3014         }
3015         
3016         
3017         
3018         
3019         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3020             cfg.cn[0].cls += ' nav-' + this.arrangement;
3021         }
3022         
3023         
3024         if (this.align === 'right') {
3025             cfg.cn[0].cls += ' navbar-right';
3026         }
3027         
3028         if (this.inverse) {
3029             cfg.cls += ' navbar-inverse';
3030             
3031         }
3032         
3033         
3034         return cfg;
3035     
3036         
3037     }
3038     
3039     
3040     
3041 });
3042
3043
3044
3045  
3046
3047  
3048        /*
3049  * - LGPL
3050  *
3051  * navbar
3052  * 
3053  */
3054
3055 /**
3056  * @class Roo.bootstrap.NavHeaderbar
3057  * @extends Roo.bootstrap.NavSimplebar
3058  * Bootstrap Sidebar class
3059  *
3060  * @cfg {String} brand what is brand
3061  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3062  * @cfg {String} brand_href href of the brand
3063  * @cfg {Boolean} srButton generate the sr-only button (true | false) default true
3064  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3065  * 
3066  * @constructor
3067  * Create a new Sidebar
3068  * @param {Object} config The config object
3069  */
3070
3071
3072 Roo.bootstrap.NavHeaderbar = function(config){
3073     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3074 };
3075
3076 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3077     
3078     position: '',
3079     brand: '',
3080     brand_href: false,
3081     srButton : true,
3082     autohide : false,
3083     
3084     getAutoCreate : function(){
3085         
3086         var   cfg = {
3087             tag: this.nav || 'nav',
3088             cls: 'navbar',
3089             role: 'navigation',
3090             cn: []
3091         };
3092         
3093         if(this.srButton){
3094             cfg.cn.push({
3095                 tag: 'div',
3096                 cls: 'navbar-header',
3097                 cn: [
3098                     {
3099                         tag: 'button',
3100                         type: 'button',
3101                         cls: 'navbar-toggle',
3102                         'data-toggle': 'collapse',
3103                         cn: [
3104                             {
3105                                 tag: 'span',
3106                                 cls: 'sr-only',
3107                                 html: 'Toggle navigation'
3108                             },
3109                             {
3110                                 tag: 'span',
3111                                 cls: 'icon-bar'
3112                             },
3113                             {
3114                                 tag: 'span',
3115                                 cls: 'icon-bar'
3116                             },
3117                             {
3118                                 tag: 'span',
3119                                 cls: 'icon-bar'
3120                             }
3121                         ]
3122                     }
3123                 ]
3124             });
3125         }
3126         
3127         cfg.cn.push({
3128             tag: 'div',
3129             cls: 'collapse navbar-collapse',
3130             cn : []
3131         });
3132         
3133         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3134         
3135         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3136             cfg.cls += ' navbar-' + this.position;
3137             
3138             // tag can override this..
3139             
3140             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3141         }
3142         
3143         if (this.brand !== '') {
3144             cfg.cn[0].cn.push({
3145                 tag: 'a',
3146                 href: this.brand_href ? this.brand_href : '#',
3147                 cls: 'navbar-brand',
3148                 cn: [
3149                 this.brand
3150                 ]
3151             });
3152         }
3153         
3154         if(this.main){
3155             cfg.cls += ' main-nav';
3156         }
3157         
3158         
3159         return cfg;
3160
3161         
3162     },
3163     initEvents : function()
3164     {
3165         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3166         
3167         if (this.autohide) {
3168             
3169             var prevScroll = 0;
3170             var ft = this.el;
3171             
3172             Roo.get(document).on('scroll',function(e) {
3173                 var ns = Roo.get(document).getScroll().top;
3174                 var os = prevScroll;
3175                 prevScroll = ns;
3176                 
3177                 if(ns > os){
3178                     ft.removeClass('slideDown');
3179                     ft.addClass('slideUp');
3180                     return;
3181                 }
3182                 ft.removeClass('slideUp');
3183                 ft.addClass('slideDown');
3184                  
3185               
3186           },this);
3187         }
3188     }    
3189           
3190       
3191     
3192     
3193 });
3194
3195
3196
3197  
3198
3199  /*
3200  * - LGPL
3201  *
3202  * navbar
3203  * 
3204  */
3205
3206 /**
3207  * @class Roo.bootstrap.NavSidebar
3208  * @extends Roo.bootstrap.Navbar
3209  * Bootstrap Sidebar class
3210  * 
3211  * @constructor
3212  * Create a new Sidebar
3213  * @param {Object} config The config object
3214  */
3215
3216
3217 Roo.bootstrap.NavSidebar = function(config){
3218     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3219 };
3220
3221 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3222     
3223     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3224     
3225     getAutoCreate : function(){
3226         
3227         
3228         return  {
3229             tag: 'div',
3230             cls: 'sidebar sidebar-nav'
3231         };
3232     
3233         
3234     }
3235     
3236     
3237     
3238 });
3239
3240
3241
3242  
3243
3244  /*
3245  * - LGPL
3246  *
3247  * nav group
3248  * 
3249  */
3250
3251 /**
3252  * @class Roo.bootstrap.NavGroup
3253  * @extends Roo.bootstrap.Component
3254  * Bootstrap NavGroup class
3255  * @cfg {String} align left | right
3256  * @cfg {Boolean} inverse false | true
3257  * @cfg {String} type (nav|pills|tab) default nav
3258  * @cfg {String} navId - reference Id for navbar.
3259
3260  * 
3261  * @constructor
3262  * Create a new nav group
3263  * @param {Object} config The config object
3264  */
3265
3266 Roo.bootstrap.NavGroup = function(config){
3267     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3268     this.navItems = [];
3269    
3270     Roo.bootstrap.NavGroup.register(this);
3271      this.addEvents({
3272         /**
3273              * @event changed
3274              * Fires when the active item changes
3275              * @param {Roo.bootstrap.NavGroup} this
3276              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3277              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3278          */
3279         'changed': true
3280      });
3281     
3282 };
3283
3284 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3285     
3286     align: '',
3287     inverse: false,
3288     form: false,
3289     type: 'nav',
3290     navId : '',
3291     // private
3292     
3293     navItems : false, 
3294     
3295     getAutoCreate : function()
3296     {
3297         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3298         
3299         cfg = {
3300             tag : 'ul',
3301             cls: 'nav' 
3302         }
3303         
3304         if (['tabs','pills'].indexOf(this.type)!==-1) {
3305             cfg.cls += ' nav-' + this.type
3306         } else {
3307             if (this.type!=='nav') {
3308                 Roo.log('nav type must be nav/tabs/pills')
3309             }
3310             cfg.cls += ' navbar-nav'
3311         }
3312         
3313         if (this.parent().sidebar) {
3314             cfg = {
3315                 tag: 'ul',
3316                 cls: 'dashboard-menu sidebar-menu'
3317             }
3318             
3319             return cfg;
3320         }
3321         
3322         if (this.form === true) {
3323             cfg = {
3324                 tag: 'form',
3325                 cls: 'navbar-form'
3326             }
3327             
3328             if (this.align === 'right') {
3329                 cfg.cls += ' navbar-right';
3330             } else {
3331                 cfg.cls += ' navbar-left';
3332             }
3333         }
3334         
3335         if (this.align === 'right') {
3336             cfg.cls += ' navbar-right';
3337         }
3338         
3339         if (this.inverse) {
3340             cfg.cls += ' navbar-inverse';
3341             
3342         }
3343         
3344         
3345         return cfg;
3346     },
3347     /**
3348     * sets the active Navigation item
3349     * @param {Roo.bootstrap.NavItem} the new current navitem
3350     */
3351     setActiveItem : function(item)
3352     {
3353         var prev = false;
3354         Roo.each(this.navItems, function(v){
3355             if (v == item) {
3356                 return ;
3357             }
3358             if (v.isActive()) {
3359                 v.setActive(false, true);
3360                 prev = v;
3361                 
3362             }
3363             
3364         });
3365
3366         item.setActive(true, true);
3367         this.fireEvent('changed', this, item, prev);
3368         
3369         
3370     },
3371     /**
3372     * gets the active Navigation item
3373     * @return {Roo.bootstrap.NavItem} the current navitem
3374     */
3375     getActive : function()
3376     {
3377         
3378         var prev = false;
3379         Roo.each(this.navItems, function(v){
3380             
3381             if (v.isActive()) {
3382                 prev = v;
3383                 
3384             }
3385             
3386         });
3387         return prev;
3388     },
3389     
3390     indexOfNav : function()
3391     {
3392         
3393         var prev = false;
3394         Roo.each(this.navItems, function(v,i){
3395             
3396             if (v.isActive()) {
3397                 prev = i;
3398                 
3399             }
3400             
3401         });
3402         return prev;
3403     },
3404     /**
3405     * adds a Navigation item
3406     * @param {Roo.bootstrap.NavItem} the navitem to add
3407     */
3408     addItem : function(cfg)
3409     {
3410         var cn = new Roo.bootstrap.NavItem(cfg);
3411         this.register(cn);
3412         cn.parentId = this.id;
3413         cn.onRender(this.el, null);
3414         return cn;
3415     },
3416     /**
3417     * register a Navigation item
3418     * @param {Roo.bootstrap.NavItem} the navitem to add
3419     */
3420     register : function(item)
3421     {
3422         this.navItems.push( item);
3423         item.navId = this.navId;
3424     
3425     },
3426   
3427     
3428     getNavItem: function(tabId)
3429     {
3430         var ret = false;
3431         Roo.each(this.navItems, function(e) {
3432             if (e.tabId == tabId) {
3433                ret =  e;
3434                return false;
3435             }
3436             return true;
3437             
3438         });
3439         return ret;
3440     },
3441     
3442     setActiveNext : function()
3443     {
3444         var i = this.indexOfNav(this.getActive());
3445         if (i > this.navItems.length) {
3446             return;
3447         }
3448         this.setActiveItem(this.navItems[i+1]);
3449     },
3450     setActivePrev : function()
3451     {
3452         var i = this.indexOfNav(this.getActive());
3453         if (i  < 1) {
3454             return;
3455         }
3456         this.setActiveItem(this.navItems[i-1]);
3457     },
3458     clearWasActive : function(except) {
3459         Roo.each(this.navItems, function(e) {
3460             if (e.tabId != except.tabId && e.was_active) {
3461                e.was_active = false;
3462                return false;
3463             }
3464             return true;
3465             
3466         });
3467     },
3468     getWasActive : function ()
3469     {
3470         var r = false;
3471         Roo.each(this.navItems, function(e) {
3472             if (e.was_active) {
3473                r = e;
3474                return false;
3475             }
3476             return true;
3477             
3478         });
3479         return r;
3480     }
3481     
3482     
3483 });
3484
3485  
3486 Roo.apply(Roo.bootstrap.NavGroup, {
3487     
3488     groups: {},
3489      /**
3490     * register a Navigation Group
3491     * @param {Roo.bootstrap.NavGroup} the navgroup to add
3492     */
3493     register : function(navgrp)
3494     {
3495         this.groups[navgrp.navId] = navgrp;
3496         
3497     },
3498     /**
3499     * fetch a Navigation Group based on the navigation ID
3500     * @param {string} the navgroup to add
3501     * @returns {Roo.bootstrap.NavGroup} the navgroup 
3502     */
3503     get: function(navId) {
3504         if (typeof(this.groups[navId]) == 'undefined') {
3505             return false;
3506             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
3507         }
3508         return this.groups[navId] ;
3509     }
3510     
3511     
3512     
3513 });
3514
3515  /*
3516  * - LGPL
3517  *
3518  * row
3519  * 
3520  */
3521
3522 /**
3523  * @class Roo.bootstrap.NavItem
3524  * @extends Roo.bootstrap.Component
3525  * Bootstrap Navbar.NavItem class
3526  * @cfg {String} href  link to
3527  * @cfg {String} html content of button
3528  * @cfg {String} badge text inside badge
3529  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
3530  * @cfg {String} glyphicon name of glyphicon
3531  * @cfg {String} icon name of font awesome icon
3532  * @cfg {Boolean} active Is item active
3533  * @cfg {Boolean} disabled Is item disabled
3534  
3535  * @cfg {Boolean} preventDefault (true | false) default false
3536  * @cfg {String} tabId the tab that this item activates.
3537  * @cfg {String} tagtype (a|span) render as a href or span?
3538   
3539  * @constructor
3540  * Create a new Navbar Item
3541  * @param {Object} config The config object
3542  */
3543 Roo.bootstrap.NavItem = function(config){
3544     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
3545     this.addEvents({
3546         // raw events
3547         /**
3548          * @event click
3549          * The raw click event for the entire grid.
3550          * @param {Roo.EventObject} e
3551          */
3552         "click" : true,
3553          /**
3554             * @event changed
3555             * Fires when the active item active state changes
3556             * @param {Roo.bootstrap.NavItem} this
3557             * @param {boolean} state the new state
3558              
3559          */
3560         'changed': true
3561     });
3562    
3563 };
3564
3565 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
3566     
3567     href: false,
3568     html: '',
3569     badge: '',
3570     icon: false,
3571     glyphicon: false,
3572     active: false,
3573     preventDefault : false,
3574     tabId : false,
3575     tagtype : 'a',
3576     disabled : false,
3577     
3578     was_active : false,
3579     
3580     getAutoCreate : function(){
3581          
3582         var cfg = {
3583             tag: 'li',
3584             cls: 'nav-item'
3585             
3586         }
3587         if (this.active) {
3588             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
3589         }
3590         if (this.disabled) {
3591             cfg.cls += ' disabled';
3592         }
3593         
3594         if (this.href || this.html || this.glyphicon || this.icon) {
3595             cfg.cn = [
3596                 {
3597                     tag: this.tagtype,
3598                     href : this.href || "#",
3599                     html: this.html || ''
3600                 }
3601             ];
3602             
3603             if (this.icon) {
3604                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
3605             }
3606
3607             if(this.glyphicon) {
3608                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
3609             }
3610             
3611             if (this.menu) {
3612                 
3613                 cfg.cn[0].html += " <span class='caret'></span>";
3614              
3615             }
3616             
3617             if (this.badge !== '') {
3618                  
3619                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
3620             }
3621         }
3622         
3623         
3624         
3625         return cfg;
3626     },
3627     initEvents: function() {
3628        // Roo.log('init events?');
3629        // Roo.log(this.el.dom);
3630         if (typeof (this.menu) != 'undefined') {
3631             this.menu.parentType = this.xtype;
3632             this.menu.triggerEl = this.el;
3633             this.addxtype(Roo.apply({}, this.menu));
3634         }
3635
3636        
3637         this.el.select('a',true).on('click', this.onClick, this);
3638         // at this point parent should be available..
3639         this.parent().register(this);
3640     },
3641     
3642     onClick : function(e)
3643     {
3644          
3645         if(this.preventDefault){
3646             e.preventDefault();
3647         }
3648         if (this.disabled) {
3649             return;
3650         }
3651         
3652         var tg = Roo.bootstrap.TabGroup.get(this.navId);
3653         if (tg && tg.transition) {
3654             Roo.log("waiting for the transitionend");
3655             return;
3656         }
3657         
3658         Roo.log("fire event clicked");
3659         if(this.fireEvent('click', this, e) === false){
3660             return;
3661         };
3662         
3663         if (['tabs','pills'].indexOf(this.parent().type)!==-1) {
3664             if (typeof(this.parent().setActiveItem) !== 'undefined') {
3665                 this.parent().setActiveItem(this);
3666             }
3667         } 
3668     },
3669     
3670     isActive: function () {
3671         return this.active
3672     },
3673     setActive : function(state, fire, is_was_active)
3674     {
3675         if (this.active && !state & this.navId) {
3676             this.was_active = true;
3677             var nv = Roo.bootstrap.NavGroup.get(this.navId);
3678             if (nv) {
3679                 nv.clearWasActive(this);
3680             }
3681             
3682         }
3683         this.active = state;
3684         
3685         if (!state ) {
3686             this.el.removeClass('active');
3687         } else if (!this.el.hasClass('active')) {
3688             this.el.addClass('active');
3689         }
3690         if (fire) {
3691             this.fireEvent('changed', this, state);
3692         }
3693         
3694         // show a panel if it's registered and related..
3695         
3696         if (!this.navId || !this.tabId || !state || is_was_active) {
3697             return;
3698         }
3699         
3700         var tg = Roo.bootstrap.TabGroup.get(this.navId);
3701         if (!tg) {
3702             return;
3703         }
3704         var pan = tg.getPanelByName(this.tabId);
3705         if (!pan) {
3706             return;
3707         }
3708         // if we can not flip to new panel - go back to old nav highlight..
3709         if (false == tg.showPanel(pan)) {
3710             var nv = Roo.bootstrap.NavGroup.get(this.navId);
3711             if (nv) {
3712                 var onav = nv.getWasActive();
3713                 if (onav) {
3714                     onav.setActive(true, false, true);
3715                 }
3716             }
3717             
3718         }
3719         
3720         
3721         
3722     },
3723      // this should not be here...
3724     setDisabled : function(state)
3725     {
3726         this.disabled = state;
3727         if (!state ) {
3728             this.el.removeClass('disabled');
3729         } else if (!this.el.hasClass('disabled')) {
3730             this.el.addClass('disabled');
3731         }
3732         
3733     }
3734 });
3735  
3736
3737  /*
3738  * - LGPL
3739  *
3740  * sidebar item
3741  *
3742  *  li
3743  *    <span> icon </span>
3744  *    <span> text </span>
3745  *    <span>badge </span>
3746  */
3747
3748 /**
3749  * @class Roo.bootstrap.NavSidebarItem
3750  * @extends Roo.bootstrap.NavItem
3751  * Bootstrap Navbar.NavSidebarItem class
3752  * @constructor
3753  * Create a new Navbar Button
3754  * @param {Object} config The config object
3755  */
3756 Roo.bootstrap.NavSidebarItem = function(config){
3757     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
3758     this.addEvents({
3759         // raw events
3760         /**
3761          * @event click
3762          * The raw click event for the entire grid.
3763          * @param {Roo.EventObject} e
3764          */
3765         "click" : true,
3766          /**
3767             * @event changed
3768             * Fires when the active item active state changes
3769             * @param {Roo.bootstrap.NavSidebarItem} this
3770             * @param {boolean} state the new state
3771              
3772          */
3773         'changed': true
3774     });
3775    
3776 };
3777
3778 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
3779     
3780     
3781     getAutoCreate : function(){
3782         
3783         
3784         var a = {
3785                 tag: 'a',
3786                 href : this.href || '#',
3787                 cls: '',
3788                 html : '',
3789                 cn : []
3790         };
3791         var cfg = {
3792             tag: 'li',
3793             cls: '',
3794             cn: [ a ]
3795         }
3796         var span = {
3797             tag: 'span',
3798             html : this.html || ''
3799         }
3800         
3801         
3802         if (this.active) {
3803             cfg.cls += ' active';
3804         }
3805         
3806         // left icon..
3807         if (this.glyphicon || this.icon) {
3808             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
3809             a.cn.push({ tag : 'i', cls : c }) ;
3810         }
3811         // html..
3812         a.cn.push(span);
3813         // then badge..
3814         if (this.badge !== '') {
3815             a.cn.push({ tag: 'span',  cls : 'badge pull-right ' + (this.badgecls || ''), html: this.badge }); 
3816         }
3817         // fi
3818         if (this.menu) {
3819             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
3820             a.cls += 'dropdown-toggle treeview' ;
3821             
3822         }
3823         
3824         
3825         
3826         return cfg;
3827          
3828            
3829     }
3830    
3831      
3832  
3833 });
3834  
3835
3836  /*
3837  * - LGPL
3838  *
3839  * row
3840  * 
3841  */
3842
3843 /**
3844  * @class Roo.bootstrap.Row
3845  * @extends Roo.bootstrap.Component
3846  * Bootstrap Row class (contains columns...)
3847  * 
3848  * @constructor
3849  * Create a new Row
3850  * @param {Object} config The config object
3851  */
3852
3853 Roo.bootstrap.Row = function(config){
3854     Roo.bootstrap.Row.superclass.constructor.call(this, config);
3855 };
3856
3857 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
3858     
3859     getAutoCreate : function(){
3860        return {
3861             cls: 'row clearfix'
3862        };
3863     }
3864     
3865     
3866 });
3867
3868  
3869
3870  /*
3871  * - LGPL
3872  *
3873  * element
3874  * 
3875  */
3876
3877 /**
3878  * @class Roo.bootstrap.Element
3879  * @extends Roo.bootstrap.Component
3880  * Bootstrap Element class
3881  * @cfg {String} html contents of the element
3882  * @cfg {String} tag tag of the element
3883  * @cfg {String} cls class of the element
3884  * 
3885  * @constructor
3886  * Create a new Element
3887  * @param {Object} config The config object
3888  */
3889
3890 Roo.bootstrap.Element = function(config){
3891     Roo.bootstrap.Element.superclass.constructor.call(this, config);
3892 };
3893
3894 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
3895     
3896     tag: 'div',
3897     cls: '',
3898     html: '',
3899      
3900     
3901     getAutoCreate : function(){
3902         
3903         var cfg = {
3904             tag: this.tag,
3905             cls: this.cls,
3906             html: this.html
3907         }
3908         
3909         
3910         
3911         return cfg;
3912     }
3913    
3914 });
3915
3916  
3917
3918  /*
3919  * - LGPL
3920  *
3921  * pagination
3922  * 
3923  */
3924
3925 /**
3926  * @class Roo.bootstrap.Pagination
3927  * @extends Roo.bootstrap.Component
3928  * Bootstrap Pagination class
3929  * @cfg {String} size xs | sm | md | lg
3930  * @cfg {Boolean} inverse false | true
3931  * 
3932  * @constructor
3933  * Create a new Pagination
3934  * @param {Object} config The config object
3935  */
3936
3937 Roo.bootstrap.Pagination = function(config){
3938     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
3939 };
3940
3941 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
3942     
3943     cls: false,
3944     size: false,
3945     inverse: false,
3946     
3947     getAutoCreate : function(){
3948         var cfg = {
3949             tag: 'ul',
3950                 cls: 'pagination'
3951         };
3952         if (this.inverse) {
3953             cfg.cls += ' inverse';
3954         }
3955         if (this.html) {
3956             cfg.html=this.html;
3957         }
3958         if (this.cls) {
3959             cfg.cls += " " + this.cls;
3960         }
3961         return cfg;
3962     }
3963    
3964 });
3965
3966  
3967
3968  /*
3969  * - LGPL
3970  *
3971  * Pagination item
3972  * 
3973  */
3974
3975
3976 /**
3977  * @class Roo.bootstrap.PaginationItem
3978  * @extends Roo.bootstrap.Component
3979  * Bootstrap PaginationItem class
3980  * @cfg {String} html text
3981  * @cfg {String} href the link
3982  * @cfg {Boolean} preventDefault (true | false) default true
3983  * @cfg {Boolean} active (true | false) default false
3984  * 
3985  * 
3986  * @constructor
3987  * Create a new PaginationItem
3988  * @param {Object} config The config object
3989  */
3990
3991
3992 Roo.bootstrap.PaginationItem = function(config){
3993     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
3994     this.addEvents({
3995         // raw events
3996         /**
3997          * @event click
3998          * The raw click event for the entire grid.
3999          * @param {Roo.EventObject} e
4000          */
4001         "click" : true
4002     });
4003 };
4004
4005 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4006     
4007     href : false,
4008     html : false,
4009     preventDefault: true,
4010     active : false,
4011     cls : false,
4012     
4013     getAutoCreate : function(){
4014         var cfg= {
4015             tag: 'li',
4016             cn: [
4017                 {
4018                     tag : 'a',
4019                     href : this.href ? this.href : '#',
4020                     html : this.html ? this.html : ''
4021                 }
4022             ]
4023         };
4024         
4025         if(this.cls){
4026             cfg.cls = this.cls;
4027         }
4028         
4029         if(this.active){
4030             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4031         }
4032         
4033         return cfg;
4034     },
4035     
4036     initEvents: function() {
4037         
4038         this.el.on('click', this.onClick, this);
4039         
4040     },
4041     onClick : function(e)
4042     {
4043         Roo.log('PaginationItem on click ');
4044         if(this.preventDefault){
4045             e.preventDefault();
4046         }
4047         
4048         this.fireEvent('click', this, e);
4049     }
4050    
4051 });
4052
4053  
4054
4055  /*
4056  * - LGPL
4057  *
4058  * slider
4059  * 
4060  */
4061
4062
4063 /**
4064  * @class Roo.bootstrap.Slider
4065  * @extends Roo.bootstrap.Component
4066  * Bootstrap Slider class
4067  *    
4068  * @constructor
4069  * Create a new Slider
4070  * @param {Object} config The config object
4071  */
4072
4073 Roo.bootstrap.Slider = function(config){
4074     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4075 };
4076
4077 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
4078     
4079     getAutoCreate : function(){
4080         
4081         var cfg = {
4082             tag: 'div',
4083             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4084             cn: [
4085                 {
4086                     tag: 'a',
4087                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
4088                 }
4089             ]
4090         }
4091         
4092         return cfg;
4093     }
4094    
4095 });
4096
4097  /*
4098  * Based on:
4099  * Ext JS Library 1.1.1
4100  * Copyright(c) 2006-2007, Ext JS, LLC.
4101  *
4102  * Originally Released Under LGPL - original licence link has changed is not relivant.
4103  *
4104  * Fork - LGPL
4105  * <script type="text/javascript">
4106  */
4107  
4108
4109 /**
4110  * @class Roo.grid.ColumnModel
4111  * @extends Roo.util.Observable
4112  * This is the default implementation of a ColumnModel used by the Grid. It defines
4113  * the columns in the grid.
4114  * <br>Usage:<br>
4115  <pre><code>
4116  var colModel = new Roo.grid.ColumnModel([
4117         {header: "Ticker", width: 60, sortable: true, locked: true},
4118         {header: "Company Name", width: 150, sortable: true},
4119         {header: "Market Cap.", width: 100, sortable: true},
4120         {header: "$ Sales", width: 100, sortable: true, renderer: money},
4121         {header: "Employees", width: 100, sortable: true, resizable: false}
4122  ]);
4123  </code></pre>
4124  * <p>
4125  
4126  * The config options listed for this class are options which may appear in each
4127  * individual column definition.
4128  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4129  * @constructor
4130  * @param {Object} config An Array of column config objects. See this class's
4131  * config objects for details.
4132 */
4133 Roo.grid.ColumnModel = function(config){
4134         /**
4135      * The config passed into the constructor
4136      */
4137     this.config = config;
4138     this.lookup = {};
4139
4140     // if no id, create one
4141     // if the column does not have a dataIndex mapping,
4142     // map it to the order it is in the config
4143     for(var i = 0, len = config.length; i < len; i++){
4144         var c = config[i];
4145         if(typeof c.dataIndex == "undefined"){
4146             c.dataIndex = i;
4147         }
4148         if(typeof c.renderer == "string"){
4149             c.renderer = Roo.util.Format[c.renderer];
4150         }
4151         if(typeof c.id == "undefined"){
4152             c.id = Roo.id();
4153         }
4154         if(c.editor && c.editor.xtype){
4155             c.editor  = Roo.factory(c.editor, Roo.grid);
4156         }
4157         if(c.editor && c.editor.isFormField){
4158             c.editor = new Roo.grid.GridEditor(c.editor);
4159         }
4160         this.lookup[c.id] = c;
4161     }
4162
4163     /**
4164      * The width of columns which have no width specified (defaults to 100)
4165      * @type Number
4166      */
4167     this.defaultWidth = 100;
4168
4169     /**
4170      * Default sortable of columns which have no sortable specified (defaults to false)
4171      * @type Boolean
4172      */
4173     this.defaultSortable = false;
4174
4175     this.addEvents({
4176         /**
4177              * @event widthchange
4178              * Fires when the width of a column changes.
4179              * @param {ColumnModel} this
4180              * @param {Number} columnIndex The column index
4181              * @param {Number} newWidth The new width
4182              */
4183             "widthchange": true,
4184         /**
4185              * @event headerchange
4186              * Fires when the text of a header changes.
4187              * @param {ColumnModel} this
4188              * @param {Number} columnIndex The column index
4189              * @param {Number} newText The new header text
4190              */
4191             "headerchange": true,
4192         /**
4193              * @event hiddenchange
4194              * Fires when a column is hidden or "unhidden".
4195              * @param {ColumnModel} this
4196              * @param {Number} columnIndex The column index
4197              * @param {Boolean} hidden true if hidden, false otherwise
4198              */
4199             "hiddenchange": true,
4200             /**
4201          * @event columnmoved
4202          * Fires when a column is moved.
4203          * @param {ColumnModel} this
4204          * @param {Number} oldIndex
4205          * @param {Number} newIndex
4206          */
4207         "columnmoved" : true,
4208         /**
4209          * @event columlockchange
4210          * Fires when a column's locked state is changed
4211          * @param {ColumnModel} this
4212          * @param {Number} colIndex
4213          * @param {Boolean} locked true if locked
4214          */
4215         "columnlockchange" : true
4216     });
4217     Roo.grid.ColumnModel.superclass.constructor.call(this);
4218 };
4219 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4220     /**
4221      * @cfg {String} header The header text to display in the Grid view.
4222      */
4223     /**
4224      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4225      * {@link Roo.data.Record} definition from which to draw the column's value. If not
4226      * specified, the column's index is used as an index into the Record's data Array.
4227      */
4228     /**
4229      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4230      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4231      */
4232     /**
4233      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4234      * Defaults to the value of the {@link #defaultSortable} property.
4235      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4236      */
4237     /**
4238      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
4239      */
4240     /**
4241      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
4242      */
4243     /**
4244      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4245      */
4246     /**
4247      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4248      */
4249     /**
4250      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4251      * given the cell's data value. See {@link #setRenderer}. If not specified, the
4252      * default renderer uses the raw data value. If an object is returned (bootstrap only)
4253      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4254      */
4255        /**
4256      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
4257      */
4258     /**
4259      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
4260      */
4261
4262     /**
4263      * Returns the id of the column at the specified index.
4264      * @param {Number} index The column index
4265      * @return {String} the id
4266      */
4267     getColumnId : function(index){
4268         return this.config[index].id;
4269     },
4270
4271     /**
4272      * Returns the column for a specified id.
4273      * @param {String} id The column id
4274      * @return {Object} the column
4275      */
4276     getColumnById : function(id){
4277         return this.lookup[id];
4278     },
4279
4280     
4281     /**
4282      * Returns the column for a specified dataIndex.
4283      * @param {String} dataIndex The column dataIndex
4284      * @return {Object|Boolean} the column or false if not found
4285      */
4286     getColumnByDataIndex: function(dataIndex){
4287         var index = this.findColumnIndex(dataIndex);
4288         return index > -1 ? this.config[index] : false;
4289     },
4290     
4291     /**
4292      * Returns the index for a specified column id.
4293      * @param {String} id The column id
4294      * @return {Number} the index, or -1 if not found
4295      */
4296     getIndexById : function(id){
4297         for(var i = 0, len = this.config.length; i < len; i++){
4298             if(this.config[i].id == id){
4299                 return i;
4300             }
4301         }
4302         return -1;
4303     },
4304     
4305     /**
4306      * Returns the index for a specified column dataIndex.
4307      * @param {String} dataIndex The column dataIndex
4308      * @return {Number} the index, or -1 if not found
4309      */
4310     
4311     findColumnIndex : function(dataIndex){
4312         for(var i = 0, len = this.config.length; i < len; i++){
4313             if(this.config[i].dataIndex == dataIndex){
4314                 return i;
4315             }
4316         }
4317         return -1;
4318     },
4319     
4320     
4321     moveColumn : function(oldIndex, newIndex){
4322         var c = this.config[oldIndex];
4323         this.config.splice(oldIndex, 1);
4324         this.config.splice(newIndex, 0, c);
4325         this.dataMap = null;
4326         this.fireEvent("columnmoved", this, oldIndex, newIndex);
4327     },
4328
4329     isLocked : function(colIndex){
4330         return this.config[colIndex].locked === true;
4331     },
4332
4333     setLocked : function(colIndex, value, suppressEvent){
4334         if(this.isLocked(colIndex) == value){
4335             return;
4336         }
4337         this.config[colIndex].locked = value;
4338         if(!suppressEvent){
4339             this.fireEvent("columnlockchange", this, colIndex, value);
4340         }
4341     },
4342
4343     getTotalLockedWidth : function(){
4344         var totalWidth = 0;
4345         for(var i = 0; i < this.config.length; i++){
4346             if(this.isLocked(i) && !this.isHidden(i)){
4347                 this.totalWidth += this.getColumnWidth(i);
4348             }
4349         }
4350         return totalWidth;
4351     },
4352
4353     getLockedCount : function(){
4354         for(var i = 0, len = this.config.length; i < len; i++){
4355             if(!this.isLocked(i)){
4356                 return i;
4357             }
4358         }
4359     },
4360
4361     /**
4362      * Returns the number of columns.
4363      * @return {Number}
4364      */
4365     getColumnCount : function(visibleOnly){
4366         if(visibleOnly === true){
4367             var c = 0;
4368             for(var i = 0, len = this.config.length; i < len; i++){
4369                 if(!this.isHidden(i)){
4370                     c++;
4371                 }
4372             }
4373             return c;
4374         }
4375         return this.config.length;
4376     },
4377
4378     /**
4379      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
4380      * @param {Function} fn
4381      * @param {Object} scope (optional)
4382      * @return {Array} result
4383      */
4384     getColumnsBy : function(fn, scope){
4385         var r = [];
4386         for(var i = 0, len = this.config.length; i < len; i++){
4387             var c = this.config[i];
4388             if(fn.call(scope||this, c, i) === true){
4389                 r[r.length] = c;
4390             }
4391         }
4392         return r;
4393     },
4394
4395     /**
4396      * Returns true if the specified column is sortable.
4397      * @param {Number} col The column index
4398      * @return {Boolean}
4399      */
4400     isSortable : function(col){
4401         if(typeof this.config[col].sortable == "undefined"){
4402             return this.defaultSortable;
4403         }
4404         return this.config[col].sortable;
4405     },
4406
4407     /**
4408      * Returns the rendering (formatting) function defined for the column.
4409      * @param {Number} col The column index.
4410      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
4411      */
4412     getRenderer : function(col){
4413         if(!this.config[col].renderer){
4414             return Roo.grid.ColumnModel.defaultRenderer;
4415         }
4416         return this.config[col].renderer;
4417     },
4418
4419     /**
4420      * Sets the rendering (formatting) function for a column.
4421      * @param {Number} col The column index
4422      * @param {Function} fn The function to use to process the cell's raw data
4423      * to return HTML markup for the grid view. The render function is called with
4424      * the following parameters:<ul>
4425      * <li>Data value.</li>
4426      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
4427      * <li>css A CSS style string to apply to the table cell.</li>
4428      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
4429      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
4430      * <li>Row index</li>
4431      * <li>Column index</li>
4432      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
4433      */
4434     setRenderer : function(col, fn){
4435         this.config[col].renderer = fn;
4436     },
4437
4438     /**
4439      * Returns the width for the specified column.
4440      * @param {Number} col The column index
4441      * @return {Number}
4442      */
4443     getColumnWidth : function(col){
4444         return this.config[col].width * 1 || this.defaultWidth;
4445     },
4446
4447     /**
4448      * Sets the width for a column.
4449      * @param {Number} col The column index
4450      * @param {Number} width The new width
4451      */
4452     setColumnWidth : function(col, width, suppressEvent){
4453         this.config[col].width = width;
4454         this.totalWidth = null;
4455         if(!suppressEvent){
4456              this.fireEvent("widthchange", this, col, width);
4457         }
4458     },
4459
4460     /**
4461      * Returns the total width of all columns.
4462      * @param {Boolean} includeHidden True to include hidden column widths
4463      * @return {Number}
4464      */
4465     getTotalWidth : function(includeHidden){
4466         if(!this.totalWidth){
4467             this.totalWidth = 0;
4468             for(var i = 0, len = this.config.length; i < len; i++){
4469                 if(includeHidden || !this.isHidden(i)){
4470                     this.totalWidth += this.getColumnWidth(i);
4471                 }
4472             }
4473         }
4474         return this.totalWidth;
4475     },
4476
4477     /**
4478      * Returns the header for the specified column.
4479      * @param {Number} col The column index
4480      * @return {String}
4481      */
4482     getColumnHeader : function(col){
4483         return this.config[col].header;
4484     },
4485
4486     /**
4487      * Sets the header for a column.
4488      * @param {Number} col The column index
4489      * @param {String} header The new header
4490      */
4491     setColumnHeader : function(col, header){
4492         this.config[col].header = header;
4493         this.fireEvent("headerchange", this, col, header);
4494     },
4495
4496     /**
4497      * Returns the tooltip for the specified column.
4498      * @param {Number} col The column index
4499      * @return {String}
4500      */
4501     getColumnTooltip : function(col){
4502             return this.config[col].tooltip;
4503     },
4504     /**
4505      * Sets the tooltip for a column.
4506      * @param {Number} col The column index
4507      * @param {String} tooltip The new tooltip
4508      */
4509     setColumnTooltip : function(col, tooltip){
4510             this.config[col].tooltip = tooltip;
4511     },
4512
4513     /**
4514      * Returns the dataIndex for the specified column.
4515      * @param {Number} col The column index
4516      * @return {Number}
4517      */
4518     getDataIndex : function(col){
4519         return this.config[col].dataIndex;
4520     },
4521
4522     /**
4523      * Sets the dataIndex for a column.
4524      * @param {Number} col The column index
4525      * @param {Number} dataIndex The new dataIndex
4526      */
4527     setDataIndex : function(col, dataIndex){
4528         this.config[col].dataIndex = dataIndex;
4529     },
4530
4531     
4532     
4533     /**
4534      * Returns true if the cell is editable.
4535      * @param {Number} colIndex The column index
4536      * @param {Number} rowIndex The row index
4537      * @return {Boolean}
4538      */
4539     isCellEditable : function(colIndex, rowIndex){
4540         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
4541     },
4542
4543     /**
4544      * Returns the editor defined for the cell/column.
4545      * return false or null to disable editing.
4546      * @param {Number} colIndex The column index
4547      * @param {Number} rowIndex The row index
4548      * @return {Object}
4549      */
4550     getCellEditor : function(colIndex, rowIndex){
4551         return this.config[colIndex].editor;
4552     },
4553
4554     /**
4555      * Sets if a column is editable.
4556      * @param {Number} col The column index
4557      * @param {Boolean} editable True if the column is editable
4558      */
4559     setEditable : function(col, editable){
4560         this.config[col].editable = editable;
4561     },
4562
4563
4564     /**
4565      * Returns true if the column is hidden.
4566      * @param {Number} colIndex The column index
4567      * @return {Boolean}
4568      */
4569     isHidden : function(colIndex){
4570         return this.config[colIndex].hidden;
4571     },
4572
4573
4574     /**
4575      * Returns true if the column width cannot be changed
4576      */
4577     isFixed : function(colIndex){
4578         return this.config[colIndex].fixed;
4579     },
4580
4581     /**
4582      * Returns true if the column can be resized
4583      * @return {Boolean}
4584      */
4585     isResizable : function(colIndex){
4586         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
4587     },
4588     /**
4589      * Sets if a column is hidden.
4590      * @param {Number} colIndex The column index
4591      * @param {Boolean} hidden True if the column is hidden
4592      */
4593     setHidden : function(colIndex, hidden){
4594         this.config[colIndex].hidden = hidden;
4595         this.totalWidth = null;
4596         this.fireEvent("hiddenchange", this, colIndex, hidden);
4597     },
4598
4599     /**
4600      * Sets the editor for a column.
4601      * @param {Number} col The column index
4602      * @param {Object} editor The editor object
4603      */
4604     setEditor : function(col, editor){
4605         this.config[col].editor = editor;
4606     }
4607 });
4608
4609 Roo.grid.ColumnModel.defaultRenderer = function(value){
4610         if(typeof value == "string" && value.length < 1){
4611             return "&#160;";
4612         }
4613         return value;
4614 };
4615
4616 // Alias for backwards compatibility
4617 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
4618 /*
4619  * Based on:
4620  * Ext JS Library 1.1.1
4621  * Copyright(c) 2006-2007, Ext JS, LLC.
4622  *
4623  * Originally Released Under LGPL - original licence link has changed is not relivant.
4624  *
4625  * Fork - LGPL
4626  * <script type="text/javascript">
4627  */
4628  
4629 /**
4630  * @class Roo.LoadMask
4631  * A simple utility class for generically masking elements while loading data.  If the element being masked has
4632  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
4633  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
4634  * element's UpdateManager load indicator and will be destroyed after the initial load.
4635  * @constructor
4636  * Create a new LoadMask
4637  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
4638  * @param {Object} config The config object
4639  */
4640 Roo.LoadMask = function(el, config){
4641     this.el = Roo.get(el);
4642     Roo.apply(this, config);
4643     if(this.store){
4644         this.store.on('beforeload', this.onBeforeLoad, this);
4645         this.store.on('load', this.onLoad, this);
4646         this.store.on('loadexception', this.onLoadException, this);
4647         this.removeMask = false;
4648     }else{
4649         var um = this.el.getUpdateManager();
4650         um.showLoadIndicator = false; // disable the default indicator
4651         um.on('beforeupdate', this.onBeforeLoad, this);
4652         um.on('update', this.onLoad, this);
4653         um.on('failure', this.onLoad, this);
4654         this.removeMask = true;
4655     }
4656 };
4657
4658 Roo.LoadMask.prototype = {
4659     /**
4660      * @cfg {Boolean} removeMask
4661      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
4662      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
4663      */
4664     /**
4665      * @cfg {String} msg
4666      * The text to display in a centered loading message box (defaults to 'Loading...')
4667      */
4668     msg : 'Loading...',
4669     /**
4670      * @cfg {String} msgCls
4671      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
4672      */
4673     msgCls : 'x-mask-loading',
4674
4675     /**
4676      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
4677      * @type Boolean
4678      */
4679     disabled: false,
4680
4681     /**
4682      * Disables the mask to prevent it from being displayed
4683      */
4684     disable : function(){
4685        this.disabled = true;
4686     },
4687
4688     /**
4689      * Enables the mask so that it can be displayed
4690      */
4691     enable : function(){
4692         this.disabled = false;
4693     },
4694     
4695     onLoadException : function()
4696     {
4697         Roo.log(arguments);
4698         
4699         if (typeof(arguments[3]) != 'undefined') {
4700             Roo.MessageBox.alert("Error loading",arguments[3]);
4701         } 
4702         /*
4703         try {
4704             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
4705                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
4706             }   
4707         } catch(e) {
4708             
4709         }
4710         */
4711     
4712         
4713         
4714         this.el.unmask(this.removeMask);
4715     },
4716     // private
4717     onLoad : function()
4718     {
4719         this.el.unmask(this.removeMask);
4720     },
4721
4722     // private
4723     onBeforeLoad : function(){
4724         if(!this.disabled){
4725             this.el.mask(this.msg, this.msgCls);
4726         }
4727     },
4728
4729     // private
4730     destroy : function(){
4731         if(this.store){
4732             this.store.un('beforeload', this.onBeforeLoad, this);
4733             this.store.un('load', this.onLoad, this);
4734             this.store.un('loadexception', this.onLoadException, this);
4735         }else{
4736             var um = this.el.getUpdateManager();
4737             um.un('beforeupdate', this.onBeforeLoad, this);
4738             um.un('update', this.onLoad, this);
4739             um.un('failure', this.onLoad, this);
4740         }
4741     }
4742 };/*
4743  * - LGPL
4744  *
4745  * table
4746  * 
4747  */
4748
4749 /**
4750  * @class Roo.bootstrap.Table
4751  * @extends Roo.bootstrap.Component
4752  * Bootstrap Table class
4753  * @cfg {String} cls table class
4754  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
4755  * @cfg {String} bgcolor Specifies the background color for a table
4756  * @cfg {Number} border Specifies whether the table cells should have borders or not
4757  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
4758  * @cfg {Number} cellspacing Specifies the space between cells
4759  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
4760  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
4761  * @cfg {String} sortable Specifies that the table should be sortable
4762  * @cfg {String} summary Specifies a summary of the content of a table
4763  * @cfg {Number} width Specifies the width of a table
4764  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
4765  * 
4766  * @cfg {boolean} striped Should the rows be alternative striped
4767  * @cfg {boolean} bordered Add borders to the table
4768  * @cfg {boolean} hover Add hover highlighting
4769  * @cfg {boolean} condensed Format condensed
4770  * @cfg {boolean} responsive Format condensed
4771  * @cfg {Boolean} loadMask (true|false) default false
4772  * @cfg {Boolean} tfoot (true|false) generate tfoot, default true
4773  * @cfg {Boolean} thead (true|false) generate thead, default true
4774  * @cfg {Boolean} RowSelection (true|false) default false
4775  * @cfg {Boolean} CellSelection (true|false) default false
4776  *
4777  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
4778  
4779  * 
4780  * @constructor
4781  * Create a new Table
4782  * @param {Object} config The config object
4783  */
4784
4785 Roo.bootstrap.Table = function(config){
4786     Roo.bootstrap.Table.superclass.constructor.call(this, config);
4787     
4788     if (this.sm) {
4789         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
4790         this.sm = this.selModel;
4791         this.sm.xmodule = this.xmodule || false;
4792     }
4793     if (this.cm && typeof(this.cm.config) == 'undefined') {
4794         this.colModel = new Roo.grid.ColumnModel(this.cm);
4795         this.cm = this.colModel;
4796         this.cm.xmodule = this.xmodule || false;
4797     }
4798     if (this.store) {
4799         this.store= Roo.factory(this.store, Roo.data);
4800         this.ds = this.store;
4801         this.ds.xmodule = this.xmodule || false;
4802          
4803     }
4804     if (this.footer && this.store) {
4805         this.footer.dataSource = this.ds;
4806         this.footer = Roo.factory(this.footer);
4807     }
4808     
4809     /** @private */
4810     this.addEvents({
4811         /**
4812          * @event cellclick
4813          * Fires when a cell is clicked
4814          * @param {Roo.bootstrap.Table} this
4815          * @param {Roo.Element} el
4816          * @param {Number} rowIndex
4817          * @param {Number} columnIndex
4818          * @param {Roo.EventObject} e
4819          */
4820         "cellclick" : true,
4821         /**
4822          * @event celldblclick
4823          * Fires when a cell is double clicked
4824          * @param {Roo.bootstrap.Table} this
4825          * @param {Roo.Element} el
4826          * @param {Number} rowIndex
4827          * @param {Number} columnIndex
4828          * @param {Roo.EventObject} e
4829          */
4830         "celldblclick" : true,
4831         /**
4832          * @event rowclick
4833          * Fires when a row is clicked
4834          * @param {Roo.bootstrap.Table} this
4835          * @param {Roo.Element} el
4836          * @param {Number} rowIndex
4837          * @param {Roo.EventObject} e
4838          */
4839         "rowclick" : true,
4840         /**
4841          * @event rowdblclick
4842          * Fires when a row is double clicked
4843          * @param {Roo.bootstrap.Table} this
4844          * @param {Roo.Element} el
4845          * @param {Number} rowIndex
4846          * @param {Roo.EventObject} e
4847          */
4848         "rowdblclick" : true,
4849         /**
4850          * @event mouseover
4851          * Fires when a mouseover occur
4852          * @param {Roo.bootstrap.Table} this
4853          * @param {Roo.Element} el
4854          * @param {Number} rowIndex
4855          * @param {Number} columnIndex
4856          * @param {Roo.EventObject} e
4857          */
4858         "mouseover" : true,
4859         /**
4860          * @event mouseout
4861          * Fires when a mouseout occur
4862          * @param {Roo.bootstrap.Table} this
4863          * @param {Roo.Element} el
4864          * @param {Number} rowIndex
4865          * @param {Number} columnIndex
4866          * @param {Roo.EventObject} e
4867          */
4868         "mouseout" : true,
4869         /**
4870          * @event rowclass
4871          * Fires when a row is rendered, so you can change add a style to it.
4872          * @param {Roo.bootstrap.Table} this
4873          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
4874          */
4875         'rowclass' : true
4876         
4877     });
4878 };
4879
4880 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
4881     
4882     cls: false,
4883     align: false,
4884     bgcolor: false,
4885     border: false,
4886     cellpadding: false,
4887     cellspacing: false,
4888     frame: false,
4889     rules: false,
4890     sortable: false,
4891     summary: false,
4892     width: false,
4893     striped : false,
4894     bordered: false,
4895     hover:  false,
4896     condensed : false,
4897     responsive : false,
4898     sm : false,
4899     cm : false,
4900     store : false,
4901     loadMask : false,
4902     tfoot : true,
4903     thead : true,
4904     RowSelection : false,
4905     CellSelection : false,
4906     layout : false,
4907     
4908     // Roo.Element - the tbody
4909     mainBody: false, 
4910     
4911     getAutoCreate : function(){
4912         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
4913         
4914         cfg = {
4915             tag: 'table',
4916             cls : 'table',
4917             cn : []
4918         }
4919             
4920         if (this.striped) {
4921             cfg.cls += ' table-striped';
4922         }
4923         
4924         if (this.hover) {
4925             cfg.cls += ' table-hover';
4926         }
4927         if (this.bordered) {
4928             cfg.cls += ' table-bordered';
4929         }
4930         if (this.condensed) {
4931             cfg.cls += ' table-condensed';
4932         }
4933         if (this.responsive) {
4934             cfg.cls += ' table-responsive';
4935         }
4936         
4937         if (this.cls) {
4938             cfg.cls+=  ' ' +this.cls;
4939         }
4940         
4941         // this lot should be simplifed...
4942         
4943         if (this.align) {
4944             cfg.align=this.align;
4945         }
4946         if (this.bgcolor) {
4947             cfg.bgcolor=this.bgcolor;
4948         }
4949         if (this.border) {
4950             cfg.border=this.border;
4951         }
4952         if (this.cellpadding) {
4953             cfg.cellpadding=this.cellpadding;
4954         }
4955         if (this.cellspacing) {
4956             cfg.cellspacing=this.cellspacing;
4957         }
4958         if (this.frame) {
4959             cfg.frame=this.frame;
4960         }
4961         if (this.rules) {
4962             cfg.rules=this.rules;
4963         }
4964         if (this.sortable) {
4965             cfg.sortable=this.sortable;
4966         }
4967         if (this.summary) {
4968             cfg.summary=this.summary;
4969         }
4970         if (this.width) {
4971             cfg.width=this.width;
4972         }
4973         if (this.layout) {
4974             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
4975         }
4976         
4977         if(this.store || this.cm){
4978             if(this.thead){
4979                 cfg.cn.push(this.renderHeader());
4980             }
4981             
4982             cfg.cn.push(this.renderBody());
4983             
4984             if(this.tfoot){
4985                 cfg.cn.push(this.renderFooter());
4986             }
4987             
4988             cfg.cls+=  ' TableGrid';
4989         }
4990         
4991         return { cn : [ cfg ] };
4992     },
4993     
4994     initEvents : function()
4995     {   
4996         if(!this.store || !this.cm){
4997             return;
4998         }
4999         
5000         //Roo.log('initEvents with ds!!!!');
5001         
5002         this.mainBody = this.el.select('tbody', true).first();
5003         
5004         
5005         var _this = this;
5006         
5007         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5008             e.on('click', _this.sort, _this);
5009         });
5010         
5011         this.el.on("click", this.onClick, this);
5012         this.el.on("dblclick", this.onDblClick, this);
5013         
5014         this.parent().el.setStyle('position', 'relative');
5015         if (this.footer) {
5016             this.footer.parentId = this.id;
5017             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
5018         }
5019         
5020         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5021         
5022         this.store.on('load', this.onLoad, this);
5023         this.store.on('beforeload', this.onBeforeLoad, this);
5024         this.store.on('update', this.onUpdate, this);
5025         
5026     },
5027     
5028     onMouseover : function(e, el)
5029     {
5030         var cell = Roo.get(el);
5031         
5032         if(!cell){
5033             return;
5034         }
5035         
5036         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5037             cell = cell.findParent('td', false, true);
5038         }
5039         
5040         var row = cell.findParent('tr', false, true);
5041         var cellIndex = cell.dom.cellIndex;
5042         var rowIndex = row.dom.rowIndex - 1; // start from 0
5043         
5044         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5045         
5046     },
5047     
5048     onMouseout : function(e, el)
5049     {
5050         var cell = Roo.get(el);
5051         
5052         if(!cell){
5053             return;
5054         }
5055         
5056         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5057             cell = cell.findParent('td', false, true);
5058         }
5059         
5060         var row = cell.findParent('tr', false, true);
5061         var cellIndex = cell.dom.cellIndex;
5062         var rowIndex = row.dom.rowIndex - 1; // start from 0
5063         
5064         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5065         
5066     },
5067     
5068     onClick : function(e, el)
5069     {
5070         var cell = Roo.get(el);
5071         
5072         if(!cell || (!this.CellSelection && !this.RowSelection)){
5073             return;
5074         }
5075         
5076         
5077         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5078             cell = cell.findParent('td', false, true);
5079         }
5080         
5081         var row = cell.findParent('tr', false, true);
5082         var cellIndex = cell.dom.cellIndex;
5083         var rowIndex = row.dom.rowIndex - 1;
5084         
5085         if(this.CellSelection){
5086             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5087         }
5088         
5089         if(this.RowSelection){
5090             this.fireEvent('rowclick', this, row, rowIndex, e);
5091         }
5092         
5093         
5094     },
5095     
5096     onDblClick : function(e,el)
5097     {
5098         var cell = Roo.get(el);
5099         
5100         if(!cell || (!this.CellSelection && !this.RowSelection)){
5101             return;
5102         }
5103         
5104         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5105             cell = cell.findParent('td', false, true);
5106         }
5107         
5108         var row = cell.findParent('tr', false, true);
5109         var cellIndex = cell.dom.cellIndex;
5110         var rowIndex = row.dom.rowIndex - 1;
5111         
5112         if(this.CellSelection){
5113             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5114         }
5115         
5116         if(this.RowSelection){
5117             this.fireEvent('rowdblclick', this, row, rowIndex, e);
5118         }
5119     },
5120     
5121     sort : function(e,el)
5122     {
5123         var col = Roo.get(el)
5124         
5125         if(!col.hasClass('sortable')){
5126             return;
5127         }
5128         
5129         var sort = col.attr('sort');
5130         var dir = 'ASC';
5131         
5132         if(col.hasClass('glyphicon-arrow-up')){
5133             dir = 'DESC';
5134         }
5135         
5136         this.store.sortInfo = {field : sort, direction : dir};
5137         
5138         if (this.footer) {
5139             Roo.log("calling footer first");
5140             this.footer.onClick('first');
5141         } else {
5142         
5143             this.store.load({ params : { start : 0 } });
5144         }
5145     },
5146     
5147     renderHeader : function()
5148     {
5149         var header = {
5150             tag: 'thead',
5151             cn : []
5152         };
5153         
5154         var cm = this.cm;
5155         
5156         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5157             
5158             var config = cm.config[i];
5159                     
5160             var c = {
5161                 tag: 'th',
5162                 style : '',
5163                 html: cm.getColumnHeader(i)
5164             };
5165             
5166             if(typeof(config.hidden) != 'undefined' && config.hidden){
5167                 c.style += ' display:none;';
5168             }
5169             
5170             if(typeof(config.dataIndex) != 'undefined'){
5171                 c.sort = config.dataIndex;
5172             }
5173             
5174             if(typeof(config.sortable) != 'undefined' && config.sortable){
5175                 c.cls = 'sortable';
5176             }
5177             
5178             if(typeof(config.align) != 'undefined' && config.align.length){
5179                 c.style += ' text-align:' + config.align + ';';
5180             }
5181             
5182             if(typeof(config.width) != 'undefined'){
5183                 c.style += ' width:' + config.width + 'px;';
5184             }
5185             
5186             header.cn.push(c)
5187         }
5188         
5189         return header;
5190     },
5191     
5192     renderBody : function()
5193     {
5194         var body = {
5195             tag: 'tbody',
5196             cn : [
5197                 {
5198                     tag: 'tr',
5199                     cn : [
5200                         {
5201                             tag : 'td',
5202                             colspan :  this.cm.getColumnCount()
5203                         }
5204                     ]
5205                 }
5206             ]
5207         };
5208         
5209         return body;
5210     },
5211     
5212     renderFooter : function()
5213     {
5214         var footer = {
5215             tag: 'tfoot',
5216             cn : [
5217                 {
5218                     tag: 'tr',
5219                     cn : [
5220                         {
5221                             tag : 'td',
5222                             colspan :  this.cm.getColumnCount()
5223                         }
5224                     ]
5225                 }
5226             ]
5227         };
5228         
5229         return footer;
5230     },
5231     
5232     
5233     
5234     onLoad : function()
5235     {
5236         Roo.log('ds onload');
5237         this.clear();
5238         
5239         var _this = this;
5240         var cm = this.cm;
5241         var ds = this.store;
5242         
5243         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5244             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
5245             
5246             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
5247                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
5248             }
5249             
5250             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
5251                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
5252             }
5253         });
5254         
5255         var tbody =  this.mainBody;
5256               
5257         if(ds.getCount() > 0){
5258             ds.data.each(function(d,rowIndex){
5259                 var row =  this.renderRow(cm, ds, rowIndex);
5260                 
5261                 tbody.createChild(row);
5262                 
5263                 var _this = this;
5264                 
5265                 if(row.cellObjects.length){
5266                     Roo.each(row.cellObjects, function(r){
5267                         _this.renderCellObject(r);
5268                     })
5269                 }
5270                 
5271             }, this);
5272         }
5273         
5274         Roo.each(this.el.select('tbody td', true).elements, function(e){
5275             e.on('mouseover', _this.onMouseover, _this);
5276         });
5277         
5278         Roo.each(this.el.select('tbody td', true).elements, function(e){
5279             e.on('mouseout', _this.onMouseout, _this);
5280         });
5281
5282         //if(this.loadMask){
5283         //    this.maskEl.hide();
5284         //}
5285     },
5286     
5287     
5288     onUpdate : function(ds,record)
5289     {
5290         this.refreshRow(record);
5291     },
5292     onRemove : function(ds, record, index, isUpdate){
5293         if(isUpdate !== true){
5294             this.fireEvent("beforerowremoved", this, index, record);
5295         }
5296         var bt = this.mainBody.dom;
5297         if(bt.rows[index]){
5298             bt.removeChild(bt.rows[index]);
5299         }
5300         
5301         if(isUpdate !== true){
5302             //this.stripeRows(index);
5303             //this.syncRowHeights(index, index);
5304             //this.layout();
5305             this.fireEvent("rowremoved", this, index, record);
5306         }
5307     },
5308     
5309     
5310     refreshRow : function(record){
5311         var ds = this.store, index;
5312         if(typeof record == 'number'){
5313             index = record;
5314             record = ds.getAt(index);
5315         }else{
5316             index = ds.indexOf(record);
5317         }
5318         this.insertRow(ds, index, true);
5319         this.onRemove(ds, record, index+1, true);
5320         //this.syncRowHeights(index, index);
5321         //this.layout();
5322         this.fireEvent("rowupdated", this, index, record);
5323     },
5324     
5325     insertRow : function(dm, rowIndex, isUpdate){
5326         
5327         if(!isUpdate){
5328             this.fireEvent("beforerowsinserted", this, rowIndex);
5329         }
5330             //var s = this.getScrollState();
5331         var row = this.renderRow(this.cm, this.store, rowIndex);
5332         // insert before rowIndex..
5333         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
5334         
5335         var _this = this;
5336                 
5337         if(row.cellObjects.length){
5338             Roo.each(row.cellObjects, function(r){
5339                 _this.renderCellObject(r);
5340             })
5341         }
5342             
5343         if(!isUpdate){
5344             this.fireEvent("rowsinserted", this, rowIndex);
5345             //this.syncRowHeights(firstRow, lastRow);
5346             //this.stripeRows(firstRow);
5347             //this.layout();
5348         }
5349         
5350     },
5351     
5352     
5353     getRowDom : function(rowIndex)
5354     {
5355         // not sure if I need to check this.. but let's do it anyway..
5356         return (this.mainBody.dom.rows && (rowIndex-1) < this.mainBody.dom.rows.length ) ?
5357                 this.mainBody.dom.rows[rowIndex] : false
5358     },
5359     // returns the object tree for a tr..
5360   
5361     
5362     renderRow : function(cm, ds, rowIndex) {
5363         
5364         var d = ds.getAt(rowIndex);
5365         
5366         var row = {
5367             tag : 'tr',
5368             cn : []
5369         };
5370             
5371         var cellObjects = [];
5372         
5373         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5374             var config = cm.config[i];
5375             
5376             var renderer = cm.getRenderer(i);
5377             var value = '';
5378             var id = false;
5379             
5380             if(typeof(renderer) !== 'undefined'){
5381                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
5382             }
5383             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
5384             // and are rendered into the cells after the row is rendered - using the id for the element.
5385             
5386             if(typeof(value) === 'object'){
5387                 id = Roo.id();
5388                 cellObjects.push({
5389                     container : id,
5390                     cfg : value 
5391                 })
5392             }
5393             
5394             var rowcfg = {
5395                 record: d,
5396                 rowIndex : rowIndex,
5397                 colIndex : i,
5398                 rowClass : ''
5399             }
5400
5401             this.fireEvent('rowclass', this, rowcfg);
5402             
5403             var td = {
5404                 tag: 'td',
5405                 cls : rowcfg.rowClass,
5406                 style: '',
5407                 html: (typeof(value) === 'object') ? '' : value
5408             };
5409             
5410             if (id) {
5411                 td.id = id;
5412             }
5413             
5414             if(typeof(config.hidden) != 'undefined' && config.hidden){
5415                 td.style += ' display:none;';
5416             }
5417             
5418             if(typeof(config.align) != 'undefined' && config.align.length){
5419                 td.style += ' text-align:' + config.align + ';';
5420             }
5421             
5422             if(typeof(config.width) != 'undefined'){
5423                 td.style += ' width:' +  config.width + 'px;';
5424             }
5425              
5426             row.cn.push(td);
5427            
5428         }
5429         
5430         row.cellObjects = cellObjects;
5431         
5432         return row;
5433           
5434     },
5435     
5436     
5437     
5438     onBeforeLoad : function()
5439     {
5440         //Roo.log('ds onBeforeLoad');
5441         
5442         //this.clear();
5443         
5444         //if(this.loadMask){
5445         //    this.maskEl.show();
5446         //}
5447     },
5448     
5449     clear : function()
5450     {
5451         this.el.select('tbody', true).first().dom.innerHTML = '';
5452     },
5453     
5454     getSelectionModel : function(){
5455         if(!this.selModel){
5456             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
5457         }
5458         return this.selModel;
5459     },
5460     /*
5461      * Render the Roo.bootstrap object from renderder
5462      */
5463     renderCellObject : function(r)
5464     {
5465         var _this = this;
5466         
5467         var t = r.cfg.render(r.container);
5468         
5469         if(r.cfg.cn){
5470             Roo.each(r.cfg.cn, function(c){
5471                 var child = {
5472                     container: t.getChildContainer(),
5473                     cfg: c
5474                 }
5475                 _this.renderCellObject(child);
5476             })
5477         }
5478     }
5479    
5480 });
5481
5482  
5483
5484  /*
5485  * - LGPL
5486  *
5487  * table cell
5488  * 
5489  */
5490
5491 /**
5492  * @class Roo.bootstrap.TableCell
5493  * @extends Roo.bootstrap.Component
5494  * Bootstrap TableCell class
5495  * @cfg {String} html cell contain text
5496  * @cfg {String} cls cell class
5497  * @cfg {String} tag cell tag (td|th) default td
5498  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
5499  * @cfg {String} align Aligns the content in a cell
5500  * @cfg {String} axis Categorizes cells
5501  * @cfg {String} bgcolor Specifies the background color of a cell
5502  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5503  * @cfg {Number} colspan Specifies the number of columns a cell should span
5504  * @cfg {String} headers Specifies one or more header cells a cell is related to
5505  * @cfg {Number} height Sets the height of a cell
5506  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
5507  * @cfg {Number} rowspan Sets the number of rows a cell should span
5508  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
5509  * @cfg {String} valign Vertical aligns the content in a cell
5510  * @cfg {Number} width Specifies the width of a cell
5511  * 
5512  * @constructor
5513  * Create a new TableCell
5514  * @param {Object} config The config object
5515  */
5516
5517 Roo.bootstrap.TableCell = function(config){
5518     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
5519 };
5520
5521 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
5522     
5523     html: false,
5524     cls: false,
5525     tag: false,
5526     abbr: false,
5527     align: false,
5528     axis: false,
5529     bgcolor: false,
5530     charoff: false,
5531     colspan: false,
5532     headers: false,
5533     height: false,
5534     nowrap: false,
5535     rowspan: false,
5536     scope: false,
5537     valign: false,
5538     width: false,
5539     
5540     
5541     getAutoCreate : function(){
5542         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
5543         
5544         cfg = {
5545             tag: 'td'
5546         }
5547         
5548         if(this.tag){
5549             cfg.tag = this.tag;
5550         }
5551         
5552         if (this.html) {
5553             cfg.html=this.html
5554         }
5555         if (this.cls) {
5556             cfg.cls=this.cls
5557         }
5558         if (this.abbr) {
5559             cfg.abbr=this.abbr
5560         }
5561         if (this.align) {
5562             cfg.align=this.align
5563         }
5564         if (this.axis) {
5565             cfg.axis=this.axis
5566         }
5567         if (this.bgcolor) {
5568             cfg.bgcolor=this.bgcolor
5569         }
5570         if (this.charoff) {
5571             cfg.charoff=this.charoff
5572         }
5573         if (this.colspan) {
5574             cfg.colspan=this.colspan
5575         }
5576         if (this.headers) {
5577             cfg.headers=this.headers
5578         }
5579         if (this.height) {
5580             cfg.height=this.height
5581         }
5582         if (this.nowrap) {
5583             cfg.nowrap=this.nowrap
5584         }
5585         if (this.rowspan) {
5586             cfg.rowspan=this.rowspan
5587         }
5588         if (this.scope) {
5589             cfg.scope=this.scope
5590         }
5591         if (this.valign) {
5592             cfg.valign=this.valign
5593         }
5594         if (this.width) {
5595             cfg.width=this.width
5596         }
5597         
5598         
5599         return cfg;
5600     }
5601    
5602 });
5603
5604  
5605
5606  /*
5607  * - LGPL
5608  *
5609  * table row
5610  * 
5611  */
5612
5613 /**
5614  * @class Roo.bootstrap.TableRow
5615  * @extends Roo.bootstrap.Component
5616  * Bootstrap TableRow class
5617  * @cfg {String} cls row class
5618  * @cfg {String} align Aligns the content in a table row
5619  * @cfg {String} bgcolor Specifies a background color for a table row
5620  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5621  * @cfg {String} valign Vertical aligns the content in a table row
5622  * 
5623  * @constructor
5624  * Create a new TableRow
5625  * @param {Object} config The config object
5626  */
5627
5628 Roo.bootstrap.TableRow = function(config){
5629     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
5630 };
5631
5632 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
5633     
5634     cls: false,
5635     align: false,
5636     bgcolor: false,
5637     charoff: false,
5638     valign: false,
5639     
5640     getAutoCreate : function(){
5641         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
5642         
5643         cfg = {
5644             tag: 'tr'
5645         }
5646             
5647         if(this.cls){
5648             cfg.cls = this.cls;
5649         }
5650         if(this.align){
5651             cfg.align = this.align;
5652         }
5653         if(this.bgcolor){
5654             cfg.bgcolor = this.bgcolor;
5655         }
5656         if(this.charoff){
5657             cfg.charoff = this.charoff;
5658         }
5659         if(this.valign){
5660             cfg.valign = this.valign;
5661         }
5662         
5663         return cfg;
5664     }
5665    
5666 });
5667
5668  
5669
5670  /*
5671  * - LGPL
5672  *
5673  * table body
5674  * 
5675  */
5676
5677 /**
5678  * @class Roo.bootstrap.TableBody
5679  * @extends Roo.bootstrap.Component
5680  * Bootstrap TableBody class
5681  * @cfg {String} cls element class
5682  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
5683  * @cfg {String} align Aligns the content inside the element
5684  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
5685  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
5686  * 
5687  * @constructor
5688  * Create a new TableBody
5689  * @param {Object} config The config object
5690  */
5691
5692 Roo.bootstrap.TableBody = function(config){
5693     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
5694 };
5695
5696 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
5697     
5698     cls: false,
5699     tag: false,
5700     align: false,
5701     charoff: false,
5702     valign: false,
5703     
5704     getAutoCreate : function(){
5705         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
5706         
5707         cfg = {
5708             tag: 'tbody'
5709         }
5710             
5711         if (this.cls) {
5712             cfg.cls=this.cls
5713         }
5714         if(this.tag){
5715             cfg.tag = this.tag;
5716         }
5717         
5718         if(this.align){
5719             cfg.align = this.align;
5720         }
5721         if(this.charoff){
5722             cfg.charoff = this.charoff;
5723         }
5724         if(this.valign){
5725             cfg.valign = this.valign;
5726         }
5727         
5728         return cfg;
5729     }
5730     
5731     
5732 //    initEvents : function()
5733 //    {
5734 //        
5735 //        if(!this.store){
5736 //            return;
5737 //        }
5738 //        
5739 //        this.store = Roo.factory(this.store, Roo.data);
5740 //        this.store.on('load', this.onLoad, this);
5741 //        
5742 //        this.store.load();
5743 //        
5744 //    },
5745 //    
5746 //    onLoad: function () 
5747 //    {   
5748 //        this.fireEvent('load', this);
5749 //    }
5750 //    
5751 //   
5752 });
5753
5754  
5755
5756  /*
5757  * Based on:
5758  * Ext JS Library 1.1.1
5759  * Copyright(c) 2006-2007, Ext JS, LLC.
5760  *
5761  * Originally Released Under LGPL - original licence link has changed is not relivant.
5762  *
5763  * Fork - LGPL
5764  * <script type="text/javascript">
5765  */
5766
5767 // as we use this in bootstrap.
5768 Roo.namespace('Roo.form');
5769  /**
5770  * @class Roo.form.Action
5771  * Internal Class used to handle form actions
5772  * @constructor
5773  * @param {Roo.form.BasicForm} el The form element or its id
5774  * @param {Object} config Configuration options
5775  */
5776
5777  
5778  
5779 // define the action interface
5780 Roo.form.Action = function(form, options){
5781     this.form = form;
5782     this.options = options || {};
5783 };
5784 /**
5785  * Client Validation Failed
5786  * @const 
5787  */
5788 Roo.form.Action.CLIENT_INVALID = 'client';
5789 /**
5790  * Server Validation Failed
5791  * @const 
5792  */
5793 Roo.form.Action.SERVER_INVALID = 'server';
5794  /**
5795  * Connect to Server Failed
5796  * @const 
5797  */
5798 Roo.form.Action.CONNECT_FAILURE = 'connect';
5799 /**
5800  * Reading Data from Server Failed
5801  * @const 
5802  */
5803 Roo.form.Action.LOAD_FAILURE = 'load';
5804
5805 Roo.form.Action.prototype = {
5806     type : 'default',
5807     failureType : undefined,
5808     response : undefined,
5809     result : undefined,
5810
5811     // interface method
5812     run : function(options){
5813
5814     },
5815
5816     // interface method
5817     success : function(response){
5818
5819     },
5820
5821     // interface method
5822     handleResponse : function(response){
5823
5824     },
5825
5826     // default connection failure
5827     failure : function(response){
5828         
5829         this.response = response;
5830         this.failureType = Roo.form.Action.CONNECT_FAILURE;
5831         this.form.afterAction(this, false);
5832     },
5833
5834     processResponse : function(response){
5835         this.response = response;
5836         if(!response.responseText){
5837             return true;
5838         }
5839         this.result = this.handleResponse(response);
5840         return this.result;
5841     },
5842
5843     // utility functions used internally
5844     getUrl : function(appendParams){
5845         var url = this.options.url || this.form.url || this.form.el.dom.action;
5846         if(appendParams){
5847             var p = this.getParams();
5848             if(p){
5849                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
5850             }
5851         }
5852         return url;
5853     },
5854
5855     getMethod : function(){
5856         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
5857     },
5858
5859     getParams : function(){
5860         var bp = this.form.baseParams;
5861         var p = this.options.params;
5862         if(p){
5863             if(typeof p == "object"){
5864                 p = Roo.urlEncode(Roo.applyIf(p, bp));
5865             }else if(typeof p == 'string' && bp){
5866                 p += '&' + Roo.urlEncode(bp);
5867             }
5868         }else if(bp){
5869             p = Roo.urlEncode(bp);
5870         }
5871         return p;
5872     },
5873
5874     createCallback : function(){
5875         return {
5876             success: this.success,
5877             failure: this.failure,
5878             scope: this,
5879             timeout: (this.form.timeout*1000),
5880             upload: this.form.fileUpload ? this.success : undefined
5881         };
5882     }
5883 };
5884
5885 Roo.form.Action.Submit = function(form, options){
5886     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
5887 };
5888
5889 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
5890     type : 'submit',
5891
5892     haveProgress : false,
5893     uploadComplete : false,
5894     
5895     // uploadProgress indicator.
5896     uploadProgress : function()
5897     {
5898         if (!this.form.progressUrl) {
5899             return;
5900         }
5901         
5902         if (!this.haveProgress) {
5903             Roo.MessageBox.progress("Uploading", "Uploading");
5904         }
5905         if (this.uploadComplete) {
5906            Roo.MessageBox.hide();
5907            return;
5908         }
5909         
5910         this.haveProgress = true;
5911    
5912         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
5913         
5914         var c = new Roo.data.Connection();
5915         c.request({
5916             url : this.form.progressUrl,
5917             params: {
5918                 id : uid
5919             },
5920             method: 'GET',
5921             success : function(req){
5922                //console.log(data);
5923                 var rdata = false;
5924                 var edata;
5925                 try  {
5926                    rdata = Roo.decode(req.responseText)
5927                 } catch (e) {
5928                     Roo.log("Invalid data from server..");
5929                     Roo.log(edata);
5930                     return;
5931                 }
5932                 if (!rdata || !rdata.success) {
5933                     Roo.log(rdata);
5934                     Roo.MessageBox.alert(Roo.encode(rdata));
5935                     return;
5936                 }
5937                 var data = rdata.data;
5938                 
5939                 if (this.uploadComplete) {
5940                    Roo.MessageBox.hide();
5941                    return;
5942                 }
5943                    
5944                 if (data){
5945                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
5946                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
5947                     );
5948                 }
5949                 this.uploadProgress.defer(2000,this);
5950             },
5951        
5952             failure: function(data) {
5953                 Roo.log('progress url failed ');
5954                 Roo.log(data);
5955             },
5956             scope : this
5957         });
5958            
5959     },
5960     
5961     
5962     run : function()
5963     {
5964         // run get Values on the form, so it syncs any secondary forms.
5965         this.form.getValues();
5966         
5967         var o = this.options;
5968         var method = this.getMethod();
5969         var isPost = method == 'POST';
5970         if(o.clientValidation === false || this.form.isValid()){
5971             
5972             if (this.form.progressUrl) {
5973                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
5974                     (new Date() * 1) + '' + Math.random());
5975                     
5976             } 
5977             
5978             
5979             Roo.Ajax.request(Roo.apply(this.createCallback(), {
5980                 form:this.form.el.dom,
5981                 url:this.getUrl(!isPost),
5982                 method: method,
5983                 params:isPost ? this.getParams() : null,
5984                 isUpload: this.form.fileUpload
5985             }));
5986             
5987             this.uploadProgress();
5988
5989         }else if (o.clientValidation !== false){ // client validation failed
5990             this.failureType = Roo.form.Action.CLIENT_INVALID;
5991             this.form.afterAction(this, false);
5992         }
5993     },
5994
5995     success : function(response)
5996     {
5997         this.uploadComplete= true;
5998         if (this.haveProgress) {
5999             Roo.MessageBox.hide();
6000         }
6001         
6002         
6003         var result = this.processResponse(response);
6004         if(result === true || result.success){
6005             this.form.afterAction(this, true);
6006             return;
6007         }
6008         if(result.errors){
6009             this.form.markInvalid(result.errors);
6010             this.failureType = Roo.form.Action.SERVER_INVALID;
6011         }
6012         this.form.afterAction(this, false);
6013     },
6014     failure : function(response)
6015     {
6016         this.uploadComplete= true;
6017         if (this.haveProgress) {
6018             Roo.MessageBox.hide();
6019         }
6020         
6021         this.response = response;
6022         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6023         this.form.afterAction(this, false);
6024     },
6025     
6026     handleResponse : function(response){
6027         if(this.form.errorReader){
6028             var rs = this.form.errorReader.read(response);
6029             var errors = [];
6030             if(rs.records){
6031                 for(var i = 0, len = rs.records.length; i < len; i++) {
6032                     var r = rs.records[i];
6033                     errors[i] = r.data;
6034                 }
6035             }
6036             if(errors.length < 1){
6037                 errors = null;
6038             }
6039             return {
6040                 success : rs.success,
6041                 errors : errors
6042             };
6043         }
6044         var ret = false;
6045         try {
6046             ret = Roo.decode(response.responseText);
6047         } catch (e) {
6048             ret = {
6049                 success: false,
6050                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6051                 errors : []
6052             };
6053         }
6054         return ret;
6055         
6056     }
6057 });
6058
6059
6060 Roo.form.Action.Load = function(form, options){
6061     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6062     this.reader = this.form.reader;
6063 };
6064
6065 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6066     type : 'load',
6067
6068     run : function(){
6069         
6070         Roo.Ajax.request(Roo.apply(
6071                 this.createCallback(), {
6072                     method:this.getMethod(),
6073                     url:this.getUrl(false),
6074                     params:this.getParams()
6075         }));
6076     },
6077
6078     success : function(response){
6079         
6080         var result = this.processResponse(response);
6081         if(result === true || !result.success || !result.data){
6082             this.failureType = Roo.form.Action.LOAD_FAILURE;
6083             this.form.afterAction(this, false);
6084             return;
6085         }
6086         this.form.clearInvalid();
6087         this.form.setValues(result.data);
6088         this.form.afterAction(this, true);
6089     },
6090
6091     handleResponse : function(response){
6092         if(this.form.reader){
6093             var rs = this.form.reader.read(response);
6094             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
6095             return {
6096                 success : rs.success,
6097                 data : data
6098             };
6099         }
6100         return Roo.decode(response.responseText);
6101     }
6102 });
6103
6104 Roo.form.Action.ACTION_TYPES = {
6105     'load' : Roo.form.Action.Load,
6106     'submit' : Roo.form.Action.Submit
6107 };/*
6108  * - LGPL
6109  *
6110  * form
6111  * 
6112  */
6113
6114 /**
6115  * @class Roo.bootstrap.Form
6116  * @extends Roo.bootstrap.Component
6117  * Bootstrap Form class
6118  * @cfg {String} method  GET | POST (default POST)
6119  * @cfg {String} labelAlign top | left (default top)
6120  * @cfg {String} align left  | right - for navbars
6121  * @cfg {Boolean} loadMask load mask when submit (default true)
6122
6123  * 
6124  * @constructor
6125  * Create a new Form
6126  * @param {Object} config The config object
6127  */
6128
6129
6130 Roo.bootstrap.Form = function(config){
6131     Roo.bootstrap.Form.superclass.constructor.call(this, config);
6132     this.addEvents({
6133         /**
6134          * @event clientvalidation
6135          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
6136          * @param {Form} this
6137          * @param {Boolean} valid true if the form has passed client-side validation
6138          */
6139         clientvalidation: true,
6140         /**
6141          * @event beforeaction
6142          * Fires before any action is performed. Return false to cancel the action.
6143          * @param {Form} this
6144          * @param {Action} action The action to be performed
6145          */
6146         beforeaction: true,
6147         /**
6148          * @event actionfailed
6149          * Fires when an action fails.
6150          * @param {Form} this
6151          * @param {Action} action The action that failed
6152          */
6153         actionfailed : true,
6154         /**
6155          * @event actioncomplete
6156          * Fires when an action is completed.
6157          * @param {Form} this
6158          * @param {Action} action The action that completed
6159          */
6160         actioncomplete : true
6161     });
6162     
6163 };
6164
6165 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
6166       
6167      /**
6168      * @cfg {String} method
6169      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
6170      */
6171     method : 'POST',
6172     /**
6173      * @cfg {String} url
6174      * The URL to use for form actions if one isn't supplied in the action options.
6175      */
6176     /**
6177      * @cfg {Boolean} fileUpload
6178      * Set to true if this form is a file upload.
6179      */
6180      
6181     /**
6182      * @cfg {Object} baseParams
6183      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
6184      */
6185       
6186     /**
6187      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
6188      */
6189     timeout: 30,
6190     /**
6191      * @cfg {Sting} align (left|right) for navbar forms
6192      */
6193     align : 'left',
6194
6195     // private
6196     activeAction : null,
6197  
6198     /**
6199      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
6200      * element by passing it or its id or mask the form itself by passing in true.
6201      * @type Mixed
6202      */
6203     waitMsgTarget : false,
6204     
6205     loadMask : true,
6206     
6207     getAutoCreate : function(){
6208         
6209         var cfg = {
6210             tag: 'form',
6211             method : this.method || 'POST',
6212             id : this.id || Roo.id(),
6213             cls : ''
6214         }
6215         if (this.parent().xtype.match(/^Nav/)) {
6216             cfg.cls = 'navbar-form navbar-' + this.align;
6217             
6218         }
6219         
6220         if (this.labelAlign == 'left' ) {
6221             cfg.cls += ' form-horizontal';
6222         }
6223         
6224         
6225         return cfg;
6226     },
6227     initEvents : function()
6228     {
6229         this.el.on('submit', this.onSubmit, this);
6230         // this was added as random key presses on the form where triggering form submit.
6231         this.el.on('keypress', function(e) {
6232             if (e.getCharCode() != 13) {
6233                 return true;
6234             }
6235             // we might need to allow it for textareas.. and some other items.
6236             // check e.getTarget().
6237             
6238             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
6239                 return true;
6240             }
6241         
6242             Roo.log("keypress blocked");
6243             
6244             e.preventDefault();
6245             return false;
6246         });
6247         
6248     },
6249     // private
6250     onSubmit : function(e){
6251         e.stopEvent();
6252     },
6253     
6254      /**
6255      * Returns true if client-side validation on the form is successful.
6256      * @return Boolean
6257      */
6258     isValid : function(){
6259         var items = this.getItems();
6260         var valid = true;
6261         items.each(function(f){
6262            if(!f.validate()){
6263                valid = false;
6264                
6265            }
6266         });
6267         return valid;
6268     },
6269     /**
6270      * Returns true if any fields in this form have changed since their original load.
6271      * @return Boolean
6272      */
6273     isDirty : function(){
6274         var dirty = false;
6275         var items = this.getItems();
6276         items.each(function(f){
6277            if(f.isDirty()){
6278                dirty = true;
6279                return false;
6280            }
6281            return true;
6282         });
6283         return dirty;
6284     },
6285      /**
6286      * Performs a predefined action (submit or load) or custom actions you define on this form.
6287      * @param {String} actionName The name of the action type
6288      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
6289      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
6290      * accept other config options):
6291      * <pre>
6292 Property          Type             Description
6293 ----------------  ---------------  ----------------------------------------------------------------------------------
6294 url               String           The url for the action (defaults to the form's url)
6295 method            String           The form method to use (defaults to the form's method, or POST if not defined)
6296 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
6297 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
6298                                    validate the form on the client (defaults to false)
6299      * </pre>
6300      * @return {BasicForm} this
6301      */
6302     doAction : function(action, options){
6303         if(typeof action == 'string'){
6304             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
6305         }
6306         if(this.fireEvent('beforeaction', this, action) !== false){
6307             this.beforeAction(action);
6308             action.run.defer(100, action);
6309         }
6310         return this;
6311     },
6312     
6313     // private
6314     beforeAction : function(action){
6315         var o = action.options;
6316         
6317         if(this.loadMask){
6318             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6319         }
6320         // not really supported yet.. ??
6321         
6322         //if(this.waitMsgTarget === true){
6323         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6324         //}else if(this.waitMsgTarget){
6325         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
6326         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
6327         //}else {
6328         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
6329        // }
6330          
6331     },
6332
6333     // private
6334     afterAction : function(action, success){
6335         this.activeAction = null;
6336         var o = action.options;
6337         
6338         //if(this.waitMsgTarget === true){
6339             this.el.unmask();
6340         //}else if(this.waitMsgTarget){
6341         //    this.waitMsgTarget.unmask();
6342         //}else{
6343         //    Roo.MessageBox.updateProgress(1);
6344         //    Roo.MessageBox.hide();
6345        // }
6346         // 
6347         if(success){
6348             if(o.reset){
6349                 this.reset();
6350             }
6351             Roo.callback(o.success, o.scope, [this, action]);
6352             this.fireEvent('actioncomplete', this, action);
6353             
6354         }else{
6355             
6356             // failure condition..
6357             // we have a scenario where updates need confirming.
6358             // eg. if a locking scenario exists..
6359             // we look for { errors : { needs_confirm : true }} in the response.
6360             if (
6361                 (typeof(action.result) != 'undefined')  &&
6362                 (typeof(action.result.errors) != 'undefined')  &&
6363                 (typeof(action.result.errors.needs_confirm) != 'undefined')
6364            ){
6365                 var _t = this;
6366                 Roo.log("not supported yet");
6367                  /*
6368                 
6369                 Roo.MessageBox.confirm(
6370                     "Change requires confirmation",
6371                     action.result.errorMsg,
6372                     function(r) {
6373                         if (r != 'yes') {
6374                             return;
6375                         }
6376                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
6377                     }
6378                     
6379                 );
6380                 */
6381                 
6382                 
6383                 return;
6384             }
6385             
6386             Roo.callback(o.failure, o.scope, [this, action]);
6387             // show an error message if no failed handler is set..
6388             if (!this.hasListener('actionfailed')) {
6389                 Roo.log("need to add dialog support");
6390                 /*
6391                 Roo.MessageBox.alert("Error",
6392                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
6393                         action.result.errorMsg :
6394                         "Saving Failed, please check your entries or try again"
6395                 );
6396                 */
6397             }
6398             
6399             this.fireEvent('actionfailed', this, action);
6400         }
6401         
6402     },
6403     /**
6404      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
6405      * @param {String} id The value to search for
6406      * @return Field
6407      */
6408     findField : function(id){
6409         var items = this.getItems();
6410         var field = items.get(id);
6411         if(!field){
6412              items.each(function(f){
6413                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
6414                     field = f;
6415                     return false;
6416                 }
6417                 return true;
6418             });
6419         }
6420         return field || null;
6421     },
6422      /**
6423      * Mark fields in this form invalid in bulk.
6424      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
6425      * @return {BasicForm} this
6426      */
6427     markInvalid : function(errors){
6428         if(errors instanceof Array){
6429             for(var i = 0, len = errors.length; i < len; i++){
6430                 var fieldError = errors[i];
6431                 var f = this.findField(fieldError.id);
6432                 if(f){
6433                     f.markInvalid(fieldError.msg);
6434                 }
6435             }
6436         }else{
6437             var field, id;
6438             for(id in errors){
6439                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
6440                     field.markInvalid(errors[id]);
6441                 }
6442             }
6443         }
6444         //Roo.each(this.childForms || [], function (f) {
6445         //    f.markInvalid(errors);
6446         //});
6447         
6448         return this;
6449     },
6450
6451     /**
6452      * Set values for fields in this form in bulk.
6453      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
6454      * @return {BasicForm} this
6455      */
6456     setValues : function(values){
6457         if(values instanceof Array){ // array of objects
6458             for(var i = 0, len = values.length; i < len; i++){
6459                 var v = values[i];
6460                 var f = this.findField(v.id);
6461                 if(f){
6462                     f.setValue(v.value);
6463                     if(this.trackResetOnLoad){
6464                         f.originalValue = f.getValue();
6465                     }
6466                 }
6467             }
6468         }else{ // object hash
6469             var field, id;
6470             for(id in values){
6471                 if(typeof values[id] != 'function' && (field = this.findField(id))){
6472                     
6473                     if (field.setFromData && 
6474                         field.valueField && 
6475                         field.displayField &&
6476                         // combos' with local stores can 
6477                         // be queried via setValue()
6478                         // to set their value..
6479                         (field.store && !field.store.isLocal)
6480                         ) {
6481                         // it's a combo
6482                         var sd = { };
6483                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
6484                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
6485                         field.setFromData(sd);
6486                         
6487                     } else {
6488                         field.setValue(values[id]);
6489                     }
6490                     
6491                     
6492                     if(this.trackResetOnLoad){
6493                         field.originalValue = field.getValue();
6494                     }
6495                 }
6496             }
6497         }
6498          
6499         //Roo.each(this.childForms || [], function (f) {
6500         //    f.setValues(values);
6501         //});
6502                 
6503         return this;
6504     },
6505
6506     /**
6507      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
6508      * they are returned as an array.
6509      * @param {Boolean} asString
6510      * @return {Object}
6511      */
6512     getValues : function(asString){
6513         //if (this.childForms) {
6514             // copy values from the child forms
6515         //    Roo.each(this.childForms, function (f) {
6516         //        this.setValues(f.getValues());
6517         //    }, this);
6518         //}
6519         
6520         
6521         
6522         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
6523         if(asString === true){
6524             return fs;
6525         }
6526         return Roo.urlDecode(fs);
6527     },
6528     
6529     /**
6530      * Returns the fields in this form as an object with key/value pairs. 
6531      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
6532      * @return {Object}
6533      */
6534     getFieldValues : function(with_hidden)
6535     {
6536         var items = this.getItems();
6537         var ret = {};
6538         items.each(function(f){
6539             if (!f.getName()) {
6540                 return;
6541             }
6542             var v = f.getValue();
6543             if (f.inputType =='radio') {
6544                 if (typeof(ret[f.getName()]) == 'undefined') {
6545                     ret[f.getName()] = ''; // empty..
6546                 }
6547                 
6548                 if (!f.el.dom.checked) {
6549                     return;
6550                     
6551                 }
6552                 v = f.el.dom.value;
6553                 
6554             }
6555             
6556             // not sure if this supported any more..
6557             if ((typeof(v) == 'object') && f.getRawValue) {
6558                 v = f.getRawValue() ; // dates..
6559             }
6560             // combo boxes where name != hiddenName...
6561             if (f.name != f.getName()) {
6562                 ret[f.name] = f.getRawValue();
6563             }
6564             ret[f.getName()] = v;
6565         });
6566         
6567         return ret;
6568     },
6569
6570     /**
6571      * Clears all invalid messages in this form.
6572      * @return {BasicForm} this
6573      */
6574     clearInvalid : function(){
6575         var items = this.getItems();
6576         
6577         items.each(function(f){
6578            f.clearInvalid();
6579         });
6580         
6581         
6582         
6583         return this;
6584     },
6585
6586     /**
6587      * Resets this form.
6588      * @return {BasicForm} this
6589      */
6590     reset : function(){
6591         var items = this.getItems();
6592         items.each(function(f){
6593             f.reset();
6594         });
6595         
6596         Roo.each(this.childForms || [], function (f) {
6597             f.reset();
6598         });
6599        
6600         
6601         return this;
6602     },
6603     getItems : function()
6604     {
6605         var r=new Roo.util.MixedCollection(false, function(o){
6606             return o.id || (o.id = Roo.id());
6607         });
6608         var iter = function(el) {
6609             if (el.inputEl) {
6610                 r.add(el);
6611             }
6612             if (!el.items) {
6613                 return;
6614             }
6615             Roo.each(el.items,function(e) {
6616                 iter(e);
6617             });
6618             
6619             
6620         };
6621         iter(this);
6622         return r;
6623         
6624         
6625         
6626         
6627     }
6628     
6629 });
6630
6631  
6632 /*
6633  * Based on:
6634  * Ext JS Library 1.1.1
6635  * Copyright(c) 2006-2007, Ext JS, LLC.
6636  *
6637  * Originally Released Under LGPL - original licence link has changed is not relivant.
6638  *
6639  * Fork - LGPL
6640  * <script type="text/javascript">
6641  */
6642 /**
6643  * @class Roo.form.VTypes
6644  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
6645  * @singleton
6646  */
6647 Roo.form.VTypes = function(){
6648     // closure these in so they are only created once.
6649     var alpha = /^[a-zA-Z_]+$/;
6650     var alphanum = /^[a-zA-Z0-9_]+$/;
6651     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
6652     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
6653
6654     // All these messages and functions are configurable
6655     return {
6656         /**
6657          * The function used to validate email addresses
6658          * @param {String} value The email address
6659          */
6660         'email' : function(v){
6661             return email.test(v);
6662         },
6663         /**
6664          * The error text to display when the email validation function returns false
6665          * @type String
6666          */
6667         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
6668         /**
6669          * The keystroke filter mask to be applied on email input
6670          * @type RegExp
6671          */
6672         'emailMask' : /[a-z0-9_\.\-@]/i,
6673
6674         /**
6675          * The function used to validate URLs
6676          * @param {String} value The URL
6677          */
6678         'url' : function(v){
6679             return url.test(v);
6680         },
6681         /**
6682          * The error text to display when the url validation function returns false
6683          * @type String
6684          */
6685         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
6686         
6687         /**
6688          * The function used to validate alpha values
6689          * @param {String} value The value
6690          */
6691         'alpha' : function(v){
6692             return alpha.test(v);
6693         },
6694         /**
6695          * The error text to display when the alpha validation function returns false
6696          * @type String
6697          */
6698         'alphaText' : 'This field should only contain letters and _',
6699         /**
6700          * The keystroke filter mask to be applied on alpha input
6701          * @type RegExp
6702          */
6703         'alphaMask' : /[a-z_]/i,
6704
6705         /**
6706          * The function used to validate alphanumeric values
6707          * @param {String} value The value
6708          */
6709         'alphanum' : function(v){
6710             return alphanum.test(v);
6711         },
6712         /**
6713          * The error text to display when the alphanumeric validation function returns false
6714          * @type String
6715          */
6716         'alphanumText' : 'This field should only contain letters, numbers and _',
6717         /**
6718          * The keystroke filter mask to be applied on alphanumeric input
6719          * @type RegExp
6720          */
6721         'alphanumMask' : /[a-z0-9_]/i
6722     };
6723 }();/*
6724  * - LGPL
6725  *
6726  * Input
6727  * 
6728  */
6729
6730 /**
6731  * @class Roo.bootstrap.Input
6732  * @extends Roo.bootstrap.Component
6733  * Bootstrap Input class
6734  * @cfg {Boolean} disabled is it disabled
6735  * @cfg {String} fieldLabel - the label associated
6736  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
6737  * @cfg {String} name name of the input
6738  * @cfg {string} fieldLabel - the label associated
6739  * @cfg {string}  inputType - input / file submit ...
6740  * @cfg {string} placeholder - placeholder to put in text.
6741  * @cfg {string}  before - input group add on before
6742  * @cfg {string} after - input group add on after
6743  * @cfg {string} size - (lg|sm) or leave empty..
6744  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
6745  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
6746  * @cfg {Number} md colspan out of 12 for computer-sized screens
6747  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
6748  * @cfg {string} value default value of the input
6749  * @cfg {Number} labelWidth set the width of label (0-12)
6750  * @cfg {String} labelAlign (top|left)
6751  * @cfg {Boolean} readOnly Specifies that the field should be read-only
6752  * @cfg {String} align (left|center|right) Default left
6753  * 
6754  * 
6755  * @constructor
6756  * Create a new Input
6757  * @param {Object} config The config object
6758  */
6759
6760 Roo.bootstrap.Input = function(config){
6761     Roo.bootstrap.Input.superclass.constructor.call(this, config);
6762    
6763         this.addEvents({
6764             /**
6765              * @event focus
6766              * Fires when this field receives input focus.
6767              * @param {Roo.form.Field} this
6768              */
6769             focus : true,
6770             /**
6771              * @event blur
6772              * Fires when this field loses input focus.
6773              * @param {Roo.form.Field} this
6774              */
6775             blur : true,
6776             /**
6777              * @event specialkey
6778              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
6779              * {@link Roo.EventObject#getKey} to determine which key was pressed.
6780              * @param {Roo.form.Field} this
6781              * @param {Roo.EventObject} e The event object
6782              */
6783             specialkey : true,
6784             /**
6785              * @event change
6786              * Fires just before the field blurs if the field value has changed.
6787              * @param {Roo.form.Field} this
6788              * @param {Mixed} newValue The new value
6789              * @param {Mixed} oldValue The original value
6790              */
6791             change : true,
6792             /**
6793              * @event invalid
6794              * Fires after the field has been marked as invalid.
6795              * @param {Roo.form.Field} this
6796              * @param {String} msg The validation message
6797              */
6798             invalid : true,
6799             /**
6800              * @event valid
6801              * Fires after the field has been validated with no errors.
6802              * @param {Roo.form.Field} this
6803              */
6804             valid : true,
6805              /**
6806              * @event keyup
6807              * Fires after the key up
6808              * @param {Roo.form.Field} this
6809              * @param {Roo.EventObject}  e The event Object
6810              */
6811             keyup : true
6812         });
6813 };
6814
6815 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
6816      /**
6817      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
6818       automatic validation (defaults to "keyup").
6819      */
6820     validationEvent : "keyup",
6821      /**
6822      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
6823      */
6824     validateOnBlur : true,
6825     /**
6826      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
6827      */
6828     validationDelay : 250,
6829      /**
6830      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
6831      */
6832     focusClass : "x-form-focus",  // not needed???
6833     
6834        
6835     /**
6836      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
6837      */
6838     invalidClass : "has-error",
6839     
6840     /**
6841      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
6842      */
6843     selectOnFocus : false,
6844     
6845      /**
6846      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
6847      */
6848     maskRe : null,
6849        /**
6850      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
6851      */
6852     vtype : null,
6853     
6854       /**
6855      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
6856      */
6857     disableKeyFilter : false,
6858     
6859        /**
6860      * @cfg {Boolean} disabled True to disable the field (defaults to false).
6861      */
6862     disabled : false,
6863      /**
6864      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
6865      */
6866     allowBlank : true,
6867     /**
6868      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
6869      */
6870     blankText : "This field is required",
6871     
6872      /**
6873      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
6874      */
6875     minLength : 0,
6876     /**
6877      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
6878      */
6879     maxLength : Number.MAX_VALUE,
6880     /**
6881      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
6882      */
6883     minLengthText : "The minimum length for this field is {0}",
6884     /**
6885      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
6886      */
6887     maxLengthText : "The maximum length for this field is {0}",
6888   
6889     
6890     /**
6891      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
6892      * If available, this function will be called only after the basic validators all return true, and will be passed the
6893      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
6894      */
6895     validator : null,
6896     /**
6897      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
6898      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
6899      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
6900      */
6901     regex : null,
6902     /**
6903      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
6904      */
6905     regexText : "",
6906     
6907     
6908     
6909     fieldLabel : '',
6910     inputType : 'text',
6911     
6912     name : false,
6913     placeholder: false,
6914     before : false,
6915     after : false,
6916     size : false,
6917     // private
6918     hasFocus : false,
6919     preventMark: false,
6920     isFormField : true,
6921     value : '',
6922     labelWidth : 2,
6923     labelAlign : false,
6924     readOnly : false,
6925     align : false,
6926     formatedValue : false,
6927     
6928     parentLabelAlign : function()
6929     {
6930         var parent = this;
6931         while (parent.parent()) {
6932             parent = parent.parent();
6933             if (typeof(parent.labelAlign) !='undefined') {
6934                 return parent.labelAlign;
6935             }
6936         }
6937         return 'left';
6938         
6939     },
6940     
6941     getAutoCreate : function(){
6942         
6943         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
6944         
6945         var id = Roo.id();
6946         
6947         var cfg = {};
6948         
6949         if(this.inputType != 'hidden'){
6950             cfg.cls = 'form-group' //input-group
6951         }
6952         
6953         var input =  {
6954             tag: 'input',
6955             id : id,
6956             type : this.inputType,
6957             value : this.value,
6958             cls : 'form-control',
6959             placeholder : this.placeholder || ''
6960             
6961         };
6962         
6963         if(this.align){
6964             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
6965         }
6966         
6967         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
6968             input.maxLength = this.maxLength;
6969         }
6970         
6971         if (this.disabled) {
6972             input.disabled=true;
6973         }
6974         
6975         if (this.readOnly) {
6976             input.readonly=true;
6977         }
6978         
6979         if (this.name) {
6980             input.name = this.name;
6981         }
6982         if (this.size) {
6983             input.cls += ' input-' + this.size;
6984         }
6985         var settings=this;
6986         ['xs','sm','md','lg'].map(function(size){
6987             if (settings[size]) {
6988                 cfg.cls += ' col-' + size + '-' + settings[size];
6989             }
6990         });
6991         
6992         var inputblock = input;
6993         
6994         if (this.before || this.after) {
6995             
6996             inputblock = {
6997                 cls : 'input-group',
6998                 cn :  [] 
6999             };
7000             if (this.before && typeof(this.before) == 'string') {
7001                 
7002                 inputblock.cn.push({
7003                     tag :'span',
7004                     cls : 'roo-input-before input-group-addon',
7005                     html : this.before
7006                 });
7007             }
7008             if (this.before && typeof(this.before) == 'object') {
7009                 this.before = Roo.factory(this.before);
7010                 Roo.log(this.before);
7011                 inputblock.cn.push({
7012                     tag :'span',
7013                     cls : 'roo-input-before input-group-' +
7014                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7015                 });
7016             }
7017             
7018             inputblock.cn.push(input);
7019             
7020             if (this.after && typeof(this.after) == 'string') {
7021                 inputblock.cn.push({
7022                     tag :'span',
7023                     cls : 'roo-input-after input-group-addon',
7024                     html : this.after
7025                 });
7026             }
7027             if (this.after && typeof(this.after) == 'object') {
7028                 this.after = Roo.factory(this.after);
7029                 Roo.log(this.after);
7030                 inputblock.cn.push({
7031                     tag :'span',
7032                     cls : 'roo-input-after input-group-' +
7033                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7034                 });
7035             }
7036         };
7037         
7038         if (align ==='left' && this.fieldLabel.length) {
7039                 Roo.log("left and has label");
7040                 cfg.cn = [
7041                     
7042                     {
7043                         tag: 'label',
7044                         'for' :  id,
7045                         cls : 'control-label col-sm-' + this.labelWidth,
7046                         html : this.fieldLabel
7047                         
7048                     },
7049                     {
7050                         cls : "col-sm-" + (12 - this.labelWidth), 
7051                         cn: [
7052                             inputblock
7053                         ]
7054                     }
7055                     
7056                 ];
7057         } else if ( this.fieldLabel.length) {
7058                 Roo.log(" label");
7059                  cfg.cn = [
7060                    
7061                     {
7062                         tag: 'label',
7063                         //cls : 'input-group-addon',
7064                         html : this.fieldLabel
7065                         
7066                     },
7067                     
7068                     inputblock
7069                     
7070                 ];
7071
7072         } else {
7073             
7074                 Roo.log(" no label && no align");
7075                 cfg.cn = [
7076                     
7077                         inputblock
7078                     
7079                 ];
7080                 
7081                 
7082         };
7083         Roo.log('input-parentType: ' + this.parentType);
7084         
7085         if (this.parentType === 'Navbar' &&  this.parent().bar) {
7086            cfg.cls += ' navbar-form';
7087            Roo.log(cfg);
7088         }
7089         
7090         return cfg;
7091         
7092     },
7093     /**
7094      * return the real input element.
7095      */
7096     inputEl: function ()
7097     {
7098         return this.el.select('input.form-control',true).first();
7099     },
7100     setDisabled : function(v)
7101     {
7102         var i  = this.inputEl().dom;
7103         if (!v) {
7104             i.removeAttribute('disabled');
7105             return;
7106             
7107         }
7108         i.setAttribute('disabled','true');
7109     },
7110     initEvents : function()
7111     {
7112         
7113         this.inputEl().on("keydown" , this.fireKey,  this);
7114         this.inputEl().on("focus", this.onFocus,  this);
7115         this.inputEl().on("blur", this.onBlur,  this);
7116         
7117         this.inputEl().relayEvent('keyup', this);
7118
7119         // reference to original value for reset
7120         this.originalValue = this.getValue();
7121         //Roo.form.TextField.superclass.initEvents.call(this);
7122         if(this.validationEvent == 'keyup'){
7123             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
7124             this.inputEl().on('keyup', this.filterValidation, this);
7125         }
7126         else if(this.validationEvent !== false){
7127             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
7128         }
7129         
7130         if(this.selectOnFocus){
7131             this.on("focus", this.preFocus, this);
7132             
7133         }
7134         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
7135             this.inputEl().on("keypress", this.filterKeys, this);
7136         }
7137        /* if(this.grow){
7138             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
7139             this.el.on("click", this.autoSize,  this);
7140         }
7141         */
7142         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
7143             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
7144         }
7145         
7146         if (typeof(this.before) == 'object') {
7147             this.before.render(this.el.select('.roo-input-before',true).first());
7148         }
7149         if (typeof(this.after) == 'object') {
7150             this.after.render(this.el.select('.roo-input-after',true).first());
7151         }
7152         
7153         
7154     },
7155     filterValidation : function(e){
7156         if(!e.isNavKeyPress()){
7157             this.validationTask.delay(this.validationDelay);
7158         }
7159     },
7160      /**
7161      * Validates the field value
7162      * @return {Boolean} True if the value is valid, else false
7163      */
7164     validate : function(){
7165         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
7166         if(this.disabled || this.validateValue(this.getRawValue())){
7167             this.clearInvalid();
7168             return true;
7169         }
7170         return false;
7171     },
7172     
7173     
7174     /**
7175      * Validates a value according to the field's validation rules and marks the field as invalid
7176      * if the validation fails
7177      * @param {Mixed} value The value to validate
7178      * @return {Boolean} True if the value is valid, else false
7179      */
7180     validateValue : function(value){
7181         if(value.length < 1)  { // if it's blank
7182              if(this.allowBlank){
7183                 this.clearInvalid();
7184                 return true;
7185              }else{
7186                 this.markInvalid(this.blankText);
7187                 return false;
7188              }
7189         }
7190         if(value.length < this.minLength){
7191             this.markInvalid(String.format(this.minLengthText, this.minLength));
7192             return false;
7193         }
7194         if(value.length > this.maxLength){
7195             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
7196             return false;
7197         }
7198         if(this.vtype){
7199             var vt = Roo.form.VTypes;
7200             if(!vt[this.vtype](value, this)){
7201                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
7202                 return false;
7203             }
7204         }
7205         if(typeof this.validator == "function"){
7206             var msg = this.validator(value);
7207             if(msg !== true){
7208                 this.markInvalid(msg);
7209                 return false;
7210             }
7211         }
7212         if(this.regex && !this.regex.test(value)){
7213             this.markInvalid(this.regexText);
7214             return false;
7215         }
7216         return true;
7217     },
7218
7219     
7220     
7221      // private
7222     fireKey : function(e){
7223         //Roo.log('field ' + e.getKey());
7224         if(e.isNavKeyPress()){
7225             this.fireEvent("specialkey", this, e);
7226         }
7227     },
7228     focus : function (selectText){
7229         if(this.rendered){
7230             this.inputEl().focus();
7231             if(selectText === true){
7232                 this.inputEl().dom.select();
7233             }
7234         }
7235         return this;
7236     } ,
7237     
7238     onFocus : function(){
7239         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7240            // this.el.addClass(this.focusClass);
7241         }
7242         if(!this.hasFocus){
7243             this.hasFocus = true;
7244             this.startValue = this.getValue();
7245             this.fireEvent("focus", this);
7246         }
7247     },
7248     
7249     beforeBlur : Roo.emptyFn,
7250
7251     
7252     // private
7253     onBlur : function(){
7254         this.beforeBlur();
7255         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7256             //this.el.removeClass(this.focusClass);
7257         }
7258         this.hasFocus = false;
7259         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
7260             this.validate();
7261         }
7262         var v = this.getValue();
7263         if(String(v) !== String(this.startValue)){
7264             this.fireEvent('change', this, v, this.startValue);
7265         }
7266         this.fireEvent("blur", this);
7267     },
7268     
7269     /**
7270      * Resets the current field value to the originally loaded value and clears any validation messages
7271      */
7272     reset : function(){
7273         this.setValue(this.originalValue);
7274         this.clearInvalid();
7275     },
7276      /**
7277      * Returns the name of the field
7278      * @return {Mixed} name The name field
7279      */
7280     getName: function(){
7281         return this.name;
7282     },
7283      /**
7284      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
7285      * @return {Mixed} value The field value
7286      */
7287     getValue : function(){
7288         
7289         var v = this.inputEl().getValue();
7290         
7291         return v;
7292     },
7293     /**
7294      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
7295      * @return {Mixed} value The field value
7296      */
7297     getRawValue : function(){
7298         var v = this.inputEl().getValue();
7299         
7300         return v;
7301     },
7302     
7303     /**
7304      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
7305      * @param {Mixed} value The value to set
7306      */
7307     setRawValue : function(v){
7308         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7309     },
7310     
7311     selectText : function(start, end){
7312         var v = this.getRawValue();
7313         if(v.length > 0){
7314             start = start === undefined ? 0 : start;
7315             end = end === undefined ? v.length : end;
7316             var d = this.inputEl().dom;
7317             if(d.setSelectionRange){
7318                 d.setSelectionRange(start, end);
7319             }else if(d.createTextRange){
7320                 var range = d.createTextRange();
7321                 range.moveStart("character", start);
7322                 range.moveEnd("character", v.length-end);
7323                 range.select();
7324             }
7325         }
7326     },
7327     
7328     /**
7329      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
7330      * @param {Mixed} value The value to set
7331      */
7332     setValue : function(v){
7333         this.value = v;
7334         if(this.rendered){
7335             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7336             this.validate();
7337         }
7338     },
7339     
7340     /*
7341     processValue : function(value){
7342         if(this.stripCharsRe){
7343             var newValue = value.replace(this.stripCharsRe, '');
7344             if(newValue !== value){
7345                 this.setRawValue(newValue);
7346                 return newValue;
7347             }
7348         }
7349         return value;
7350     },
7351   */
7352     preFocus : function(){
7353         
7354         if(this.selectOnFocus){
7355             this.inputEl().dom.select();
7356         }
7357     },
7358     filterKeys : function(e){
7359         var k = e.getKey();
7360         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
7361             return;
7362         }
7363         var c = e.getCharCode(), cc = String.fromCharCode(c);
7364         if(Roo.isIE && (e.isSpecialKey() || !cc)){
7365             return;
7366         }
7367         if(!this.maskRe.test(cc)){
7368             e.stopEvent();
7369         }
7370     },
7371      /**
7372      * Clear any invalid styles/messages for this field
7373      */
7374     clearInvalid : function(){
7375         
7376         if(!this.el || this.preventMark){ // not rendered
7377             return;
7378         }
7379         this.el.removeClass(this.invalidClass);
7380         /*
7381         switch(this.msgTarget){
7382             case 'qtip':
7383                 this.el.dom.qtip = '';
7384                 break;
7385             case 'title':
7386                 this.el.dom.title = '';
7387                 break;
7388             case 'under':
7389                 if(this.errorEl){
7390                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
7391                 }
7392                 break;
7393             case 'side':
7394                 if(this.errorIcon){
7395                     this.errorIcon.dom.qtip = '';
7396                     this.errorIcon.hide();
7397                     this.un('resize', this.alignErrorIcon, this);
7398                 }
7399                 break;
7400             default:
7401                 var t = Roo.getDom(this.msgTarget);
7402                 t.innerHTML = '';
7403                 t.style.display = 'none';
7404                 break;
7405         }
7406         */
7407         this.fireEvent('valid', this);
7408     },
7409      /**
7410      * Mark this field as invalid
7411      * @param {String} msg The validation message
7412      */
7413     markInvalid : function(msg){
7414         if(!this.el  || this.preventMark){ // not rendered
7415             return;
7416         }
7417         this.el.addClass(this.invalidClass);
7418         /*
7419         msg = msg || this.invalidText;
7420         switch(this.msgTarget){
7421             case 'qtip':
7422                 this.el.dom.qtip = msg;
7423                 this.el.dom.qclass = 'x-form-invalid-tip';
7424                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
7425                     Roo.QuickTips.enable();
7426                 }
7427                 break;
7428             case 'title':
7429                 this.el.dom.title = msg;
7430                 break;
7431             case 'under':
7432                 if(!this.errorEl){
7433                     var elp = this.el.findParent('.x-form-element', 5, true);
7434                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
7435                     this.errorEl.setWidth(elp.getWidth(true)-20);
7436                 }
7437                 this.errorEl.update(msg);
7438                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
7439                 break;
7440             case 'side':
7441                 if(!this.errorIcon){
7442                     var elp = this.el.findParent('.x-form-element', 5, true);
7443                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
7444                 }
7445                 this.alignErrorIcon();
7446                 this.errorIcon.dom.qtip = msg;
7447                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
7448                 this.errorIcon.show();
7449                 this.on('resize', this.alignErrorIcon, this);
7450                 break;
7451             default:
7452                 var t = Roo.getDom(this.msgTarget);
7453                 t.innerHTML = msg;
7454                 t.style.display = this.msgDisplay;
7455                 break;
7456         }
7457         */
7458         this.fireEvent('invalid', this, msg);
7459     },
7460     // private
7461     SafariOnKeyDown : function(event)
7462     {
7463         // this is a workaround for a password hang bug on chrome/ webkit.
7464         
7465         var isSelectAll = false;
7466         
7467         if(this.inputEl().dom.selectionEnd > 0){
7468             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
7469         }
7470         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
7471             event.preventDefault();
7472             this.setValue('');
7473             return;
7474         }
7475         
7476         if(isSelectAll){ // backspace and delete key
7477             
7478             event.preventDefault();
7479             // this is very hacky as keydown always get's upper case.
7480             //
7481             var cc = String.fromCharCode(event.getCharCode());
7482             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
7483             
7484         }
7485     },
7486     adjustWidth : function(tag, w){
7487         tag = tag.toLowerCase();
7488         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
7489             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
7490                 if(tag == 'input'){
7491                     return w + 2;
7492                 }
7493                 if(tag == 'textarea'){
7494                     return w-2;
7495                 }
7496             }else if(Roo.isOpera){
7497                 if(tag == 'input'){
7498                     return w + 2;
7499                 }
7500                 if(tag == 'textarea'){
7501                     return w-2;
7502                 }
7503             }
7504         }
7505         return w;
7506     }
7507     
7508 });
7509
7510  
7511 /*
7512  * - LGPL
7513  *
7514  * Input
7515  * 
7516  */
7517
7518 /**
7519  * @class Roo.bootstrap.TextArea
7520  * @extends Roo.bootstrap.Input
7521  * Bootstrap TextArea class
7522  * @cfg {Number} cols Specifies the visible width of a text area
7523  * @cfg {Number} rows Specifies the visible number of lines in a text area
7524  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
7525  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
7526  * @cfg {string} html text
7527  * 
7528  * @constructor
7529  * Create a new TextArea
7530  * @param {Object} config The config object
7531  */
7532
7533 Roo.bootstrap.TextArea = function(config){
7534     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
7535    
7536 };
7537
7538 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
7539      
7540     cols : false,
7541     rows : 5,
7542     readOnly : false,
7543     warp : 'soft',
7544     resize : false,
7545     value: false,
7546     html: false,
7547     
7548     getAutoCreate : function(){
7549         
7550         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7551         
7552         var id = Roo.id();
7553         
7554         var cfg = {};
7555         
7556         var input =  {
7557             tag: 'textarea',
7558             id : id,
7559             warp : this.warp,
7560             rows : this.rows,
7561             value : this.value || '',
7562             html: this.html || '',
7563             cls : 'form-control',
7564             placeholder : this.placeholder || '' 
7565             
7566         };
7567         
7568         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7569             input.maxLength = this.maxLength;
7570         }
7571         
7572         if(this.resize){
7573             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
7574         }
7575         
7576         if(this.cols){
7577             input.cols = this.cols;
7578         }
7579         
7580         if (this.readOnly) {
7581             input.readonly = true;
7582         }
7583         
7584         if (this.name) {
7585             input.name = this.name;
7586         }
7587         
7588         if (this.size) {
7589             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
7590         }
7591         
7592         var settings=this;
7593         ['xs','sm','md','lg'].map(function(size){
7594             if (settings[size]) {
7595                 cfg.cls += ' col-' + size + '-' + settings[size];
7596             }
7597         });
7598         
7599         var inputblock = input;
7600         
7601         if (this.before || this.after) {
7602             
7603             inputblock = {
7604                 cls : 'input-group',
7605                 cn :  [] 
7606             };
7607             if (this.before) {
7608                 inputblock.cn.push({
7609                     tag :'span',
7610                     cls : 'input-group-addon',
7611                     html : this.before
7612                 });
7613             }
7614             inputblock.cn.push(input);
7615             if (this.after) {
7616                 inputblock.cn.push({
7617                     tag :'span',
7618                     cls : 'input-group-addon',
7619                     html : this.after
7620                 });
7621             }
7622             
7623         }
7624         
7625         if (align ==='left' && this.fieldLabel.length) {
7626                 Roo.log("left and has label");
7627                 cfg.cn = [
7628                     
7629                     {
7630                         tag: 'label',
7631                         'for' :  id,
7632                         cls : 'control-label col-sm-' + this.labelWidth,
7633                         html : this.fieldLabel
7634                         
7635                     },
7636                     {
7637                         cls : "col-sm-" + (12 - this.labelWidth), 
7638                         cn: [
7639                             inputblock
7640                         ]
7641                     }
7642                     
7643                 ];
7644         } else if ( this.fieldLabel.length) {
7645                 Roo.log(" label");
7646                  cfg.cn = [
7647                    
7648                     {
7649                         tag: 'label',
7650                         //cls : 'input-group-addon',
7651                         html : this.fieldLabel
7652                         
7653                     },
7654                     
7655                     inputblock
7656                     
7657                 ];
7658
7659         } else {
7660             
7661                    Roo.log(" no label && no align");
7662                 cfg.cn = [
7663                     
7664                         inputblock
7665                     
7666                 ];
7667                 
7668                 
7669         }
7670         
7671         if (this.disabled) {
7672             input.disabled=true;
7673         }
7674         
7675         return cfg;
7676         
7677     },
7678     /**
7679      * return the real textarea element.
7680      */
7681     inputEl: function ()
7682     {
7683         return this.el.select('textarea.form-control',true).first();
7684     }
7685 });
7686
7687  
7688 /*
7689  * - LGPL
7690  *
7691  * trigger field - base class for combo..
7692  * 
7693  */
7694  
7695 /**
7696  * @class Roo.bootstrap.TriggerField
7697  * @extends Roo.bootstrap.Input
7698  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
7699  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
7700  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
7701  * for which you can provide a custom implementation.  For example:
7702  * <pre><code>
7703 var trigger = new Roo.bootstrap.TriggerField();
7704 trigger.onTriggerClick = myTriggerFn;
7705 trigger.applyTo('my-field');
7706 </code></pre>
7707  *
7708  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
7709  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
7710  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
7711  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
7712  * @constructor
7713  * Create a new TriggerField.
7714  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
7715  * to the base TextField)
7716  */
7717 Roo.bootstrap.TriggerField = function(config){
7718     this.mimicing = false;
7719     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
7720 };
7721
7722 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
7723     /**
7724      * @cfg {String} triggerClass A CSS class to apply to the trigger
7725      */
7726      /**
7727      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
7728      */
7729     hideTrigger:false,
7730
7731     /** @cfg {Boolean} grow @hide */
7732     /** @cfg {Number} growMin @hide */
7733     /** @cfg {Number} growMax @hide */
7734
7735     /**
7736      * @hide 
7737      * @method
7738      */
7739     autoSize: Roo.emptyFn,
7740     // private
7741     monitorTab : true,
7742     // private
7743     deferHeight : true,
7744
7745     
7746     actionMode : 'wrap',
7747     
7748     
7749     
7750     getAutoCreate : function(){
7751        
7752         var align = this.labelAlign || this.parentLabelAlign();
7753         
7754         var id = Roo.id();
7755         
7756         var cfg = {
7757             cls: 'form-group' //input-group
7758         };
7759         
7760         
7761         var input =  {
7762             tag: 'input',
7763             id : id,
7764             type : this.inputType,
7765             cls : 'form-control',
7766             autocomplete: 'off',
7767             placeholder : this.placeholder || '' 
7768             
7769         };
7770         if (this.name) {
7771             input.name = this.name;
7772         }
7773         if (this.size) {
7774             input.cls += ' input-' + this.size;
7775         }
7776         
7777         if (this.disabled) {
7778             input.disabled=true;
7779         }
7780         
7781         var inputblock = input;
7782         
7783         if (this.before || this.after) {
7784             
7785             inputblock = {
7786                 cls : 'input-group',
7787                 cn :  [] 
7788             };
7789             if (this.before) {
7790                 inputblock.cn.push({
7791                     tag :'span',
7792                     cls : 'input-group-addon',
7793                     html : this.before
7794                 });
7795             }
7796             inputblock.cn.push(input);
7797             if (this.after) {
7798                 inputblock.cn.push({
7799                     tag :'span',
7800                     cls : 'input-group-addon',
7801                     html : this.after
7802                 });
7803             }
7804             
7805         };
7806         
7807         var box = {
7808             tag: 'div',
7809             cn: [
7810                 {
7811                     tag: 'input',
7812                     type : 'hidden',
7813                     cls: 'form-hidden-field'
7814                 },
7815                 inputblock
7816             ]
7817             
7818         };
7819         
7820         if(this.multiple){
7821             Roo.log('multiple');
7822             
7823             box = {
7824                 tag: 'div',
7825                 cn: [
7826                     {
7827                         tag: 'input',
7828                         type : 'hidden',
7829                         cls: 'form-hidden-field'
7830                     },
7831                     {
7832                         tag: 'ul',
7833                         cls: 'select2-choices',
7834                         cn:[
7835                             {
7836                                 tag: 'li',
7837                                 cls: 'select2-search-field',
7838                                 cn: [
7839
7840                                     inputblock
7841                                 ]
7842                             }
7843                         ]
7844                     }
7845                 ]
7846             }
7847         };
7848         
7849         var combobox = {
7850             cls: 'select2-container input-group',
7851             cn: [
7852                 box
7853 //                {
7854 //                    tag: 'ul',
7855 //                    cls: 'typeahead typeahead-long dropdown-menu',
7856 //                    style: 'display:none'
7857 //                }
7858             ]
7859         };
7860         
7861         if(!this.multiple && this.showToggleBtn){
7862             combobox.cn.push({
7863                 tag :'span',
7864                 cls : 'input-group-addon btn dropdown-toggle',
7865                 cn : [
7866                     {
7867                         tag: 'span',
7868                         cls: 'caret'
7869                     },
7870                     {
7871                         tag: 'span',
7872                         cls: 'combobox-clear',
7873                         cn  : [
7874                             {
7875                                 tag : 'i',
7876                                 cls: 'icon-remove'
7877                             }
7878                         ]
7879                     }
7880                 ]
7881
7882             })
7883         }
7884         
7885         if(this.multiple){
7886             combobox.cls += ' select2-container-multi';
7887         }
7888         
7889         if (align ==='left' && this.fieldLabel.length) {
7890             
7891                 Roo.log("left and has label");
7892                 cfg.cn = [
7893                     
7894                     {
7895                         tag: 'label',
7896                         'for' :  id,
7897                         cls : 'control-label col-sm-' + this.labelWidth,
7898                         html : this.fieldLabel
7899                         
7900                     },
7901                     {
7902                         cls : "col-sm-" + (12 - this.labelWidth), 
7903                         cn: [
7904                             combobox
7905                         ]
7906                     }
7907                     
7908                 ];
7909         } else if ( this.fieldLabel.length) {
7910                 Roo.log(" label");
7911                  cfg.cn = [
7912                    
7913                     {
7914                         tag: 'label',
7915                         //cls : 'input-group-addon',
7916                         html : this.fieldLabel
7917                         
7918                     },
7919                     
7920                     combobox
7921                     
7922                 ];
7923
7924         } else {
7925             
7926                 Roo.log(" no label && no align");
7927                 cfg = combobox
7928                      
7929                 
7930         }
7931          
7932         var settings=this;
7933         ['xs','sm','md','lg'].map(function(size){
7934             if (settings[size]) {
7935                 cfg.cls += ' col-' + size + '-' + settings[size];
7936             }
7937         });
7938         
7939         return cfg;
7940         
7941     },
7942     
7943     
7944     
7945     // private
7946     onResize : function(w, h){
7947 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
7948 //        if(typeof w == 'number'){
7949 //            var x = w - this.trigger.getWidth();
7950 //            this.inputEl().setWidth(this.adjustWidth('input', x));
7951 //            this.trigger.setStyle('left', x+'px');
7952 //        }
7953     },
7954
7955     // private
7956     adjustSize : Roo.BoxComponent.prototype.adjustSize,
7957
7958     // private
7959     getResizeEl : function(){
7960         return this.inputEl();
7961     },
7962
7963     // private
7964     getPositionEl : function(){
7965         return this.inputEl();
7966     },
7967
7968     // private
7969     alignErrorIcon : function(){
7970         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
7971     },
7972
7973     // private
7974     initEvents : function(){
7975         
7976         this.createList();
7977         
7978         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
7979         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
7980         if(!this.multiple && this.showToggleBtn){
7981             this.trigger = this.el.select('span.dropdown-toggle',true).first();
7982             if(this.hideTrigger){
7983                 this.trigger.setDisplayed(false);
7984             }
7985             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
7986         }
7987         
7988         if(this.multiple){
7989             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
7990         }
7991         
7992         //this.trigger.addClassOnOver('x-form-trigger-over');
7993         //this.trigger.addClassOnClick('x-form-trigger-click');
7994         
7995         //if(!this.width){
7996         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
7997         //}
7998     },
7999     
8000     createList : function()
8001     {
8002         this.list = Roo.get(document.body).createChild({
8003             tag: 'ul',
8004             cls: 'typeahead typeahead-long dropdown-menu',
8005             style: 'display:none'
8006         });
8007         
8008         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
8009         
8010     },
8011
8012     // private
8013     initTrigger : function(){
8014        
8015     },
8016
8017     // private
8018     onDestroy : function(){
8019         if(this.trigger){
8020             this.trigger.removeAllListeners();
8021           //  this.trigger.remove();
8022         }
8023         //if(this.wrap){
8024         //    this.wrap.remove();
8025         //}
8026         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
8027     },
8028
8029     // private
8030     onFocus : function(){
8031         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
8032         /*
8033         if(!this.mimicing){
8034             this.wrap.addClass('x-trigger-wrap-focus');
8035             this.mimicing = true;
8036             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
8037             if(this.monitorTab){
8038                 this.el.on("keydown", this.checkTab, this);
8039             }
8040         }
8041         */
8042     },
8043
8044     // private
8045     checkTab : function(e){
8046         if(e.getKey() == e.TAB){
8047             this.triggerBlur();
8048         }
8049     },
8050
8051     // private
8052     onBlur : function(){
8053         // do nothing
8054     },
8055
8056     // private
8057     mimicBlur : function(e, t){
8058         /*
8059         if(!this.wrap.contains(t) && this.validateBlur()){
8060             this.triggerBlur();
8061         }
8062         */
8063     },
8064
8065     // private
8066     triggerBlur : function(){
8067         this.mimicing = false;
8068         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
8069         if(this.monitorTab){
8070             this.el.un("keydown", this.checkTab, this);
8071         }
8072         //this.wrap.removeClass('x-trigger-wrap-focus');
8073         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
8074     },
8075
8076     // private
8077     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
8078     validateBlur : function(e, t){
8079         return true;
8080     },
8081
8082     // private
8083     onDisable : function(){
8084         this.inputEl().dom.disabled = true;
8085         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
8086         //if(this.wrap){
8087         //    this.wrap.addClass('x-item-disabled');
8088         //}
8089     },
8090
8091     // private
8092     onEnable : function(){
8093         this.inputEl().dom.disabled = false;
8094         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
8095         //if(this.wrap){
8096         //    this.el.removeClass('x-item-disabled');
8097         //}
8098     },
8099
8100     // private
8101     onShow : function(){
8102         var ae = this.getActionEl();
8103         
8104         if(ae){
8105             ae.dom.style.display = '';
8106             ae.dom.style.visibility = 'visible';
8107         }
8108     },
8109
8110     // private
8111     
8112     onHide : function(){
8113         var ae = this.getActionEl();
8114         ae.dom.style.display = 'none';
8115     },
8116
8117     /**
8118      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
8119      * by an implementing function.
8120      * @method
8121      * @param {EventObject} e
8122      */
8123     onTriggerClick : Roo.emptyFn
8124 });
8125  /*
8126  * Based on:
8127  * Ext JS Library 1.1.1
8128  * Copyright(c) 2006-2007, Ext JS, LLC.
8129  *
8130  * Originally Released Under LGPL - original licence link has changed is not relivant.
8131  *
8132  * Fork - LGPL
8133  * <script type="text/javascript">
8134  */
8135
8136
8137 /**
8138  * @class Roo.data.SortTypes
8139  * @singleton
8140  * Defines the default sorting (casting?) comparison functions used when sorting data.
8141  */
8142 Roo.data.SortTypes = {
8143     /**
8144      * Default sort that does nothing
8145      * @param {Mixed} s The value being converted
8146      * @return {Mixed} The comparison value
8147      */
8148     none : function(s){
8149         return s;
8150     },
8151     
8152     /**
8153      * The regular expression used to strip tags
8154      * @type {RegExp}
8155      * @property
8156      */
8157     stripTagsRE : /<\/?[^>]+>/gi,
8158     
8159     /**
8160      * Strips all HTML tags to sort on text only
8161      * @param {Mixed} s The value being converted
8162      * @return {String} The comparison value
8163      */
8164     asText : function(s){
8165         return String(s).replace(this.stripTagsRE, "");
8166     },
8167     
8168     /**
8169      * Strips all HTML tags to sort on text only - Case insensitive
8170      * @param {Mixed} s The value being converted
8171      * @return {String} The comparison value
8172      */
8173     asUCText : function(s){
8174         return String(s).toUpperCase().replace(this.stripTagsRE, "");
8175     },
8176     
8177     /**
8178      * Case insensitive string
8179      * @param {Mixed} s The value being converted
8180      * @return {String} The comparison value
8181      */
8182     asUCString : function(s) {
8183         return String(s).toUpperCase();
8184     },
8185     
8186     /**
8187      * Date sorting
8188      * @param {Mixed} s The value being converted
8189      * @return {Number} The comparison value
8190      */
8191     asDate : function(s) {
8192         if(!s){
8193             return 0;
8194         }
8195         if(s instanceof Date){
8196             return s.getTime();
8197         }
8198         return Date.parse(String(s));
8199     },
8200     
8201     /**
8202      * Float sorting
8203      * @param {Mixed} s The value being converted
8204      * @return {Float} The comparison value
8205      */
8206     asFloat : function(s) {
8207         var val = parseFloat(String(s).replace(/,/g, ""));
8208         if(isNaN(val)) val = 0;
8209         return val;
8210     },
8211     
8212     /**
8213      * Integer sorting
8214      * @param {Mixed} s The value being converted
8215      * @return {Number} The comparison value
8216      */
8217     asInt : function(s) {
8218         var val = parseInt(String(s).replace(/,/g, ""));
8219         if(isNaN(val)) val = 0;
8220         return val;
8221     }
8222 };/*
8223  * Based on:
8224  * Ext JS Library 1.1.1
8225  * Copyright(c) 2006-2007, Ext JS, LLC.
8226  *
8227  * Originally Released Under LGPL - original licence link has changed is not relivant.
8228  *
8229  * Fork - LGPL
8230  * <script type="text/javascript">
8231  */
8232
8233 /**
8234 * @class Roo.data.Record
8235  * Instances of this class encapsulate both record <em>definition</em> information, and record
8236  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
8237  * to access Records cached in an {@link Roo.data.Store} object.<br>
8238  * <p>
8239  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
8240  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
8241  * objects.<br>
8242  * <p>
8243  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
8244  * @constructor
8245  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
8246  * {@link #create}. The parameters are the same.
8247  * @param {Array} data An associative Array of data values keyed by the field name.
8248  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
8249  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
8250  * not specified an integer id is generated.
8251  */
8252 Roo.data.Record = function(data, id){
8253     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
8254     this.data = data;
8255 };
8256
8257 /**
8258  * Generate a constructor for a specific record layout.
8259  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
8260  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
8261  * Each field definition object may contain the following properties: <ul>
8262  * <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,
8263  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
8264  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
8265  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
8266  * is being used, then this is a string containing the javascript expression to reference the data relative to 
8267  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
8268  * to the data item relative to the record element. If the mapping expression is the same as the field name,
8269  * this may be omitted.</p></li>
8270  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
8271  * <ul><li>auto (Default, implies no conversion)</li>
8272  * <li>string</li>
8273  * <li>int</li>
8274  * <li>float</li>
8275  * <li>boolean</li>
8276  * <li>date</li></ul></p></li>
8277  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
8278  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
8279  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
8280  * by the Reader into an object that will be stored in the Record. It is passed the
8281  * following parameters:<ul>
8282  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
8283  * </ul></p></li>
8284  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
8285  * </ul>
8286  * <br>usage:<br><pre><code>
8287 var TopicRecord = Roo.data.Record.create(
8288     {name: 'title', mapping: 'topic_title'},
8289     {name: 'author', mapping: 'username'},
8290     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
8291     {name: 'lastPost', mapping: 'post_time', type: 'date'},
8292     {name: 'lastPoster', mapping: 'user2'},
8293     {name: 'excerpt', mapping: 'post_text'}
8294 );
8295
8296 var myNewRecord = new TopicRecord({
8297     title: 'Do my job please',
8298     author: 'noobie',
8299     totalPosts: 1,
8300     lastPost: new Date(),
8301     lastPoster: 'Animal',
8302     excerpt: 'No way dude!'
8303 });
8304 myStore.add(myNewRecord);
8305 </code></pre>
8306  * @method create
8307  * @static
8308  */
8309 Roo.data.Record.create = function(o){
8310     var f = function(){
8311         f.superclass.constructor.apply(this, arguments);
8312     };
8313     Roo.extend(f, Roo.data.Record);
8314     var p = f.prototype;
8315     p.fields = new Roo.util.MixedCollection(false, function(field){
8316         return field.name;
8317     });
8318     for(var i = 0, len = o.length; i < len; i++){
8319         p.fields.add(new Roo.data.Field(o[i]));
8320     }
8321     f.getField = function(name){
8322         return p.fields.get(name);  
8323     };
8324     return f;
8325 };
8326
8327 Roo.data.Record.AUTO_ID = 1000;
8328 Roo.data.Record.EDIT = 'edit';
8329 Roo.data.Record.REJECT = 'reject';
8330 Roo.data.Record.COMMIT = 'commit';
8331
8332 Roo.data.Record.prototype = {
8333     /**
8334      * Readonly flag - true if this record has been modified.
8335      * @type Boolean
8336      */
8337     dirty : false,
8338     editing : false,
8339     error: null,
8340     modified: null,
8341
8342     // private
8343     join : function(store){
8344         this.store = store;
8345     },
8346
8347     /**
8348      * Set the named field to the specified value.
8349      * @param {String} name The name of the field to set.
8350      * @param {Object} value The value to set the field to.
8351      */
8352     set : function(name, value){
8353         if(this.data[name] == value){
8354             return;
8355         }
8356         this.dirty = true;
8357         if(!this.modified){
8358             this.modified = {};
8359         }
8360         if(typeof this.modified[name] == 'undefined'){
8361             this.modified[name] = this.data[name];
8362         }
8363         this.data[name] = value;
8364         if(!this.editing && this.store){
8365             this.store.afterEdit(this);
8366         }       
8367     },
8368
8369     /**
8370      * Get the value of the named field.
8371      * @param {String} name The name of the field to get the value of.
8372      * @return {Object} The value of the field.
8373      */
8374     get : function(name){
8375         return this.data[name]; 
8376     },
8377
8378     // private
8379     beginEdit : function(){
8380         this.editing = true;
8381         this.modified = {}; 
8382     },
8383
8384     // private
8385     cancelEdit : function(){
8386         this.editing = false;
8387         delete this.modified;
8388     },
8389
8390     // private
8391     endEdit : function(){
8392         this.editing = false;
8393         if(this.dirty && this.store){
8394             this.store.afterEdit(this);
8395         }
8396     },
8397
8398     /**
8399      * Usually called by the {@link Roo.data.Store} which owns the Record.
8400      * Rejects all changes made to the Record since either creation, or the last commit operation.
8401      * Modified fields are reverted to their original values.
8402      * <p>
8403      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8404      * of reject operations.
8405      */
8406     reject : function(){
8407         var m = this.modified;
8408         for(var n in m){
8409             if(typeof m[n] != "function"){
8410                 this.data[n] = m[n];
8411             }
8412         }
8413         this.dirty = false;
8414         delete this.modified;
8415         this.editing = false;
8416         if(this.store){
8417             this.store.afterReject(this);
8418         }
8419     },
8420
8421     /**
8422      * Usually called by the {@link Roo.data.Store} which owns the Record.
8423      * Commits all changes made to the Record since either creation, or the last commit operation.
8424      * <p>
8425      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8426      * of commit operations.
8427      */
8428     commit : function(){
8429         this.dirty = false;
8430         delete this.modified;
8431         this.editing = false;
8432         if(this.store){
8433             this.store.afterCommit(this);
8434         }
8435     },
8436
8437     // private
8438     hasError : function(){
8439         return this.error != null;
8440     },
8441
8442     // private
8443     clearError : function(){
8444         this.error = null;
8445     },
8446
8447     /**
8448      * Creates a copy of this record.
8449      * @param {String} id (optional) A new record id if you don't want to use this record's id
8450      * @return {Record}
8451      */
8452     copy : function(newId) {
8453         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
8454     }
8455 };/*
8456  * Based on:
8457  * Ext JS Library 1.1.1
8458  * Copyright(c) 2006-2007, Ext JS, LLC.
8459  *
8460  * Originally Released Under LGPL - original licence link has changed is not relivant.
8461  *
8462  * Fork - LGPL
8463  * <script type="text/javascript">
8464  */
8465
8466
8467
8468 /**
8469  * @class Roo.data.Store
8470  * @extends Roo.util.Observable
8471  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
8472  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
8473  * <p>
8474  * 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
8475  * has no knowledge of the format of the data returned by the Proxy.<br>
8476  * <p>
8477  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
8478  * instances from the data object. These records are cached and made available through accessor functions.
8479  * @constructor
8480  * Creates a new Store.
8481  * @param {Object} config A config object containing the objects needed for the Store to access data,
8482  * and read the data into Records.
8483  */
8484 Roo.data.Store = function(config){
8485     this.data = new Roo.util.MixedCollection(false);
8486     this.data.getKey = function(o){
8487         return o.id;
8488     };
8489     this.baseParams = {};
8490     // private
8491     this.paramNames = {
8492         "start" : "start",
8493         "limit" : "limit",
8494         "sort" : "sort",
8495         "dir" : "dir",
8496         "multisort" : "_multisort"
8497     };
8498
8499     if(config && config.data){
8500         this.inlineData = config.data;
8501         delete config.data;
8502     }
8503
8504     Roo.apply(this, config);
8505     
8506     if(this.reader){ // reader passed
8507         this.reader = Roo.factory(this.reader, Roo.data);
8508         this.reader.xmodule = this.xmodule || false;
8509         if(!this.recordType){
8510             this.recordType = this.reader.recordType;
8511         }
8512         if(this.reader.onMetaChange){
8513             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
8514         }
8515     }
8516
8517     if(this.recordType){
8518         this.fields = this.recordType.prototype.fields;
8519     }
8520     this.modified = [];
8521
8522     this.addEvents({
8523         /**
8524          * @event datachanged
8525          * Fires when the data cache has changed, and a widget which is using this Store
8526          * as a Record cache should refresh its view.
8527          * @param {Store} this
8528          */
8529         datachanged : true,
8530         /**
8531          * @event metachange
8532          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
8533          * @param {Store} this
8534          * @param {Object} meta The JSON metadata
8535          */
8536         metachange : true,
8537         /**
8538          * @event add
8539          * Fires when Records have been added to the Store
8540          * @param {Store} this
8541          * @param {Roo.data.Record[]} records The array of Records added
8542          * @param {Number} index The index at which the record(s) were added
8543          */
8544         add : true,
8545         /**
8546          * @event remove
8547          * Fires when a Record has been removed from the Store
8548          * @param {Store} this
8549          * @param {Roo.data.Record} record The Record that was removed
8550          * @param {Number} index The index at which the record was removed
8551          */
8552         remove : true,
8553         /**
8554          * @event update
8555          * Fires when a Record has been updated
8556          * @param {Store} this
8557          * @param {Roo.data.Record} record The Record that was updated
8558          * @param {String} operation The update operation being performed.  Value may be one of:
8559          * <pre><code>
8560  Roo.data.Record.EDIT
8561  Roo.data.Record.REJECT
8562  Roo.data.Record.COMMIT
8563          * </code></pre>
8564          */
8565         update : true,
8566         /**
8567          * @event clear
8568          * Fires when the data cache has been cleared.
8569          * @param {Store} this
8570          */
8571         clear : true,
8572         /**
8573          * @event beforeload
8574          * Fires before a request is made for a new data object.  If the beforeload handler returns false
8575          * the load action will be canceled.
8576          * @param {Store} this
8577          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8578          */
8579         beforeload : true,
8580         /**
8581          * @event beforeloadadd
8582          * Fires after a new set of Records has been loaded.
8583          * @param {Store} this
8584          * @param {Roo.data.Record[]} records The Records that were loaded
8585          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8586          */
8587         beforeloadadd : true,
8588         /**
8589          * @event load
8590          * Fires after a new set of Records has been loaded, before they are added to the store.
8591          * @param {Store} this
8592          * @param {Roo.data.Record[]} records The Records that were loaded
8593          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8594          * @params {Object} return from reader
8595          */
8596         load : true,
8597         /**
8598          * @event loadexception
8599          * Fires if an exception occurs in the Proxy during loading.
8600          * Called with the signature of the Proxy's "loadexception" event.
8601          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
8602          * 
8603          * @param {Proxy} 
8604          * @param {Object} return from JsonData.reader() - success, totalRecords, records
8605          * @param {Object} load options 
8606          * @param {Object} jsonData from your request (normally this contains the Exception)
8607          */
8608         loadexception : true
8609     });
8610     
8611     if(this.proxy){
8612         this.proxy = Roo.factory(this.proxy, Roo.data);
8613         this.proxy.xmodule = this.xmodule || false;
8614         this.relayEvents(this.proxy,  ["loadexception"]);
8615     }
8616     this.sortToggle = {};
8617     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
8618
8619     Roo.data.Store.superclass.constructor.call(this);
8620
8621     if(this.inlineData){
8622         this.loadData(this.inlineData);
8623         delete this.inlineData;
8624     }
8625 };
8626
8627 Roo.extend(Roo.data.Store, Roo.util.Observable, {
8628      /**
8629     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
8630     * without a remote query - used by combo/forms at present.
8631     */
8632     
8633     /**
8634     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
8635     */
8636     /**
8637     * @cfg {Array} data Inline data to be loaded when the store is initialized.
8638     */
8639     /**
8640     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
8641     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
8642     */
8643     /**
8644     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
8645     * on any HTTP request
8646     */
8647     /**
8648     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
8649     */
8650     /**
8651     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
8652     */
8653     multiSort: false,
8654     /**
8655     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
8656     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
8657     */
8658     remoteSort : false,
8659
8660     /**
8661     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
8662      * loaded or when a record is removed. (defaults to false).
8663     */
8664     pruneModifiedRecords : false,
8665
8666     // private
8667     lastOptions : null,
8668
8669     /**
8670      * Add Records to the Store and fires the add event.
8671      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8672      */
8673     add : function(records){
8674         records = [].concat(records);
8675         for(var i = 0, len = records.length; i < len; i++){
8676             records[i].join(this);
8677         }
8678         var index = this.data.length;
8679         this.data.addAll(records);
8680         this.fireEvent("add", this, records, index);
8681     },
8682
8683     /**
8684      * Remove a Record from the Store and fires the remove event.
8685      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
8686      */
8687     remove : function(record){
8688         var index = this.data.indexOf(record);
8689         this.data.removeAt(index);
8690         if(this.pruneModifiedRecords){
8691             this.modified.remove(record);
8692         }
8693         this.fireEvent("remove", this, record, index);
8694     },
8695
8696     /**
8697      * Remove all Records from the Store and fires the clear event.
8698      */
8699     removeAll : function(){
8700         this.data.clear();
8701         if(this.pruneModifiedRecords){
8702             this.modified = [];
8703         }
8704         this.fireEvent("clear", this);
8705     },
8706
8707     /**
8708      * Inserts Records to the Store at the given index and fires the add event.
8709      * @param {Number} index The start index at which to insert the passed Records.
8710      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8711      */
8712     insert : function(index, records){
8713         records = [].concat(records);
8714         for(var i = 0, len = records.length; i < len; i++){
8715             this.data.insert(index, records[i]);
8716             records[i].join(this);
8717         }
8718         this.fireEvent("add", this, records, index);
8719     },
8720
8721     /**
8722      * Get the index within the cache of the passed Record.
8723      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
8724      * @return {Number} The index of the passed Record. Returns -1 if not found.
8725      */
8726     indexOf : function(record){
8727         return this.data.indexOf(record);
8728     },
8729
8730     /**
8731      * Get the index within the cache of the Record with the passed id.
8732      * @param {String} id The id of the Record to find.
8733      * @return {Number} The index of the Record. Returns -1 if not found.
8734      */
8735     indexOfId : function(id){
8736         return this.data.indexOfKey(id);
8737     },
8738
8739     /**
8740      * Get the Record with the specified id.
8741      * @param {String} id The id of the Record to find.
8742      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
8743      */
8744     getById : function(id){
8745         return this.data.key(id);
8746     },
8747
8748     /**
8749      * Get the Record at the specified index.
8750      * @param {Number} index The index of the Record to find.
8751      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
8752      */
8753     getAt : function(index){
8754         return this.data.itemAt(index);
8755     },
8756
8757     /**
8758      * Returns a range of Records between specified indices.
8759      * @param {Number} startIndex (optional) The starting index (defaults to 0)
8760      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
8761      * @return {Roo.data.Record[]} An array of Records
8762      */
8763     getRange : function(start, end){
8764         return this.data.getRange(start, end);
8765     },
8766
8767     // private
8768     storeOptions : function(o){
8769         o = Roo.apply({}, o);
8770         delete o.callback;
8771         delete o.scope;
8772         this.lastOptions = o;
8773     },
8774
8775     /**
8776      * Loads the Record cache from the configured Proxy using the configured Reader.
8777      * <p>
8778      * If using remote paging, then the first load call must specify the <em>start</em>
8779      * and <em>limit</em> properties in the options.params property to establish the initial
8780      * position within the dataset, and the number of Records to cache on each read from the Proxy.
8781      * <p>
8782      * <strong>It is important to note that for remote data sources, loading is asynchronous,
8783      * and this call will return before the new data has been loaded. Perform any post-processing
8784      * in a callback function, or in a "load" event handler.</strong>
8785      * <p>
8786      * @param {Object} options An object containing properties which control loading options:<ul>
8787      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
8788      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
8789      * passed the following arguments:<ul>
8790      * <li>r : Roo.data.Record[]</li>
8791      * <li>options: Options object from the load call</li>
8792      * <li>success: Boolean success indicator</li></ul></li>
8793      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
8794      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
8795      * </ul>
8796      */
8797     load : function(options){
8798         options = options || {};
8799         if(this.fireEvent("beforeload", this, options) !== false){
8800             this.storeOptions(options);
8801             var p = Roo.apply(options.params || {}, this.baseParams);
8802             // if meta was not loaded from remote source.. try requesting it.
8803             if (!this.reader.metaFromRemote) {
8804                 p._requestMeta = 1;
8805             }
8806             if(this.sortInfo && this.remoteSort){
8807                 var pn = this.paramNames;
8808                 p[pn["sort"]] = this.sortInfo.field;
8809                 p[pn["dir"]] = this.sortInfo.direction;
8810             }
8811             if (this.multiSort) {
8812                 var pn = this.paramNames;
8813                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
8814             }
8815             
8816             this.proxy.load(p, this.reader, this.loadRecords, this, options);
8817         }
8818     },
8819
8820     /**
8821      * Reloads the Record cache from the configured Proxy using the configured Reader and
8822      * the options from the last load operation performed.
8823      * @param {Object} options (optional) An object containing properties which may override the options
8824      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
8825      * the most recently used options are reused).
8826      */
8827     reload : function(options){
8828         this.load(Roo.applyIf(options||{}, this.lastOptions));
8829     },
8830
8831     // private
8832     // Called as a callback by the Reader during a load operation.
8833     loadRecords : function(o, options, success){
8834         if(!o || success === false){
8835             if(success !== false){
8836                 this.fireEvent("load", this, [], options, o);
8837             }
8838             if(options.callback){
8839                 options.callback.call(options.scope || this, [], options, false);
8840             }
8841             return;
8842         }
8843         // if data returned failure - throw an exception.
8844         if (o.success === false) {
8845             // show a message if no listener is registered.
8846             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
8847                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
8848             }
8849             // loadmask wil be hooked into this..
8850             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
8851             return;
8852         }
8853         var r = o.records, t = o.totalRecords || r.length;
8854         
8855         this.fireEvent("beforeloadadd", this, r, options, o);
8856         
8857         if(!options || options.add !== true){
8858             if(this.pruneModifiedRecords){
8859                 this.modified = [];
8860             }
8861             for(var i = 0, len = r.length; i < len; i++){
8862                 r[i].join(this);
8863             }
8864             if(this.snapshot){
8865                 this.data = this.snapshot;
8866                 delete this.snapshot;
8867             }
8868             this.data.clear();
8869             this.data.addAll(r);
8870             this.totalLength = t;
8871             this.applySort();
8872             this.fireEvent("datachanged", this);
8873         }else{
8874             this.totalLength = Math.max(t, this.data.length+r.length);
8875             this.add(r);
8876         }
8877         this.fireEvent("load", this, r, options, o);
8878         if(options.callback){
8879             options.callback.call(options.scope || this, r, options, true);
8880         }
8881     },
8882
8883
8884     /**
8885      * Loads data from a passed data block. A Reader which understands the format of the data
8886      * must have been configured in the constructor.
8887      * @param {Object} data The data block from which to read the Records.  The format of the data expected
8888      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
8889      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
8890      */
8891     loadData : function(o, append){
8892         var r = this.reader.readRecords(o);
8893         this.loadRecords(r, {add: append}, true);
8894     },
8895
8896     /**
8897      * Gets the number of cached records.
8898      * <p>
8899      * <em>If using paging, this may not be the total size of the dataset. If the data object
8900      * used by the Reader contains the dataset size, then the getTotalCount() function returns
8901      * the data set size</em>
8902      */
8903     getCount : function(){
8904         return this.data.length || 0;
8905     },
8906
8907     /**
8908      * Gets the total number of records in the dataset as returned by the server.
8909      * <p>
8910      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
8911      * the dataset size</em>
8912      */
8913     getTotalCount : function(){
8914         return this.totalLength || 0;
8915     },
8916
8917     /**
8918      * Returns the sort state of the Store as an object with two properties:
8919      * <pre><code>
8920  field {String} The name of the field by which the Records are sorted
8921  direction {String} The sort order, "ASC" or "DESC"
8922      * </code></pre>
8923      */
8924     getSortState : function(){
8925         return this.sortInfo;
8926     },
8927
8928     // private
8929     applySort : function(){
8930         if(this.sortInfo && !this.remoteSort){
8931             var s = this.sortInfo, f = s.field;
8932             var st = this.fields.get(f).sortType;
8933             var fn = function(r1, r2){
8934                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
8935                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
8936             };
8937             this.data.sort(s.direction, fn);
8938             if(this.snapshot && this.snapshot != this.data){
8939                 this.snapshot.sort(s.direction, fn);
8940             }
8941         }
8942     },
8943
8944     /**
8945      * Sets the default sort column and order to be used by the next load operation.
8946      * @param {String} fieldName The name of the field to sort by.
8947      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
8948      */
8949     setDefaultSort : function(field, dir){
8950         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
8951     },
8952
8953     /**
8954      * Sort the Records.
8955      * If remote sorting is used, the sort is performed on the server, and the cache is
8956      * reloaded. If local sorting is used, the cache is sorted internally.
8957      * @param {String} fieldName The name of the field to sort by.
8958      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
8959      */
8960     sort : function(fieldName, dir){
8961         var f = this.fields.get(fieldName);
8962         if(!dir){
8963             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
8964             
8965             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
8966                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
8967             }else{
8968                 dir = f.sortDir;
8969             }
8970         }
8971         this.sortToggle[f.name] = dir;
8972         this.sortInfo = {field: f.name, direction: dir};
8973         if(!this.remoteSort){
8974             this.applySort();
8975             this.fireEvent("datachanged", this);
8976         }else{
8977             this.load(this.lastOptions);
8978         }
8979     },
8980
8981     /**
8982      * Calls the specified function for each of the Records in the cache.
8983      * @param {Function} fn The function to call. The Record is passed as the first parameter.
8984      * Returning <em>false</em> aborts and exits the iteration.
8985      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
8986      */
8987     each : function(fn, scope){
8988         this.data.each(fn, scope);
8989     },
8990
8991     /**
8992      * Gets all records modified since the last commit.  Modified records are persisted across load operations
8993      * (e.g., during paging).
8994      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
8995      */
8996     getModifiedRecords : function(){
8997         return this.modified;
8998     },
8999
9000     // private
9001     createFilterFn : function(property, value, anyMatch){
9002         if(!value.exec){ // not a regex
9003             value = String(value);
9004             if(value.length == 0){
9005                 return false;
9006             }
9007             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
9008         }
9009         return function(r){
9010             return value.test(r.data[property]);
9011         };
9012     },
9013
9014     /**
9015      * Sums the value of <i>property</i> for each record between start and end and returns the result.
9016      * @param {String} property A field on your records
9017      * @param {Number} start The record index to start at (defaults to 0)
9018      * @param {Number} end The last record index to include (defaults to length - 1)
9019      * @return {Number} The sum
9020      */
9021     sum : function(property, start, end){
9022         var rs = this.data.items, v = 0;
9023         start = start || 0;
9024         end = (end || end === 0) ? end : rs.length-1;
9025
9026         for(var i = start; i <= end; i++){
9027             v += (rs[i].data[property] || 0);
9028         }
9029         return v;
9030     },
9031
9032     /**
9033      * Filter the records by a specified property.
9034      * @param {String} field A field on your records
9035      * @param {String/RegExp} value Either a string that the field
9036      * should start with or a RegExp to test against the field
9037      * @param {Boolean} anyMatch True to match any part not just the beginning
9038      */
9039     filter : function(property, value, anyMatch){
9040         var fn = this.createFilterFn(property, value, anyMatch);
9041         return fn ? this.filterBy(fn) : this.clearFilter();
9042     },
9043
9044     /**
9045      * Filter by a function. The specified function will be called with each
9046      * record in this data source. If the function returns true the record is included,
9047      * otherwise it is filtered.
9048      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9049      * @param {Object} scope (optional) The scope of the function (defaults to this)
9050      */
9051     filterBy : function(fn, scope){
9052         this.snapshot = this.snapshot || this.data;
9053         this.data = this.queryBy(fn, scope||this);
9054         this.fireEvent("datachanged", this);
9055     },
9056
9057     /**
9058      * Query the records by a specified property.
9059      * @param {String} field A field on your records
9060      * @param {String/RegExp} value Either a string that the field
9061      * should start with or a RegExp to test against the field
9062      * @param {Boolean} anyMatch True to match any part not just the beginning
9063      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9064      */
9065     query : function(property, value, anyMatch){
9066         var fn = this.createFilterFn(property, value, anyMatch);
9067         return fn ? this.queryBy(fn) : this.data.clone();
9068     },
9069
9070     /**
9071      * Query by a function. The specified function will be called with each
9072      * record in this data source. If the function returns true the record is included
9073      * in the results.
9074      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9075      * @param {Object} scope (optional) The scope of the function (defaults to this)
9076       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9077      **/
9078     queryBy : function(fn, scope){
9079         var data = this.snapshot || this.data;
9080         return data.filterBy(fn, scope||this);
9081     },
9082
9083     /**
9084      * Collects unique values for a particular dataIndex from this store.
9085      * @param {String} dataIndex The property to collect
9086      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
9087      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
9088      * @return {Array} An array of the unique values
9089      **/
9090     collect : function(dataIndex, allowNull, bypassFilter){
9091         var d = (bypassFilter === true && this.snapshot) ?
9092                 this.snapshot.items : this.data.items;
9093         var v, sv, r = [], l = {};
9094         for(var i = 0, len = d.length; i < len; i++){
9095             v = d[i].data[dataIndex];
9096             sv = String(v);
9097             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
9098                 l[sv] = true;
9099                 r[r.length] = v;
9100             }
9101         }
9102         return r;
9103     },
9104
9105     /**
9106      * Revert to a view of the Record cache with no filtering applied.
9107      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
9108      */
9109     clearFilter : function(suppressEvent){
9110         if(this.snapshot && this.snapshot != this.data){
9111             this.data = this.snapshot;
9112             delete this.snapshot;
9113             if(suppressEvent !== true){
9114                 this.fireEvent("datachanged", this);
9115             }
9116         }
9117     },
9118
9119     // private
9120     afterEdit : function(record){
9121         if(this.modified.indexOf(record) == -1){
9122             this.modified.push(record);
9123         }
9124         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
9125     },
9126     
9127     // private
9128     afterReject : function(record){
9129         this.modified.remove(record);
9130         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
9131     },
9132
9133     // private
9134     afterCommit : function(record){
9135         this.modified.remove(record);
9136         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
9137     },
9138
9139     /**
9140      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
9141      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
9142      */
9143     commitChanges : function(){
9144         var m = this.modified.slice(0);
9145         this.modified = [];
9146         for(var i = 0, len = m.length; i < len; i++){
9147             m[i].commit();
9148         }
9149     },
9150
9151     /**
9152      * Cancel outstanding changes on all changed records.
9153      */
9154     rejectChanges : function(){
9155         var m = this.modified.slice(0);
9156         this.modified = [];
9157         for(var i = 0, len = m.length; i < len; i++){
9158             m[i].reject();
9159         }
9160     },
9161
9162     onMetaChange : function(meta, rtype, o){
9163         this.recordType = rtype;
9164         this.fields = rtype.prototype.fields;
9165         delete this.snapshot;
9166         this.sortInfo = meta.sortInfo || this.sortInfo;
9167         this.modified = [];
9168         this.fireEvent('metachange', this, this.reader.meta);
9169     },
9170     
9171     moveIndex : function(data, type)
9172     {
9173         var index = this.indexOf(data);
9174         
9175         var newIndex = index + type;
9176         
9177         this.remove(data);
9178         
9179         this.insert(newIndex, data);
9180         
9181     }
9182 });/*
9183  * Based on:
9184  * Ext JS Library 1.1.1
9185  * Copyright(c) 2006-2007, Ext JS, LLC.
9186  *
9187  * Originally Released Under LGPL - original licence link has changed is not relivant.
9188  *
9189  * Fork - LGPL
9190  * <script type="text/javascript">
9191  */
9192
9193 /**
9194  * @class Roo.data.SimpleStore
9195  * @extends Roo.data.Store
9196  * Small helper class to make creating Stores from Array data easier.
9197  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
9198  * @cfg {Array} fields An array of field definition objects, or field name strings.
9199  * @cfg {Array} data The multi-dimensional array of data
9200  * @constructor
9201  * @param {Object} config
9202  */
9203 Roo.data.SimpleStore = function(config){
9204     Roo.data.SimpleStore.superclass.constructor.call(this, {
9205         isLocal : true,
9206         reader: new Roo.data.ArrayReader({
9207                 id: config.id
9208             },
9209             Roo.data.Record.create(config.fields)
9210         ),
9211         proxy : new Roo.data.MemoryProxy(config.data)
9212     });
9213     this.load();
9214 };
9215 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
9216  * Based on:
9217  * Ext JS Library 1.1.1
9218  * Copyright(c) 2006-2007, Ext JS, LLC.
9219  *
9220  * Originally Released Under LGPL - original licence link has changed is not relivant.
9221  *
9222  * Fork - LGPL
9223  * <script type="text/javascript">
9224  */
9225
9226 /**
9227 /**
9228  * @extends Roo.data.Store
9229  * @class Roo.data.JsonStore
9230  * Small helper class to make creating Stores for JSON data easier. <br/>
9231 <pre><code>
9232 var store = new Roo.data.JsonStore({
9233     url: 'get-images.php',
9234     root: 'images',
9235     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
9236 });
9237 </code></pre>
9238  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
9239  * JsonReader and HttpProxy (unless inline data is provided).</b>
9240  * @cfg {Array} fields An array of field definition objects, or field name strings.
9241  * @constructor
9242  * @param {Object} config
9243  */
9244 Roo.data.JsonStore = function(c){
9245     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
9246         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
9247         reader: new Roo.data.JsonReader(c, c.fields)
9248     }));
9249 };
9250 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
9251  * Based on:
9252  * Ext JS Library 1.1.1
9253  * Copyright(c) 2006-2007, Ext JS, LLC.
9254  *
9255  * Originally Released Under LGPL - original licence link has changed is not relivant.
9256  *
9257  * Fork - LGPL
9258  * <script type="text/javascript">
9259  */
9260
9261  
9262 Roo.data.Field = function(config){
9263     if(typeof config == "string"){
9264         config = {name: config};
9265     }
9266     Roo.apply(this, config);
9267     
9268     if(!this.type){
9269         this.type = "auto";
9270     }
9271     
9272     var st = Roo.data.SortTypes;
9273     // named sortTypes are supported, here we look them up
9274     if(typeof this.sortType == "string"){
9275         this.sortType = st[this.sortType];
9276     }
9277     
9278     // set default sortType for strings and dates
9279     if(!this.sortType){
9280         switch(this.type){
9281             case "string":
9282                 this.sortType = st.asUCString;
9283                 break;
9284             case "date":
9285                 this.sortType = st.asDate;
9286                 break;
9287             default:
9288                 this.sortType = st.none;
9289         }
9290     }
9291
9292     // define once
9293     var stripRe = /[\$,%]/g;
9294
9295     // prebuilt conversion function for this field, instead of
9296     // switching every time we're reading a value
9297     if(!this.convert){
9298         var cv, dateFormat = this.dateFormat;
9299         switch(this.type){
9300             case "":
9301             case "auto":
9302             case undefined:
9303                 cv = function(v){ return v; };
9304                 break;
9305             case "string":
9306                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
9307                 break;
9308             case "int":
9309                 cv = function(v){
9310                     return v !== undefined && v !== null && v !== '' ?
9311                            parseInt(String(v).replace(stripRe, ""), 10) : '';
9312                     };
9313                 break;
9314             case "float":
9315                 cv = function(v){
9316                     return v !== undefined && v !== null && v !== '' ?
9317                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
9318                     };
9319                 break;
9320             case "bool":
9321             case "boolean":
9322                 cv = function(v){ return v === true || v === "true" || v == 1; };
9323                 break;
9324             case "date":
9325                 cv = function(v){
9326                     if(!v){
9327                         return '';
9328                     }
9329                     if(v instanceof Date){
9330                         return v;
9331                     }
9332                     if(dateFormat){
9333                         if(dateFormat == "timestamp"){
9334                             return new Date(v*1000);
9335                         }
9336                         return Date.parseDate(v, dateFormat);
9337                     }
9338                     var parsed = Date.parse(v);
9339                     return parsed ? new Date(parsed) : null;
9340                 };
9341              break;
9342             
9343         }
9344         this.convert = cv;
9345     }
9346 };
9347
9348 Roo.data.Field.prototype = {
9349     dateFormat: null,
9350     defaultValue: "",
9351     mapping: null,
9352     sortType : null,
9353     sortDir : "ASC"
9354 };/*
9355  * Based on:
9356  * Ext JS Library 1.1.1
9357  * Copyright(c) 2006-2007, Ext JS, LLC.
9358  *
9359  * Originally Released Under LGPL - original licence link has changed is not relivant.
9360  *
9361  * Fork - LGPL
9362  * <script type="text/javascript">
9363  */
9364  
9365 // Base class for reading structured data from a data source.  This class is intended to be
9366 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
9367
9368 /**
9369  * @class Roo.data.DataReader
9370  * Base class for reading structured data from a data source.  This class is intended to be
9371  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
9372  */
9373
9374 Roo.data.DataReader = function(meta, recordType){
9375     
9376     this.meta = meta;
9377     
9378     this.recordType = recordType instanceof Array ? 
9379         Roo.data.Record.create(recordType) : recordType;
9380 };
9381
9382 Roo.data.DataReader.prototype = {
9383      /**
9384      * Create an empty record
9385      * @param {Object} data (optional) - overlay some values
9386      * @return {Roo.data.Record} record created.
9387      */
9388     newRow :  function(d) {
9389         var da =  {};
9390         this.recordType.prototype.fields.each(function(c) {
9391             switch( c.type) {
9392                 case 'int' : da[c.name] = 0; break;
9393                 case 'date' : da[c.name] = new Date(); break;
9394                 case 'float' : da[c.name] = 0.0; break;
9395                 case 'boolean' : da[c.name] = false; break;
9396                 default : da[c.name] = ""; break;
9397             }
9398             
9399         });
9400         return new this.recordType(Roo.apply(da, d));
9401     }
9402     
9403 };/*
9404  * Based on:
9405  * Ext JS Library 1.1.1
9406  * Copyright(c) 2006-2007, Ext JS, LLC.
9407  *
9408  * Originally Released Under LGPL - original licence link has changed is not relivant.
9409  *
9410  * Fork - LGPL
9411  * <script type="text/javascript">
9412  */
9413
9414 /**
9415  * @class Roo.data.DataProxy
9416  * @extends Roo.data.Observable
9417  * This class is an abstract base class for implementations which provide retrieval of
9418  * unformatted data objects.<br>
9419  * <p>
9420  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
9421  * (of the appropriate type which knows how to parse the data object) to provide a block of
9422  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
9423  * <p>
9424  * Custom implementations must implement the load method as described in
9425  * {@link Roo.data.HttpProxy#load}.
9426  */
9427 Roo.data.DataProxy = function(){
9428     this.addEvents({
9429         /**
9430          * @event beforeload
9431          * Fires before a network request is made to retrieve a data object.
9432          * @param {Object} This DataProxy object.
9433          * @param {Object} params The params parameter to the load function.
9434          */
9435         beforeload : true,
9436         /**
9437          * @event load
9438          * Fires before the load method's callback is called.
9439          * @param {Object} This DataProxy object.
9440          * @param {Object} o The data object.
9441          * @param {Object} arg The callback argument object passed to the load function.
9442          */
9443         load : true,
9444         /**
9445          * @event loadexception
9446          * Fires if an Exception occurs during data retrieval.
9447          * @param {Object} This DataProxy object.
9448          * @param {Object} o The data object.
9449          * @param {Object} arg The callback argument object passed to the load function.
9450          * @param {Object} e The Exception.
9451          */
9452         loadexception : true
9453     });
9454     Roo.data.DataProxy.superclass.constructor.call(this);
9455 };
9456
9457 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
9458
9459     /**
9460      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
9461      */
9462 /*
9463  * Based on:
9464  * Ext JS Library 1.1.1
9465  * Copyright(c) 2006-2007, Ext JS, LLC.
9466  *
9467  * Originally Released Under LGPL - original licence link has changed is not relivant.
9468  *
9469  * Fork - LGPL
9470  * <script type="text/javascript">
9471  */
9472 /**
9473  * @class Roo.data.MemoryProxy
9474  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
9475  * to the Reader when its load method is called.
9476  * @constructor
9477  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
9478  */
9479 Roo.data.MemoryProxy = function(data){
9480     if (data.data) {
9481         data = data.data;
9482     }
9483     Roo.data.MemoryProxy.superclass.constructor.call(this);
9484     this.data = data;
9485 };
9486
9487 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
9488     /**
9489      * Load data from the requested source (in this case an in-memory
9490      * data object passed to the constructor), read the data object into
9491      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9492      * process that block using the passed callback.
9493      * @param {Object} params This parameter is not used by the MemoryProxy class.
9494      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9495      * object into a block of Roo.data.Records.
9496      * @param {Function} callback The function into which to pass the block of Roo.data.records.
9497      * The function must be passed <ul>
9498      * <li>The Record block object</li>
9499      * <li>The "arg" argument from the load function</li>
9500      * <li>A boolean success indicator</li>
9501      * </ul>
9502      * @param {Object} scope The scope in which to call the callback
9503      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9504      */
9505     load : function(params, reader, callback, scope, arg){
9506         params = params || {};
9507         var result;
9508         try {
9509             result = reader.readRecords(this.data);
9510         }catch(e){
9511             this.fireEvent("loadexception", this, arg, null, e);
9512             callback.call(scope, null, arg, false);
9513             return;
9514         }
9515         callback.call(scope, result, arg, true);
9516     },
9517     
9518     // private
9519     update : function(params, records){
9520         
9521     }
9522 });/*
9523  * Based on:
9524  * Ext JS Library 1.1.1
9525  * Copyright(c) 2006-2007, Ext JS, LLC.
9526  *
9527  * Originally Released Under LGPL - original licence link has changed is not relivant.
9528  *
9529  * Fork - LGPL
9530  * <script type="text/javascript">
9531  */
9532 /**
9533  * @class Roo.data.HttpProxy
9534  * @extends Roo.data.DataProxy
9535  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
9536  * configured to reference a certain URL.<br><br>
9537  * <p>
9538  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
9539  * from which the running page was served.<br><br>
9540  * <p>
9541  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
9542  * <p>
9543  * Be aware that to enable the browser to parse an XML document, the server must set
9544  * the Content-Type header in the HTTP response to "text/xml".
9545  * @constructor
9546  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
9547  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
9548  * will be used to make the request.
9549  */
9550 Roo.data.HttpProxy = function(conn){
9551     Roo.data.HttpProxy.superclass.constructor.call(this);
9552     // is conn a conn config or a real conn?
9553     this.conn = conn;
9554     this.useAjax = !conn || !conn.events;
9555   
9556 };
9557
9558 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
9559     // thse are take from connection...
9560     
9561     /**
9562      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
9563      */
9564     /**
9565      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
9566      * extra parameters to each request made by this object. (defaults to undefined)
9567      */
9568     /**
9569      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
9570      *  to each request made by this object. (defaults to undefined)
9571      */
9572     /**
9573      * @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)
9574      */
9575     /**
9576      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
9577      */
9578      /**
9579      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
9580      * @type Boolean
9581      */
9582   
9583
9584     /**
9585      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
9586      * @type Boolean
9587      */
9588     /**
9589      * Return the {@link Roo.data.Connection} object being used by this Proxy.
9590      * @return {Connection} The Connection object. This object may be used to subscribe to events on
9591      * a finer-grained basis than the DataProxy events.
9592      */
9593     getConnection : function(){
9594         return this.useAjax ? Roo.Ajax : this.conn;
9595     },
9596
9597     /**
9598      * Load data from the configured {@link Roo.data.Connection}, read the data object into
9599      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
9600      * process that block using the passed callback.
9601      * @param {Object} params An object containing properties which are to be used as HTTP parameters
9602      * for the request to the remote server.
9603      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9604      * object into a block of Roo.data.Records.
9605      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9606      * The function must be passed <ul>
9607      * <li>The Record block object</li>
9608      * <li>The "arg" argument from the load function</li>
9609      * <li>A boolean success indicator</li>
9610      * </ul>
9611      * @param {Object} scope The scope in which to call the callback
9612      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9613      */
9614     load : function(params, reader, callback, scope, arg){
9615         if(this.fireEvent("beforeload", this, params) !== false){
9616             var  o = {
9617                 params : params || {},
9618                 request: {
9619                     callback : callback,
9620                     scope : scope,
9621                     arg : arg
9622                 },
9623                 reader: reader,
9624                 callback : this.loadResponse,
9625                 scope: this
9626             };
9627             if(this.useAjax){
9628                 Roo.applyIf(o, this.conn);
9629                 if(this.activeRequest){
9630                     Roo.Ajax.abort(this.activeRequest);
9631                 }
9632                 this.activeRequest = Roo.Ajax.request(o);
9633             }else{
9634                 this.conn.request(o);
9635             }
9636         }else{
9637             callback.call(scope||this, null, arg, false);
9638         }
9639     },
9640
9641     // private
9642     loadResponse : function(o, success, response){
9643         delete this.activeRequest;
9644         if(!success){
9645             this.fireEvent("loadexception", this, o, response);
9646             o.request.callback.call(o.request.scope, null, o.request.arg, false);
9647             return;
9648         }
9649         var result;
9650         try {
9651             result = o.reader.read(response);
9652         }catch(e){
9653             this.fireEvent("loadexception", this, o, response, e);
9654             o.request.callback.call(o.request.scope, null, o.request.arg, false);
9655             return;
9656         }
9657         
9658         this.fireEvent("load", this, o, o.request.arg);
9659         o.request.callback.call(o.request.scope, result, o.request.arg, true);
9660     },
9661
9662     // private
9663     update : function(dataSet){
9664
9665     },
9666
9667     // private
9668     updateResponse : function(dataSet){
9669
9670     }
9671 });/*
9672  * Based on:
9673  * Ext JS Library 1.1.1
9674  * Copyright(c) 2006-2007, Ext JS, LLC.
9675  *
9676  * Originally Released Under LGPL - original licence link has changed is not relivant.
9677  *
9678  * Fork - LGPL
9679  * <script type="text/javascript">
9680  */
9681
9682 /**
9683  * @class Roo.data.ScriptTagProxy
9684  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
9685  * other than the originating domain of the running page.<br><br>
9686  * <p>
9687  * <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
9688  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
9689  * <p>
9690  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
9691  * source code that is used as the source inside a &lt;script> tag.<br><br>
9692  * <p>
9693  * In order for the browser to process the returned data, the server must wrap the data object
9694  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
9695  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
9696  * depending on whether the callback name was passed:
9697  * <p>
9698  * <pre><code>
9699 boolean scriptTag = false;
9700 String cb = request.getParameter("callback");
9701 if (cb != null) {
9702     scriptTag = true;
9703     response.setContentType("text/javascript");
9704 } else {
9705     response.setContentType("application/x-json");
9706 }
9707 Writer out = response.getWriter();
9708 if (scriptTag) {
9709     out.write(cb + "(");
9710 }
9711 out.print(dataBlock.toJsonString());
9712 if (scriptTag) {
9713     out.write(");");
9714 }
9715 </pre></code>
9716  *
9717  * @constructor
9718  * @param {Object} config A configuration object.
9719  */
9720 Roo.data.ScriptTagProxy = function(config){
9721     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
9722     Roo.apply(this, config);
9723     this.head = document.getElementsByTagName("head")[0];
9724 };
9725
9726 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
9727
9728 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
9729     /**
9730      * @cfg {String} url The URL from which to request the data object.
9731      */
9732     /**
9733      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
9734      */
9735     timeout : 30000,
9736     /**
9737      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
9738      * the server the name of the callback function set up by the load call to process the returned data object.
9739      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
9740      * javascript output which calls this named function passing the data object as its only parameter.
9741      */
9742     callbackParam : "callback",
9743     /**
9744      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
9745      * name to the request.
9746      */
9747     nocache : true,
9748
9749     /**
9750      * Load data from the configured URL, read the data object into
9751      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9752      * process that block using the passed callback.
9753      * @param {Object} params An object containing properties which are to be used as HTTP parameters
9754      * for the request to the remote server.
9755      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9756      * object into a block of Roo.data.Records.
9757      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9758      * The function must be passed <ul>
9759      * <li>The Record block object</li>
9760      * <li>The "arg" argument from the load function</li>
9761      * <li>A boolean success indicator</li>
9762      * </ul>
9763      * @param {Object} scope The scope in which to call the callback
9764      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9765      */
9766     load : function(params, reader, callback, scope, arg){
9767         if(this.fireEvent("beforeload", this, params) !== false){
9768
9769             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
9770
9771             var url = this.url;
9772             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
9773             if(this.nocache){
9774                 url += "&_dc=" + (new Date().getTime());
9775             }
9776             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
9777             var trans = {
9778                 id : transId,
9779                 cb : "stcCallback"+transId,
9780                 scriptId : "stcScript"+transId,
9781                 params : params,
9782                 arg : arg,
9783                 url : url,
9784                 callback : callback,
9785                 scope : scope,
9786                 reader : reader
9787             };
9788             var conn = this;
9789
9790             window[trans.cb] = function(o){
9791                 conn.handleResponse(o, trans);
9792             };
9793
9794             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
9795
9796             if(this.autoAbort !== false){
9797                 this.abort();
9798             }
9799
9800             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
9801
9802             var script = document.createElement("script");
9803             script.setAttribute("src", url);
9804             script.setAttribute("type", "text/javascript");
9805             script.setAttribute("id", trans.scriptId);
9806             this.head.appendChild(script);
9807
9808             this.trans = trans;
9809         }else{
9810             callback.call(scope||this, null, arg, false);
9811         }
9812     },
9813
9814     // private
9815     isLoading : function(){
9816         return this.trans ? true : false;
9817     },
9818
9819     /**
9820      * Abort the current server request.
9821      */
9822     abort : function(){
9823         if(this.isLoading()){
9824             this.destroyTrans(this.trans);
9825         }
9826     },
9827
9828     // private
9829     destroyTrans : function(trans, isLoaded){
9830         this.head.removeChild(document.getElementById(trans.scriptId));
9831         clearTimeout(trans.timeoutId);
9832         if(isLoaded){
9833             window[trans.cb] = undefined;
9834             try{
9835                 delete window[trans.cb];
9836             }catch(e){}
9837         }else{
9838             // if hasn't been loaded, wait for load to remove it to prevent script error
9839             window[trans.cb] = function(){
9840                 window[trans.cb] = undefined;
9841                 try{
9842                     delete window[trans.cb];
9843                 }catch(e){}
9844             };
9845         }
9846     },
9847
9848     // private
9849     handleResponse : function(o, trans){
9850         this.trans = false;
9851         this.destroyTrans(trans, true);
9852         var result;
9853         try {
9854             result = trans.reader.readRecords(o);
9855         }catch(e){
9856             this.fireEvent("loadexception", this, o, trans.arg, e);
9857             trans.callback.call(trans.scope||window, null, trans.arg, false);
9858             return;
9859         }
9860         this.fireEvent("load", this, o, trans.arg);
9861         trans.callback.call(trans.scope||window, result, trans.arg, true);
9862     },
9863
9864     // private
9865     handleFailure : function(trans){
9866         this.trans = false;
9867         this.destroyTrans(trans, false);
9868         this.fireEvent("loadexception", this, null, trans.arg);
9869         trans.callback.call(trans.scope||window, null, trans.arg, false);
9870     }
9871 });/*
9872  * Based on:
9873  * Ext JS Library 1.1.1
9874  * Copyright(c) 2006-2007, Ext JS, LLC.
9875  *
9876  * Originally Released Under LGPL - original licence link has changed is not relivant.
9877  *
9878  * Fork - LGPL
9879  * <script type="text/javascript">
9880  */
9881
9882 /**
9883  * @class Roo.data.JsonReader
9884  * @extends Roo.data.DataReader
9885  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
9886  * based on mappings in a provided Roo.data.Record constructor.
9887  * 
9888  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
9889  * in the reply previously. 
9890  * 
9891  * <p>
9892  * Example code:
9893  * <pre><code>
9894 var RecordDef = Roo.data.Record.create([
9895     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
9896     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
9897 ]);
9898 var myReader = new Roo.data.JsonReader({
9899     totalProperty: "results",    // The property which contains the total dataset size (optional)
9900     root: "rows",                // The property which contains an Array of row objects
9901     id: "id"                     // The property within each row object that provides an ID for the record (optional)
9902 }, RecordDef);
9903 </code></pre>
9904  * <p>
9905  * This would consume a JSON file like this:
9906  * <pre><code>
9907 { 'results': 2, 'rows': [
9908     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
9909     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
9910 }
9911 </code></pre>
9912  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
9913  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
9914  * paged from the remote server.
9915  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
9916  * @cfg {String} root name of the property which contains the Array of row objects.
9917  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
9918  * @constructor
9919  * Create a new JsonReader
9920  * @param {Object} meta Metadata configuration options
9921  * @param {Object} recordType Either an Array of field definition objects,
9922  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
9923  */
9924 Roo.data.JsonReader = function(meta, recordType){
9925     
9926     meta = meta || {};
9927     // set some defaults:
9928     Roo.applyIf(meta, {
9929         totalProperty: 'total',
9930         successProperty : 'success',
9931         root : 'data',
9932         id : 'id'
9933     });
9934     
9935     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
9936 };
9937 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
9938     
9939     /**
9940      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
9941      * Used by Store query builder to append _requestMeta to params.
9942      * 
9943      */
9944     metaFromRemote : false,
9945     /**
9946      * This method is only used by a DataProxy which has retrieved data from a remote server.
9947      * @param {Object} response The XHR object which contains the JSON data in its responseText.
9948      * @return {Object} data A data block which is used by an Roo.data.Store object as
9949      * a cache of Roo.data.Records.
9950      */
9951     read : function(response){
9952         var json = response.responseText;
9953        
9954         var o = /* eval:var:o */ eval("("+json+")");
9955         if(!o) {
9956             throw {message: "JsonReader.read: Json object not found"};
9957         }
9958         
9959         if(o.metaData){
9960             
9961             delete this.ef;
9962             this.metaFromRemote = true;
9963             this.meta = o.metaData;
9964             this.recordType = Roo.data.Record.create(o.metaData.fields);
9965             this.onMetaChange(this.meta, this.recordType, o);
9966         }
9967         return this.readRecords(o);
9968     },
9969
9970     // private function a store will implement
9971     onMetaChange : function(meta, recordType, o){
9972
9973     },
9974
9975     /**
9976          * @ignore
9977          */
9978     simpleAccess: function(obj, subsc) {
9979         return obj[subsc];
9980     },
9981
9982         /**
9983          * @ignore
9984          */
9985     getJsonAccessor: function(){
9986         var re = /[\[\.]/;
9987         return function(expr) {
9988             try {
9989                 return(re.test(expr))
9990                     ? new Function("obj", "return obj." + expr)
9991                     : function(obj){
9992                         return obj[expr];
9993                     };
9994             } catch(e){}
9995             return Roo.emptyFn;
9996         };
9997     }(),
9998
9999     /**
10000      * Create a data block containing Roo.data.Records from an XML document.
10001      * @param {Object} o An object which contains an Array of row objects in the property specified
10002      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
10003      * which contains the total size of the dataset.
10004      * @return {Object} data A data block which is used by an Roo.data.Store object as
10005      * a cache of Roo.data.Records.
10006      */
10007     readRecords : function(o){
10008         /**
10009          * After any data loads, the raw JSON data is available for further custom processing.
10010          * @type Object
10011          */
10012         this.o = o;
10013         var s = this.meta, Record = this.recordType,
10014             f = Record.prototype.fields, fi = f.items, fl = f.length;
10015
10016 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
10017         if (!this.ef) {
10018             if(s.totalProperty) {
10019                     this.getTotal = this.getJsonAccessor(s.totalProperty);
10020                 }
10021                 if(s.successProperty) {
10022                     this.getSuccess = this.getJsonAccessor(s.successProperty);
10023                 }
10024                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
10025                 if (s.id) {
10026                         var g = this.getJsonAccessor(s.id);
10027                         this.getId = function(rec) {
10028                                 var r = g(rec);
10029                                 return (r === undefined || r === "") ? null : r;
10030                         };
10031                 } else {
10032                         this.getId = function(){return null;};
10033                 }
10034             this.ef = [];
10035             for(var jj = 0; jj < fl; jj++){
10036                 f = fi[jj];
10037                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
10038                 this.ef[jj] = this.getJsonAccessor(map);
10039             }
10040         }
10041
10042         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
10043         if(s.totalProperty){
10044             var vt = parseInt(this.getTotal(o), 10);
10045             if(!isNaN(vt)){
10046                 totalRecords = vt;
10047             }
10048         }
10049         if(s.successProperty){
10050             var vs = this.getSuccess(o);
10051             if(vs === false || vs === 'false'){
10052                 success = false;
10053             }
10054         }
10055         var records = [];
10056             for(var i = 0; i < c; i++){
10057                     var n = root[i];
10058                 var values = {};
10059                 var id = this.getId(n);
10060                 for(var j = 0; j < fl; j++){
10061                     f = fi[j];
10062                 var v = this.ef[j](n);
10063                 if (!f.convert) {
10064                     Roo.log('missing convert for ' + f.name);
10065                     Roo.log(f);
10066                     continue;
10067                 }
10068                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
10069                 }
10070                 var record = new Record(values, id);
10071                 record.json = n;
10072                 records[i] = record;
10073             }
10074             return {
10075             raw : o,
10076                 success : success,
10077                 records : records,
10078                 totalRecords : totalRecords
10079             };
10080     }
10081 });/*
10082  * Based on:
10083  * Ext JS Library 1.1.1
10084  * Copyright(c) 2006-2007, Ext JS, LLC.
10085  *
10086  * Originally Released Under LGPL - original licence link has changed is not relivant.
10087  *
10088  * Fork - LGPL
10089  * <script type="text/javascript">
10090  */
10091
10092 /**
10093  * @class Roo.data.ArrayReader
10094  * @extends Roo.data.DataReader
10095  * Data reader class to create an Array of Roo.data.Record objects from an Array.
10096  * Each element of that Array represents a row of data fields. The
10097  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
10098  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
10099  * <p>
10100  * Example code:.
10101  * <pre><code>
10102 var RecordDef = Roo.data.Record.create([
10103     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
10104     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
10105 ]);
10106 var myReader = new Roo.data.ArrayReader({
10107     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
10108 }, RecordDef);
10109 </code></pre>
10110  * <p>
10111  * This would consume an Array like this:
10112  * <pre><code>
10113 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
10114   </code></pre>
10115  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
10116  * @constructor
10117  * Create a new JsonReader
10118  * @param {Object} meta Metadata configuration options.
10119  * @param {Object} recordType Either an Array of field definition objects
10120  * as specified to {@link Roo.data.Record#create},
10121  * or an {@link Roo.data.Record} object
10122  * created using {@link Roo.data.Record#create}.
10123  */
10124 Roo.data.ArrayReader = function(meta, recordType){
10125     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
10126 };
10127
10128 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
10129     /**
10130      * Create a data block containing Roo.data.Records from an XML document.
10131      * @param {Object} o An Array of row objects which represents the dataset.
10132      * @return {Object} data A data block which is used by an Roo.data.Store object as
10133      * a cache of Roo.data.Records.
10134      */
10135     readRecords : function(o){
10136         var sid = this.meta ? this.meta.id : null;
10137         var recordType = this.recordType, fields = recordType.prototype.fields;
10138         var records = [];
10139         var root = o;
10140             for(var i = 0; i < root.length; i++){
10141                     var n = root[i];
10142                 var values = {};
10143                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
10144                 for(var j = 0, jlen = fields.length; j < jlen; j++){
10145                 var f = fields.items[j];
10146                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
10147                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
10148                 v = f.convert(v);
10149                 values[f.name] = v;
10150             }
10151                 var record = new recordType(values, id);
10152                 record.json = n;
10153                 records[records.length] = record;
10154             }
10155             return {
10156                 records : records,
10157                 totalRecords : records.length
10158             };
10159     }
10160 });/*
10161  * - LGPL
10162  * * 
10163  */
10164
10165 /**
10166  * @class Roo.bootstrap.ComboBox
10167  * @extends Roo.bootstrap.TriggerField
10168  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
10169  * @cfg {Boolean} append (true|false) default false
10170  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
10171  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
10172  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
10173  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
10174  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
10175  * @constructor
10176  * Create a new ComboBox.
10177  * @param {Object} config Configuration options
10178  */
10179 Roo.bootstrap.ComboBox = function(config){
10180     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
10181     this.addEvents({
10182         /**
10183          * @event expand
10184          * Fires when the dropdown list is expanded
10185              * @param {Roo.bootstrap.ComboBox} combo This combo box
10186              */
10187         'expand' : true,
10188         /**
10189          * @event collapse
10190          * Fires when the dropdown list is collapsed
10191              * @param {Roo.bootstrap.ComboBox} combo This combo box
10192              */
10193         'collapse' : true,
10194         /**
10195          * @event beforeselect
10196          * Fires before a list item is selected. Return false to cancel the selection.
10197              * @param {Roo.bootstrap.ComboBox} combo This combo box
10198              * @param {Roo.data.Record} record The data record returned from the underlying store
10199              * @param {Number} index The index of the selected item in the dropdown list
10200              */
10201         'beforeselect' : true,
10202         /**
10203          * @event select
10204          * Fires when a list item is selected
10205              * @param {Roo.bootstrap.ComboBox} combo This combo box
10206              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
10207              * @param {Number} index The index of the selected item in the dropdown list
10208              */
10209         'select' : true,
10210         /**
10211          * @event beforequery
10212          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
10213          * The event object passed has these properties:
10214              * @param {Roo.bootstrap.ComboBox} combo This combo box
10215              * @param {String} query The query
10216              * @param {Boolean} forceAll true to force "all" query
10217              * @param {Boolean} cancel true to cancel the query
10218              * @param {Object} e The query event object
10219              */
10220         'beforequery': true,
10221          /**
10222          * @event add
10223          * Fires when the 'add' icon is pressed (add a listener to enable add button)
10224              * @param {Roo.bootstrap.ComboBox} combo This combo box
10225              */
10226         'add' : true,
10227         /**
10228          * @event edit
10229          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
10230              * @param {Roo.bootstrap.ComboBox} combo This combo box
10231              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
10232              */
10233         'edit' : true,
10234         /**
10235          * @event remove
10236          * Fires when the remove value from the combobox array
10237              * @param {Roo.bootstrap.ComboBox} combo This combo box
10238              */
10239         'remove' : true
10240         
10241     });
10242     
10243     this.item = [];
10244     this.tickItems = [];
10245     
10246     this.selectedIndex = -1;
10247     if(this.mode == 'local'){
10248         if(config.queryDelay === undefined){
10249             this.queryDelay = 10;
10250         }
10251         if(config.minChars === undefined){
10252             this.minChars = 0;
10253         }
10254     }
10255 };
10256
10257 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
10258      
10259     /**
10260      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
10261      * rendering into an Roo.Editor, defaults to false)
10262      */
10263     /**
10264      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
10265      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
10266      */
10267     /**
10268      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
10269      */
10270     /**
10271      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
10272      * the dropdown list (defaults to undefined, with no header element)
10273      */
10274
10275      /**
10276      * @cfg {String/Roo.Template} tpl The template to use to render the output
10277      */
10278      
10279      /**
10280      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
10281      */
10282     listWidth: undefined,
10283     /**
10284      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
10285      * mode = 'remote' or 'text' if mode = 'local')
10286      */
10287     displayField: undefined,
10288     /**
10289      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
10290      * mode = 'remote' or 'value' if mode = 'local'). 
10291      * Note: use of a valueField requires the user make a selection
10292      * in order for a value to be mapped.
10293      */
10294     valueField: undefined,
10295     
10296     
10297     /**
10298      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
10299      * field's data value (defaults to the underlying DOM element's name)
10300      */
10301     hiddenName: undefined,
10302     /**
10303      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
10304      */
10305     listClass: '',
10306     /**
10307      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
10308      */
10309     selectedClass: 'active',
10310     
10311     /**
10312      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
10313      */
10314     shadow:'sides',
10315     /**
10316      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
10317      * anchor positions (defaults to 'tl-bl')
10318      */
10319     listAlign: 'tl-bl?',
10320     /**
10321      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
10322      */
10323     maxHeight: 300,
10324     /**
10325      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
10326      * query specified by the allQuery config option (defaults to 'query')
10327      */
10328     triggerAction: 'query',
10329     /**
10330      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
10331      * (defaults to 4, does not apply if editable = false)
10332      */
10333     minChars : 4,
10334     /**
10335      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
10336      * delay (typeAheadDelay) if it matches a known value (defaults to false)
10337      */
10338     typeAhead: false,
10339     /**
10340      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
10341      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
10342      */
10343     queryDelay: 500,
10344     /**
10345      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
10346      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
10347      */
10348     pageSize: 0,
10349     /**
10350      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
10351      * when editable = true (defaults to false)
10352      */
10353     selectOnFocus:false,
10354     /**
10355      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
10356      */
10357     queryParam: 'query',
10358     /**
10359      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
10360      * when mode = 'remote' (defaults to 'Loading...')
10361      */
10362     loadingText: 'Loading...',
10363     /**
10364      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
10365      */
10366     resizable: false,
10367     /**
10368      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
10369      */
10370     handleHeight : 8,
10371     /**
10372      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
10373      * traditional select (defaults to true)
10374      */
10375     editable: true,
10376     /**
10377      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
10378      */
10379     allQuery: '',
10380     /**
10381      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
10382      */
10383     mode: 'remote',
10384     /**
10385      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
10386      * listWidth has a higher value)
10387      */
10388     minListWidth : 70,
10389     /**
10390      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
10391      * allow the user to set arbitrary text into the field (defaults to false)
10392      */
10393     forceSelection:false,
10394     /**
10395      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
10396      * if typeAhead = true (defaults to 250)
10397      */
10398     typeAheadDelay : 250,
10399     /**
10400      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
10401      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
10402      */
10403     valueNotFoundText : undefined,
10404     /**
10405      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
10406      */
10407     blockFocus : false,
10408     
10409     /**
10410      * @cfg {Boolean} disableClear Disable showing of clear button.
10411      */
10412     disableClear : false,
10413     /**
10414      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
10415      */
10416     alwaysQuery : false,
10417     
10418     /**
10419      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
10420      */
10421     multiple : false,
10422     
10423     //private
10424     addicon : false,
10425     editicon: false,
10426     
10427     page: 0,
10428     hasQuery: false,
10429     append: false,
10430     loadNext: false,
10431     autoFocus : true,
10432     tickable : false,
10433     btnPosition : 'right',
10434     triggerList : true,
10435     showToggleBtn : true,
10436     // element that contains real text value.. (when hidden is used..)
10437     
10438     getAutoCreate : function()
10439     {
10440         var cfg = false;
10441         
10442         /*
10443          *  Normal ComboBox
10444          */
10445         if(!this.tickable){
10446             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
10447             return cfg;
10448         }
10449         
10450         /*
10451          *  ComboBox with tickable selections
10452          */
10453              
10454         var align = this.labelAlign || this.parentLabelAlign();
10455         
10456         cfg = {
10457             cls : 'form-group roo-combobox-tickable' //input-group
10458         };
10459         
10460         
10461         var buttons = {
10462             tag : 'div',
10463             cls : 'tickable-buttons',
10464             cn : [
10465                 {
10466                     tag : 'button',
10467                     type : 'button',
10468                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
10469                     html : 'Edit'
10470                 },
10471                 {
10472                     tag : 'button',
10473                     type : 'button',
10474                     name : 'ok',
10475                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
10476                     html : 'Done'
10477                 },
10478                 {
10479                     tag : 'button',
10480                     type : 'button',
10481                     name : 'cancel',
10482                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
10483                     html : 'Cancel'
10484                 }
10485             ]
10486         };
10487         
10488         var _this = this;
10489         Roo.each(buttons.cn, function(c){
10490             if (_this.size) {
10491                 c.cls += ' btn-' + _this.size;
10492             }
10493
10494             if (_this.disabled) {
10495                 c.disabled = true;
10496             }
10497         });
10498         
10499         var box = {
10500             tag: 'div',
10501             cn: [
10502                 {
10503                     tag: 'input',
10504                     type : 'hidden',
10505                     cls: 'form-hidden-field'
10506                 },
10507                 {
10508                     tag: 'ul',
10509                     cls: 'select2-choices',
10510                     cn:[
10511                         {
10512                             tag: 'li',
10513                             cls: 'select2-search-field',
10514                             cn: [
10515
10516                                 buttons
10517                             ]
10518                         }
10519                     ]
10520                 }
10521             ]
10522         }
10523         
10524         var combobox = {
10525             cls: 'select2-container input-group select2-container-multi',
10526             cn: [
10527                 box
10528 //                {
10529 //                    tag: 'ul',
10530 //                    cls: 'typeahead typeahead-long dropdown-menu',
10531 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
10532 //                }
10533             ]
10534         };
10535         
10536         if (align ==='left' && this.fieldLabel.length) {
10537             
10538                 Roo.log("left and has label");
10539                 cfg.cn = [
10540                     
10541                     {
10542                         tag: 'label',
10543                         'for' :  id,
10544                         cls : 'control-label col-sm-' + this.labelWidth,
10545                         html : this.fieldLabel
10546                         
10547                     },
10548                     {
10549                         cls : "col-sm-" + (12 - this.labelWidth), 
10550                         cn: [
10551                             combobox
10552                         ]
10553                     }
10554                     
10555                 ];
10556         } else if ( this.fieldLabel.length) {
10557                 Roo.log(" label");
10558                  cfg.cn = [
10559                    
10560                     {
10561                         tag: 'label',
10562                         //cls : 'input-group-addon',
10563                         html : this.fieldLabel
10564                         
10565                     },
10566                     
10567                     combobox
10568                     
10569                 ];
10570
10571         } else {
10572             
10573                 Roo.log(" no label && no align");
10574                 cfg = combobox
10575                      
10576                 
10577         }
10578          
10579         var settings=this;
10580         ['xs','sm','md','lg'].map(function(size){
10581             if (settings[size]) {
10582                 cfg.cls += ' col-' + size + '-' + settings[size];
10583             }
10584         });
10585         
10586         return cfg;
10587         
10588     },
10589     
10590     // private
10591     initEvents: function()
10592     {
10593         
10594         if (!this.store) {
10595             throw "can not find store for combo";
10596         }
10597         this.store = Roo.factory(this.store, Roo.data);
10598         
10599         if(this.tickable){
10600             this.initTickableEvents();
10601             return;
10602         }
10603         
10604         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
10605         
10606         if(this.hiddenName){
10607             
10608             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10609             
10610             this.hiddenField.dom.value =
10611                 this.hiddenValue !== undefined ? this.hiddenValue :
10612                 this.value !== undefined ? this.value : '';
10613
10614             // prevent input submission
10615             this.el.dom.removeAttribute('name');
10616             this.hiddenField.dom.setAttribute('name', this.hiddenName);
10617              
10618              
10619         }
10620         //if(Roo.isGecko){
10621         //    this.el.dom.setAttribute('autocomplete', 'off');
10622         //}
10623         
10624         var cls = 'x-combo-list';
10625         
10626         //this.list = new Roo.Layer({
10627         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
10628         //});
10629         
10630         var _this = this;
10631         
10632         (function(){
10633             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
10634             _this.list.setWidth(lw);
10635         }).defer(100);
10636         
10637         this.list.on('mouseover', this.onViewOver, this);
10638         this.list.on('mousemove', this.onViewMove, this);
10639         
10640         this.list.on('scroll', this.onViewScroll, this);
10641         
10642         /*
10643         this.list.swallowEvent('mousewheel');
10644         this.assetHeight = 0;
10645
10646         if(this.title){
10647             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
10648             this.assetHeight += this.header.getHeight();
10649         }
10650
10651         this.innerList = this.list.createChild({cls:cls+'-inner'});
10652         this.innerList.on('mouseover', this.onViewOver, this);
10653         this.innerList.on('mousemove', this.onViewMove, this);
10654         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
10655         
10656         if(this.allowBlank && !this.pageSize && !this.disableClear){
10657             this.footer = this.list.createChild({cls:cls+'-ft'});
10658             this.pageTb = new Roo.Toolbar(this.footer);
10659            
10660         }
10661         if(this.pageSize){
10662             this.footer = this.list.createChild({cls:cls+'-ft'});
10663             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
10664                     {pageSize: this.pageSize});
10665             
10666         }
10667         
10668         if (this.pageTb && this.allowBlank && !this.disableClear) {
10669             var _this = this;
10670             this.pageTb.add(new Roo.Toolbar.Fill(), {
10671                 cls: 'x-btn-icon x-btn-clear',
10672                 text: '&#160;',
10673                 handler: function()
10674                 {
10675                     _this.collapse();
10676                     _this.clearValue();
10677                     _this.onSelect(false, -1);
10678                 }
10679             });
10680         }
10681         if (this.footer) {
10682             this.assetHeight += this.footer.getHeight();
10683         }
10684         */
10685             
10686         if(!this.tpl){
10687             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
10688         }
10689
10690         this.view = new Roo.View(this.list, this.tpl, {
10691             singleSelect:true, store: this.store, selectedClass: this.selectedClass
10692         });
10693         //this.view.wrapEl.setDisplayed(false);
10694         this.view.on('click', this.onViewClick, this);
10695         
10696         
10697         
10698         this.store.on('beforeload', this.onBeforeLoad, this);
10699         this.store.on('load', this.onLoad, this);
10700         this.store.on('loadexception', this.onLoadException, this);
10701         /*
10702         if(this.resizable){
10703             this.resizer = new Roo.Resizable(this.list,  {
10704                pinned:true, handles:'se'
10705             });
10706             this.resizer.on('resize', function(r, w, h){
10707                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
10708                 this.listWidth = w;
10709                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
10710                 this.restrictHeight();
10711             }, this);
10712             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
10713         }
10714         */
10715         if(!this.editable){
10716             this.editable = true;
10717             this.setEditable(false);
10718         }
10719         
10720         /*
10721         
10722         if (typeof(this.events.add.listeners) != 'undefined') {
10723             
10724             this.addicon = this.wrap.createChild(
10725                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
10726        
10727             this.addicon.on('click', function(e) {
10728                 this.fireEvent('add', this);
10729             }, this);
10730         }
10731         if (typeof(this.events.edit.listeners) != 'undefined') {
10732             
10733             this.editicon = this.wrap.createChild(
10734                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
10735             if (this.addicon) {
10736                 this.editicon.setStyle('margin-left', '40px');
10737             }
10738             this.editicon.on('click', function(e) {
10739                 
10740                 // we fire even  if inothing is selected..
10741                 this.fireEvent('edit', this, this.lastData );
10742                 
10743             }, this);
10744         }
10745         */
10746         
10747         this.keyNav = new Roo.KeyNav(this.inputEl(), {
10748             "up" : function(e){
10749                 this.inKeyMode = true;
10750                 this.selectPrev();
10751             },
10752
10753             "down" : function(e){
10754                 if(!this.isExpanded()){
10755                     this.onTriggerClick();
10756                 }else{
10757                     this.inKeyMode = true;
10758                     this.selectNext();
10759                 }
10760             },
10761
10762             "enter" : function(e){
10763 //                this.onViewClick();
10764                 //return true;
10765                 this.collapse();
10766                 
10767                 if(this.fireEvent("specialkey", this, e)){
10768                     this.onViewClick(false);
10769                 }
10770                 
10771                 return true;
10772             },
10773
10774             "esc" : function(e){
10775                 this.collapse();
10776             },
10777
10778             "tab" : function(e){
10779                 this.collapse();
10780                 
10781                 if(this.fireEvent("specialkey", this, e)){
10782                     this.onViewClick(false);
10783                 }
10784                 
10785                 return true;
10786             },
10787
10788             scope : this,
10789
10790             doRelay : function(foo, bar, hname){
10791                 if(hname == 'down' || this.scope.isExpanded()){
10792                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
10793                 }
10794                 return true;
10795             },
10796
10797             forceKeyDown: true
10798         });
10799         
10800         
10801         this.queryDelay = Math.max(this.queryDelay || 10,
10802                 this.mode == 'local' ? 10 : 250);
10803         
10804         
10805         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
10806         
10807         if(this.typeAhead){
10808             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
10809         }
10810         if(this.editable !== false){
10811             this.inputEl().on("keyup", this.onKeyUp, this);
10812         }
10813         if(this.forceSelection){
10814             this.inputEl().on('blur', this.doForce, this);
10815         }
10816         
10817         if(this.multiple){
10818             this.choices = this.el.select('ul.select2-choices', true).first();
10819             this.searchField = this.el.select('ul li.select2-search-field', true).first();
10820         }
10821     },
10822     
10823     initTickableEvents: function()
10824     {   
10825         this.createList();
10826         
10827         if(this.hiddenName){
10828             
10829             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10830             
10831             this.hiddenField.dom.value =
10832                 this.hiddenValue !== undefined ? this.hiddenValue :
10833                 this.value !== undefined ? this.value : '';
10834
10835             // prevent input submission
10836             this.el.dom.removeAttribute('name');
10837             this.hiddenField.dom.setAttribute('name', this.hiddenName);
10838              
10839              
10840         }
10841         
10842 //        this.list = this.el.select('ul.dropdown-menu',true).first();
10843         
10844         this.choices = this.el.select('ul.select2-choices', true).first();
10845         this.searchField = this.el.select('ul li.select2-search-field', true).first();
10846         if(this.triggerList){
10847             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
10848         }
10849          
10850         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
10851         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
10852         
10853         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
10854         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
10855         
10856         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
10857         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
10858         
10859         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
10860         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
10861         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
10862         
10863         this.okBtn.hide();
10864         this.cancelBtn.hide();
10865         
10866         var _this = this;
10867         
10868         (function(){
10869             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
10870             _this.list.setWidth(lw);
10871         }).defer(100);
10872         
10873         this.list.on('mouseover', this.onViewOver, this);
10874         this.list.on('mousemove', this.onViewMove, this);
10875         
10876         this.list.on('scroll', this.onViewScroll, this);
10877         
10878         if(!this.tpl){
10879             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>';
10880         }
10881
10882         this.view = new Roo.View(this.list, this.tpl, {
10883             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
10884         });
10885         
10886         //this.view.wrapEl.setDisplayed(false);
10887         this.view.on('click', this.onViewClick, this);
10888         
10889         
10890         
10891         this.store.on('beforeload', this.onBeforeLoad, this);
10892         this.store.on('load', this.onLoad, this);
10893         this.store.on('loadexception', this.onLoadException, this);
10894         
10895 //        this.keyNav = new Roo.KeyNav(this.inputEl(), {
10896 //            "up" : function(e){
10897 //                this.inKeyMode = true;
10898 //                this.selectPrev();
10899 //            },
10900 //
10901 //            "down" : function(e){
10902 //                if(!this.isExpanded()){
10903 //                    this.onTriggerClick();
10904 //                }else{
10905 //                    this.inKeyMode = true;
10906 //                    this.selectNext();
10907 //                }
10908 //            },
10909 //
10910 //            "enter" : function(e){
10911 ////                this.onViewClick();
10912 //                //return true;
10913 //                this.collapse();
10914 //                
10915 //                if(this.fireEvent("specialkey", this, e)){
10916 //                    this.onViewClick(false);
10917 //                }
10918 //                
10919 //                return true;
10920 //            },
10921 //
10922 //            "esc" : function(e){
10923 //                this.collapse();
10924 //            },
10925 //
10926 //            "tab" : function(e){
10927 //                this.collapse();
10928 //                
10929 //                if(this.fireEvent("specialkey", this, e)){
10930 //                    this.onViewClick(false);
10931 //                }
10932 //                
10933 //                return true;
10934 //            },
10935 //
10936 //            scope : this,
10937 //
10938 //            doRelay : function(foo, bar, hname){
10939 //                if(hname == 'down' || this.scope.isExpanded()){
10940 //                   return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
10941 //                }
10942 //                return true;
10943 //            },
10944 //
10945 //            forceKeyDown: true
10946 //        });
10947         
10948         
10949         this.queryDelay = Math.max(this.queryDelay || 10,
10950                 this.mode == 'local' ? 10 : 250);
10951         
10952         
10953         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
10954         
10955         if(this.typeAhead){
10956             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
10957         }
10958     },
10959
10960     onDestroy : function(){
10961         if(this.view){
10962             this.view.setStore(null);
10963             this.view.el.removeAllListeners();
10964             this.view.el.remove();
10965             this.view.purgeListeners();
10966         }
10967         if(this.list){
10968             this.list.dom.innerHTML  = '';
10969         }
10970         
10971         if(this.store){
10972             this.store.un('beforeload', this.onBeforeLoad, this);
10973             this.store.un('load', this.onLoad, this);
10974             this.store.un('loadexception', this.onLoadException, this);
10975         }
10976         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
10977     },
10978
10979     // private
10980     fireKey : function(e){
10981         if(e.isNavKeyPress() && !this.list.isVisible()){
10982             this.fireEvent("specialkey", this, e);
10983         }
10984     },
10985
10986     // private
10987     onResize: function(w, h){
10988 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
10989 //        
10990 //        if(typeof w != 'number'){
10991 //            // we do not handle it!?!?
10992 //            return;
10993 //        }
10994 //        var tw = this.trigger.getWidth();
10995 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
10996 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
10997 //        var x = w - tw;
10998 //        this.inputEl().setWidth( this.adjustWidth('input', x));
10999 //            
11000 //        //this.trigger.setStyle('left', x+'px');
11001 //        
11002 //        if(this.list && this.listWidth === undefined){
11003 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
11004 //            this.list.setWidth(lw);
11005 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11006 //        }
11007         
11008     
11009         
11010     },
11011
11012     /**
11013      * Allow or prevent the user from directly editing the field text.  If false is passed,
11014      * the user will only be able to select from the items defined in the dropdown list.  This method
11015      * is the runtime equivalent of setting the 'editable' config option at config time.
11016      * @param {Boolean} value True to allow the user to directly edit the field text
11017      */
11018     setEditable : function(value){
11019         if(value == this.editable){
11020             return;
11021         }
11022         this.editable = value;
11023         if(!value){
11024             this.inputEl().dom.setAttribute('readOnly', true);
11025             this.inputEl().on('mousedown', this.onTriggerClick,  this);
11026             this.inputEl().addClass('x-combo-noedit');
11027         }else{
11028             this.inputEl().dom.setAttribute('readOnly', false);
11029             this.inputEl().un('mousedown', this.onTriggerClick,  this);
11030             this.inputEl().removeClass('x-combo-noedit');
11031         }
11032     },
11033
11034     // private
11035     
11036     onBeforeLoad : function(combo,opts){
11037         if(!this.hasFocus){
11038             return;
11039         }
11040          if (!opts.add) {
11041             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
11042          }
11043 //        this.restrictHeight();
11044         this.selectedIndex = -1;
11045     },
11046
11047     // private
11048     onLoad : function(){
11049         
11050         this.hasQuery = false;
11051         
11052         if(!this.hasFocus){
11053             return;
11054         }
11055         
11056         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11057             this.loading.hide();
11058         }
11059         
11060         if(this.store.getCount() > 0){
11061             this.expand();
11062 //            this.restrictHeight();
11063             if(this.lastQuery == this.allQuery){
11064                 if(this.editable && !this.tickable){
11065                     this.inputEl().dom.select();
11066                 }
11067                 
11068                 if(
11069                     !this.selectByValue(this.value, true) &&
11070                     this.autoFocus && (typeof(this.store.lastOptions.add) == 'undefined' || 
11071                     this.store.lastOptions.add != true)
11072                 ){
11073                     this.select(0, true);
11074                 }
11075             }else{
11076                 if(this.autoFocus){
11077                     this.selectNext();
11078                 }
11079                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
11080                     this.taTask.delay(this.typeAheadDelay);
11081                 }
11082             }
11083         }else{
11084             this.onEmptyResults();
11085         }
11086         
11087         //this.el.focus();
11088     },
11089     // private
11090     onLoadException : function()
11091     {
11092         this.hasQuery = false;
11093         
11094         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11095             this.loading.hide();
11096         }
11097         
11098         this.collapse();
11099         Roo.log(this.store.reader.jsonData);
11100         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
11101             // fixme
11102             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
11103         }
11104         
11105         
11106     },
11107     // private
11108     onTypeAhead : function(){
11109         if(this.store.getCount() > 0){
11110             var r = this.store.getAt(0);
11111             var newValue = r.data[this.displayField];
11112             var len = newValue.length;
11113             var selStart = this.getRawValue().length;
11114             
11115             if(selStart != len){
11116                 this.setRawValue(newValue);
11117                 this.selectText(selStart, newValue.length);
11118             }
11119         }
11120     },
11121
11122     // private
11123     onSelect : function(record, index){
11124         
11125         if(this.fireEvent('beforeselect', this, record, index) !== false){
11126         
11127             this.setFromData(index > -1 ? record.data : false);
11128             
11129             this.collapse();
11130             this.fireEvent('select', this, record, index);
11131         }
11132     },
11133
11134     /**
11135      * Returns the currently selected field value or empty string if no value is set.
11136      * @return {String} value The selected value
11137      */
11138     getValue : function(){
11139         
11140         if(this.multiple){
11141             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
11142         }
11143         
11144         if(this.valueField){
11145             return typeof this.value != 'undefined' ? this.value : '';
11146         }else{
11147             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
11148         }
11149     },
11150
11151     /**
11152      * Clears any text/value currently set in the field
11153      */
11154     clearValue : function(){
11155         if(this.hiddenField){
11156             this.hiddenField.dom.value = '';
11157         }
11158         this.value = '';
11159         this.setRawValue('');
11160         this.lastSelectionText = '';
11161         
11162     },
11163
11164     /**
11165      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
11166      * will be displayed in the field.  If the value does not match the data value of an existing item,
11167      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
11168      * Otherwise the field will be blank (although the value will still be set).
11169      * @param {String} value The value to match
11170      */
11171     setValue : function(v){
11172         if(this.multiple){
11173             this.syncValue();
11174             return;
11175         }
11176         
11177         var text = v;
11178         if(this.valueField){
11179             var r = this.findRecord(this.valueField, v);
11180             if(r){
11181                 text = r.data[this.displayField];
11182             }else if(this.valueNotFoundText !== undefined){
11183                 text = this.valueNotFoundText;
11184             }
11185         }
11186         this.lastSelectionText = text;
11187         if(this.hiddenField){
11188             this.hiddenField.dom.value = v;
11189         }
11190         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
11191         this.value = v;
11192     },
11193     /**
11194      * @property {Object} the last set data for the element
11195      */
11196     
11197     lastData : false,
11198     /**
11199      * Sets the value of the field based on a object which is related to the record format for the store.
11200      * @param {Object} value the value to set as. or false on reset?
11201      */
11202     setFromData : function(o){
11203         
11204         if(this.multiple){
11205             if(typeof o.display_name !== 'string'){
11206                 for(var i=0;i<o.display_name.length;i++){
11207                     this.addItem({'id':o.id[i],'display_name':o.display_name[i]});
11208                 }
11209                 return;
11210             }
11211             this.addItem(o);
11212             return;
11213         }
11214             
11215         var dv = ''; // display value
11216         var vv = ''; // value value..
11217         this.lastData = o;
11218         if (this.displayField) {
11219             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11220         } else {
11221             // this is an error condition!!!
11222             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
11223         }
11224         
11225         if(this.valueField){
11226             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
11227         }
11228         
11229         if(this.hiddenField){
11230             this.hiddenField.dom.value = vv;
11231             
11232             this.lastSelectionText = dv;
11233             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11234             this.value = vv;
11235             return;
11236         }
11237         // no hidden field.. - we store the value in 'value', but still display
11238         // display field!!!!
11239         this.lastSelectionText = dv;
11240         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11241         this.value = vv;
11242         
11243         
11244     },
11245     // private
11246     reset : function(){
11247         // overridden so that last data is reset..
11248         this.setValue(this.originalValue);
11249         this.clearInvalid();
11250         this.lastData = false;
11251         if (this.view) {
11252             this.view.clearSelections();
11253         }
11254     },
11255     // private
11256     findRecord : function(prop, value){
11257         var record;
11258         if(this.store.getCount() > 0){
11259             this.store.each(function(r){
11260                 if(r.data[prop] == value){
11261                     record = r;
11262                     return false;
11263                 }
11264                 return true;
11265             });
11266         }
11267         return record;
11268     },
11269     
11270     getName: function()
11271     {
11272         // returns hidden if it's set..
11273         if (!this.rendered) {return ''};
11274         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
11275         
11276     },
11277     // private
11278     onViewMove : function(e, t){
11279         this.inKeyMode = false;
11280     },
11281
11282     // private
11283     onViewOver : function(e, t){
11284         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
11285             return;
11286         }
11287         var item = this.view.findItemFromChild(t);
11288         
11289         if(item){
11290             var index = this.view.indexOf(item);
11291             this.select(index, false);
11292         }
11293     },
11294
11295     // private
11296     onViewClick : function(view, doFocus, el, e)
11297     {
11298         var index = this.view.getSelectedIndexes()[0];
11299         
11300         var r = this.store.getAt(index);
11301         
11302         if(this.tickable){
11303             
11304             if(e.getTarget().nodeName.toLowerCase() != 'input'){
11305                 return;
11306             }
11307             
11308             var rm = false;
11309             var _this = this;
11310             
11311             Roo.each(this.tickItems, function(v,k){
11312                 
11313                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
11314                     _this.tickItems.splice(k, 1);
11315                     rm = true;
11316                     return;
11317                 }
11318             })
11319             
11320             if(rm){
11321                 return;
11322             }
11323             
11324             this.tickItems.push(r.data);
11325             return;
11326         }
11327         
11328         if(r){
11329             this.onSelect(r, index);
11330         }
11331         if(doFocus !== false && !this.blockFocus){
11332             this.inputEl().focus();
11333         }
11334     },
11335
11336     // private
11337     restrictHeight : function(){
11338         //this.innerList.dom.style.height = '';
11339         //var inner = this.innerList.dom;
11340         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
11341         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
11342         //this.list.beginUpdate();
11343         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
11344         this.list.alignTo(this.inputEl(), this.listAlign);
11345         this.list.alignTo(this.inputEl(), this.listAlign);
11346         //this.list.endUpdate();
11347     },
11348
11349     // private
11350     onEmptyResults : function(){
11351         this.collapse();
11352     },
11353
11354     /**
11355      * Returns true if the dropdown list is expanded, else false.
11356      */
11357     isExpanded : function(){
11358         return this.list.isVisible();
11359     },
11360
11361     /**
11362      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
11363      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11364      * @param {String} value The data value of the item to select
11365      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11366      * selected item if it is not currently in view (defaults to true)
11367      * @return {Boolean} True if the value matched an item in the list, else false
11368      */
11369     selectByValue : function(v, scrollIntoView){
11370         if(v !== undefined && v !== null){
11371             var r = this.findRecord(this.valueField || this.displayField, v);
11372             if(r){
11373                 this.select(this.store.indexOf(r), scrollIntoView);
11374                 return true;
11375             }
11376         }
11377         return false;
11378     },
11379
11380     /**
11381      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
11382      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11383      * @param {Number} index The zero-based index of the list item to select
11384      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11385      * selected item if it is not currently in view (defaults to true)
11386      */
11387     select : function(index, scrollIntoView){
11388         this.selectedIndex = index;
11389         this.view.select(index);
11390         if(scrollIntoView !== false){
11391             var el = this.view.getNode(index);
11392             if(el && !this.multiple && !this.tickable){
11393                 this.list.scrollChildIntoView(el, false);
11394             }
11395         }
11396     },
11397
11398     // private
11399     selectNext : function(){
11400         var ct = this.store.getCount();
11401         if(ct > 0){
11402             if(this.selectedIndex == -1){
11403                 this.select(0);
11404             }else if(this.selectedIndex < ct-1){
11405                 this.select(this.selectedIndex+1);
11406             }
11407         }
11408     },
11409
11410     // private
11411     selectPrev : function(){
11412         var ct = this.store.getCount();
11413         if(ct > 0){
11414             if(this.selectedIndex == -1){
11415                 this.select(0);
11416             }else if(this.selectedIndex != 0){
11417                 this.select(this.selectedIndex-1);
11418             }
11419         }
11420     },
11421
11422     // private
11423     onKeyUp : function(e){
11424         if(this.editable !== false && !e.isSpecialKey()){
11425             this.lastKey = e.getKey();
11426             this.dqTask.delay(this.queryDelay);
11427         }
11428     },
11429
11430     // private
11431     validateBlur : function(){
11432         return !this.list || !this.list.isVisible();   
11433     },
11434
11435     // private
11436     initQuery : function(){
11437         this.doQuery(this.getRawValue());
11438     },
11439
11440     // private
11441     doForce : function(){
11442         if(this.inputEl().dom.value.length > 0){
11443             this.inputEl().dom.value =
11444                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
11445              
11446         }
11447     },
11448
11449     /**
11450      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
11451      * query allowing the query action to be canceled if needed.
11452      * @param {String} query The SQL query to execute
11453      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
11454      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
11455      * saved in the current store (defaults to false)
11456      */
11457     doQuery : function(q, forceAll){
11458         
11459         if(q === undefined || q === null){
11460             q = '';
11461         }
11462         var qe = {
11463             query: q,
11464             forceAll: forceAll,
11465             combo: this,
11466             cancel:false
11467         };
11468         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
11469             return false;
11470         }
11471         q = qe.query;
11472         
11473         forceAll = qe.forceAll;
11474         if(forceAll === true || (q.length >= this.minChars)){
11475             
11476             this.hasQuery = true;
11477             
11478             if(this.lastQuery != q || this.alwaysQuery){
11479                 this.lastQuery = q;
11480                 if(this.mode == 'local'){
11481                     this.selectedIndex = -1;
11482                     if(forceAll){
11483                         this.store.clearFilter();
11484                     }else{
11485                         this.store.filter(this.displayField, q);
11486                     }
11487                     this.onLoad();
11488                 }else{
11489                     this.store.baseParams[this.queryParam] = q;
11490                     
11491                     var options = {params : this.getParams(q)};
11492                     
11493                     if(this.loadNext){
11494                         options.add = true;
11495                         options.params.start = this.page * this.pageSize;
11496                     }
11497                     
11498                     this.store.load(options);
11499                     /*
11500                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
11501                      *  we should expand the list on onLoad
11502                      *  so command out it
11503                      */
11504 //                    this.expand();
11505                 }
11506             }else{
11507                 this.selectedIndex = -1;
11508                 this.onLoad();   
11509             }
11510         }
11511         
11512         this.loadNext = false;
11513     },
11514
11515     // private
11516     getParams : function(q){
11517         var p = {};
11518         //p[this.queryParam] = q;
11519         
11520         if(this.pageSize){
11521             p.start = 0;
11522             p.limit = this.pageSize;
11523         }
11524         return p;
11525     },
11526
11527     /**
11528      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
11529      */
11530     collapse : function(){
11531         if(!this.isExpanded()){
11532             return;
11533         }
11534         
11535         this.list.hide();
11536         
11537         if(this.tickable){
11538             this.okBtn.hide();
11539             this.cancelBtn.hide();
11540             this.trigger.show();
11541         }
11542         
11543         Roo.get(document).un('mousedown', this.collapseIf, this);
11544         Roo.get(document).un('mousewheel', this.collapseIf, this);
11545         if (!this.editable) {
11546             Roo.get(document).un('keydown', this.listKeyPress, this);
11547         }
11548         this.fireEvent('collapse', this);
11549     },
11550
11551     // private
11552     collapseIf : function(e){
11553         var in_combo  = e.within(this.el);
11554         var in_list =  e.within(this.list);
11555         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
11556         
11557         if (in_combo || in_list || is_list) {
11558             //e.stopPropagation();
11559             return;
11560         }
11561         
11562         if(this.tickable){
11563             this.onTickableFooterButtonClick(e, false, false);
11564         }
11565
11566         this.collapse();
11567         
11568     },
11569
11570     /**
11571      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
11572      */
11573     expand : function(){
11574        
11575         if(this.isExpanded() || !this.hasFocus){
11576             return;
11577         }
11578         
11579         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
11580         this.list.setWidth(lw);
11581         
11582         
11583          Roo.log('expand');
11584         
11585         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
11586         this.list.setWidth(lw);
11587             
11588         this.list.show();
11589         
11590         this.restrictHeight();
11591         
11592         if(this.tickable){
11593             
11594             this.tickItems = Roo.apply([], this.item);
11595             
11596             this.okBtn.show();
11597             this.cancelBtn.show();
11598             this.trigger.hide();
11599             
11600         }
11601         
11602         Roo.get(document).on('mousedown', this.collapseIf, this);
11603         Roo.get(document).on('mousewheel', this.collapseIf, this);
11604         if (!this.editable) {
11605             Roo.get(document).on('keydown', this.listKeyPress, this);
11606         }
11607         
11608         this.fireEvent('expand', this);
11609     },
11610
11611     // private
11612     // Implements the default empty TriggerField.onTriggerClick function
11613     onTriggerClick : function(e)
11614     {
11615         Roo.log('trigger click');
11616         
11617         if(this.disabled || !this.triggerList){
11618             return;
11619         }
11620         
11621         this.page = 0;
11622         this.loadNext = false;
11623         
11624         if(this.isExpanded()){
11625             this.collapse();
11626             if (!this.blockFocus) {
11627                 this.inputEl().focus();
11628             }
11629             
11630         }else {
11631             this.hasFocus = true;
11632             if(this.triggerAction == 'all') {
11633                 this.doQuery(this.allQuery, true);
11634             } else {
11635                 this.doQuery(this.getRawValue());
11636             }
11637             if (!this.blockFocus) {
11638                 this.inputEl().focus();
11639             }
11640         }
11641     },
11642     
11643     onTickableTriggerClick : function(e)
11644     {
11645         if(this.disabled){
11646             return;
11647         }
11648         
11649         this.page = 0;
11650         this.loadNext = false;
11651         this.hasFocus = true;
11652         
11653         if(this.triggerAction == 'all') {
11654             this.doQuery(this.allQuery, true);
11655         } else {
11656             this.doQuery(this.getRawValue());
11657         }
11658     },
11659     
11660     onSearchFieldClick : function(e)
11661     {
11662         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
11663             return;
11664         }
11665         
11666         this.page = 0;
11667         this.loadNext = false;
11668         this.hasFocus = true;
11669         
11670         if(this.triggerAction == 'all') {
11671             this.doQuery(this.allQuery, true);
11672         } else {
11673             this.doQuery(this.getRawValue());
11674         }
11675     },
11676     
11677     listKeyPress : function(e)
11678     {
11679         //Roo.log('listkeypress');
11680         // scroll to first matching element based on key pres..
11681         if (e.isSpecialKey()) {
11682             return false;
11683         }
11684         var k = String.fromCharCode(e.getKey()).toUpperCase();
11685         //Roo.log(k);
11686         var match  = false;
11687         var csel = this.view.getSelectedNodes();
11688         var cselitem = false;
11689         if (csel.length) {
11690             var ix = this.view.indexOf(csel[0]);
11691             cselitem  = this.store.getAt(ix);
11692             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
11693                 cselitem = false;
11694             }
11695             
11696         }
11697         
11698         this.store.each(function(v) { 
11699             if (cselitem) {
11700                 // start at existing selection.
11701                 if (cselitem.id == v.id) {
11702                     cselitem = false;
11703                 }
11704                 return true;
11705             }
11706                 
11707             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
11708                 match = this.store.indexOf(v);
11709                 return false;
11710             }
11711             return true;
11712         }, this);
11713         
11714         if (match === false) {
11715             return true; // no more action?
11716         }
11717         // scroll to?
11718         this.view.select(match);
11719         var sn = Roo.get(this.view.getSelectedNodes()[0])
11720         //sn.scrollIntoView(sn.dom.parentNode, false);
11721     },
11722     
11723     onViewScroll : function(e, t){
11724         
11725         if(this.view.el.getScroll().top == 0 ||this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
11726             return;
11727         }
11728         
11729         this.hasQuery = true;
11730         
11731         this.loading = this.list.select('.loading', true).first();
11732         
11733         if(this.loading === null){
11734             this.list.createChild({
11735                 tag: 'div',
11736                 cls: 'loading select2-more-results select2-active',
11737                 html: 'Loading more results...'
11738             })
11739             
11740             this.loading = this.list.select('.loading', true).first();
11741             
11742             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
11743             
11744             this.loading.hide();
11745         }
11746         
11747         this.loading.show();
11748         
11749         var _combo = this;
11750         
11751         this.page++;
11752         this.loadNext = true;
11753         
11754         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
11755         
11756         return;
11757     },
11758     
11759     addItem : function(o)
11760     {   
11761         var dv = ''; // display value
11762         
11763         if (this.displayField) {
11764             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11765         } else {
11766             // this is an error condition!!!
11767             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
11768         }
11769         
11770         if(!dv.length){
11771             return;
11772         }
11773         
11774         var choice = this.choices.createChild({
11775             tag: 'li',
11776             cls: 'select2-search-choice',
11777             cn: [
11778                 {
11779                     tag: 'div',
11780                     html: dv
11781                 },
11782                 {
11783                     tag: 'a',
11784                     href: '#',
11785                     cls: 'select2-search-choice-close',
11786                     tabindex: '-1'
11787                 }
11788             ]
11789             
11790         }, this.searchField);
11791         
11792         var close = choice.select('a.select2-search-choice-close', true).first()
11793         
11794         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
11795         
11796         this.item.push(o);
11797         
11798         this.lastData = o;
11799         
11800         this.syncValue();
11801         
11802         this.inputEl().dom.value = '';
11803         
11804     },
11805     
11806     onRemoveItem : function(e, _self, o)
11807     {
11808         e.preventDefault();
11809         var index = this.item.indexOf(o.data) * 1;
11810         
11811         if( index < 0){
11812             Roo.log('not this item?!');
11813             return;
11814         }
11815         
11816         this.item.splice(index, 1);
11817         o.item.remove();
11818         
11819         this.syncValue();
11820         
11821         this.fireEvent('remove', this, e);
11822         
11823     },
11824     
11825     syncValue : function()
11826     {
11827         if(!this.item.length){
11828             this.clearValue();
11829             return;
11830         }
11831             
11832         var value = [];
11833         var _this = this;
11834         Roo.each(this.item, function(i){
11835             if(_this.valueField){
11836                 value.push(i[_this.valueField]);
11837                 return;
11838             }
11839
11840             value.push(i);
11841         });
11842
11843         this.value = value.join(',');
11844
11845         if(this.hiddenField){
11846             this.hiddenField.dom.value = this.value;
11847         }
11848     },
11849     
11850     clearItem : function()
11851     {
11852         if(!this.multiple){
11853             return;
11854         }
11855         
11856         this.item = [];
11857         
11858         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
11859            c.remove();
11860         });
11861         
11862         this.syncValue();
11863     },
11864     
11865     inputEl: function ()
11866     {
11867         if(this.tickable){
11868             return this.searchField;
11869         }
11870         return this.el.select('input.form-control',true).first();
11871     },
11872     
11873     
11874     onTickableFooterButtonClick : function(e, btn, el)
11875     {
11876         e.preventDefault();
11877         
11878         if(btn && btn.name == 'cancel'){
11879             this.tickItems = Roo.apply([], this.item);
11880             this.collapse();
11881             return;
11882         }
11883         
11884         this.clearItem();
11885         
11886         var _this = this;
11887         
11888         Roo.each(this.tickItems, function(o){
11889             _this.addItem(o);
11890         });
11891         
11892         this.collapse();
11893         
11894     }
11895     
11896     
11897
11898     /** 
11899     * @cfg {Boolean} grow 
11900     * @hide 
11901     */
11902     /** 
11903     * @cfg {Number} growMin 
11904     * @hide 
11905     */
11906     /** 
11907     * @cfg {Number} growMax 
11908     * @hide 
11909     */
11910     /**
11911      * @hide
11912      * @method autoSize
11913      */
11914 });
11915 /*
11916  * Based on:
11917  * Ext JS Library 1.1.1
11918  * Copyright(c) 2006-2007, Ext JS, LLC.
11919  *
11920  * Originally Released Under LGPL - original licence link has changed is not relivant.
11921  *
11922  * Fork - LGPL
11923  * <script type="text/javascript">
11924  */
11925
11926 /**
11927  * @class Roo.View
11928  * @extends Roo.util.Observable
11929  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
11930  * This class also supports single and multi selection modes. <br>
11931  * Create a data model bound view:
11932  <pre><code>
11933  var store = new Roo.data.Store(...);
11934
11935  var view = new Roo.View({
11936     el : "my-element",
11937     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
11938  
11939     singleSelect: true,
11940     selectedClass: "ydataview-selected",
11941     store: store
11942  });
11943
11944  // listen for node click?
11945  view.on("click", function(vw, index, node, e){
11946  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
11947  });
11948
11949  // load XML data
11950  dataModel.load("foobar.xml");
11951  </code></pre>
11952  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
11953  * <br><br>
11954  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
11955  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
11956  * 
11957  * Note: old style constructor is still suported (container, template, config)
11958  * 
11959  * @constructor
11960  * Create a new View
11961  * @param {Object} config The config object
11962  * 
11963  */
11964 Roo.View = function(config, depreciated_tpl, depreciated_config){
11965     
11966     this.parent = false;
11967     
11968     if (typeof(depreciated_tpl) == 'undefined') {
11969         // new way.. - universal constructor.
11970         Roo.apply(this, config);
11971         this.el  = Roo.get(this.el);
11972     } else {
11973         // old format..
11974         this.el  = Roo.get(config);
11975         this.tpl = depreciated_tpl;
11976         Roo.apply(this, depreciated_config);
11977     }
11978     this.wrapEl  = this.el.wrap().wrap();
11979     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
11980     
11981     
11982     if(typeof(this.tpl) == "string"){
11983         this.tpl = new Roo.Template(this.tpl);
11984     } else {
11985         // support xtype ctors..
11986         this.tpl = new Roo.factory(this.tpl, Roo);
11987     }
11988     
11989     
11990     this.tpl.compile();
11991     
11992     /** @private */
11993     this.addEvents({
11994         /**
11995          * @event beforeclick
11996          * Fires before a click is processed. Returns false to cancel the default action.
11997          * @param {Roo.View} this
11998          * @param {Number} index The index of the target node
11999          * @param {HTMLElement} node The target node
12000          * @param {Roo.EventObject} e The raw event object
12001          */
12002             "beforeclick" : true,
12003         /**
12004          * @event click
12005          * Fires when a template node is clicked.
12006          * @param {Roo.View} this
12007          * @param {Number} index The index of the target node
12008          * @param {HTMLElement} node The target node
12009          * @param {Roo.EventObject} e The raw event object
12010          */
12011             "click" : true,
12012         /**
12013          * @event dblclick
12014          * Fires when a template node is double clicked.
12015          * @param {Roo.View} this
12016          * @param {Number} index The index of the target node
12017          * @param {HTMLElement} node The target node
12018          * @param {Roo.EventObject} e The raw event object
12019          */
12020             "dblclick" : true,
12021         /**
12022          * @event contextmenu
12023          * Fires when a template node is right clicked.
12024          * @param {Roo.View} this
12025          * @param {Number} index The index of the target node
12026          * @param {HTMLElement} node The target node
12027          * @param {Roo.EventObject} e The raw event object
12028          */
12029             "contextmenu" : true,
12030         /**
12031          * @event selectionchange
12032          * Fires when the selected nodes change.
12033          * @param {Roo.View} this
12034          * @param {Array} selections Array of the selected nodes
12035          */
12036             "selectionchange" : true,
12037     
12038         /**
12039          * @event beforeselect
12040          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
12041          * @param {Roo.View} this
12042          * @param {HTMLElement} node The node to be selected
12043          * @param {Array} selections Array of currently selected nodes
12044          */
12045             "beforeselect" : true,
12046         /**
12047          * @event preparedata
12048          * Fires on every row to render, to allow you to change the data.
12049          * @param {Roo.View} this
12050          * @param {Object} data to be rendered (change this)
12051          */
12052           "preparedata" : true
12053           
12054           
12055         });
12056
12057
12058
12059     this.el.on({
12060         "click": this.onClick,
12061         "dblclick": this.onDblClick,
12062         "contextmenu": this.onContextMenu,
12063         scope:this
12064     });
12065
12066     this.selections = [];
12067     this.nodes = [];
12068     this.cmp = new Roo.CompositeElementLite([]);
12069     if(this.store){
12070         this.store = Roo.factory(this.store, Roo.data);
12071         this.setStore(this.store, true);
12072     }
12073     
12074     if ( this.footer && this.footer.xtype) {
12075            
12076          var fctr = this.wrapEl.appendChild(document.createElement("div"));
12077         
12078         this.footer.dataSource = this.store
12079         this.footer.container = fctr;
12080         this.footer = Roo.factory(this.footer, Roo);
12081         fctr.insertFirst(this.el);
12082         
12083         // this is a bit insane - as the paging toolbar seems to detach the el..
12084 //        dom.parentNode.parentNode.parentNode
12085          // they get detached?
12086     }
12087     
12088     
12089     Roo.View.superclass.constructor.call(this);
12090     
12091     
12092 };
12093
12094 Roo.extend(Roo.View, Roo.util.Observable, {
12095     
12096      /**
12097      * @cfg {Roo.data.Store} store Data store to load data from.
12098      */
12099     store : false,
12100     
12101     /**
12102      * @cfg {String|Roo.Element} el The container element.
12103      */
12104     el : '',
12105     
12106     /**
12107      * @cfg {String|Roo.Template} tpl The template used by this View 
12108      */
12109     tpl : false,
12110     /**
12111      * @cfg {String} dataName the named area of the template to use as the data area
12112      *                          Works with domtemplates roo-name="name"
12113      */
12114     dataName: false,
12115     /**
12116      * @cfg {String} selectedClass The css class to add to selected nodes
12117      */
12118     selectedClass : "x-view-selected",
12119      /**
12120      * @cfg {String} emptyText The empty text to show when nothing is loaded.
12121      */
12122     emptyText : "",
12123     
12124     /**
12125      * @cfg {String} text to display on mask (default Loading)
12126      */
12127     mask : false,
12128     /**
12129      * @cfg {Boolean} multiSelect Allow multiple selection
12130      */
12131     multiSelect : false,
12132     /**
12133      * @cfg {Boolean} singleSelect Allow single selection
12134      */
12135     singleSelect:  false,
12136     
12137     /**
12138      * @cfg {Boolean} toggleSelect - selecting 
12139      */
12140     toggleSelect : false,
12141     
12142     /**
12143      * @cfg {Boolean} tickable - selecting 
12144      */
12145     tickable : false,
12146     
12147     /**
12148      * Returns the element this view is bound to.
12149      * @return {Roo.Element}
12150      */
12151     getEl : function(){
12152         return this.wrapEl;
12153     },
12154     
12155     
12156
12157     /**
12158      * Refreshes the view. - called by datachanged on the store. - do not call directly.
12159      */
12160     refresh : function(){
12161         Roo.log('refresh');
12162         var t = this.tpl;
12163         
12164         // if we are using something like 'domtemplate', then
12165         // the what gets used is:
12166         // t.applySubtemplate(NAME, data, wrapping data..)
12167         // the outer template then get' applied with
12168         //     the store 'extra data'
12169         // and the body get's added to the
12170         //      roo-name="data" node?
12171         //      <span class='roo-tpl-{name}'></span> ?????
12172         
12173         
12174         
12175         this.clearSelections();
12176         this.el.update("");
12177         var html = [];
12178         var records = this.store.getRange();
12179         if(records.length < 1) {
12180             
12181             // is this valid??  = should it render a template??
12182             
12183             this.el.update(this.emptyText);
12184             return;
12185         }
12186         var el = this.el;
12187         if (this.dataName) {
12188             this.el.update(t.apply(this.store.meta)); //????
12189             el = this.el.child('.roo-tpl-' + this.dataName);
12190         }
12191         
12192         for(var i = 0, len = records.length; i < len; i++){
12193             var data = this.prepareData(records[i].data, i, records[i]);
12194             this.fireEvent("preparedata", this, data, i, records[i]);
12195             
12196             var d = Roo.apply({}, data);
12197             
12198             if(this.tickable){
12199                 Roo.apply(d, {'roo-id' : Roo.id()});
12200                 
12201                 var _this = this;
12202             
12203                 Roo.each(this.parent.item, function(item){
12204                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
12205                         return;
12206                     }
12207                     Roo.apply(d, {'roo-data-checked' : 'checked'});
12208                 });
12209             }
12210             
12211             html[html.length] = Roo.util.Format.trim(
12212                 this.dataName ?
12213                     t.applySubtemplate(this.dataName, d, this.store.meta) :
12214                     t.apply(d)
12215             );
12216         }
12217         
12218         
12219         
12220         el.update(html.join(""));
12221         this.nodes = el.dom.childNodes;
12222         this.updateIndexes(0);
12223     },
12224     
12225
12226     /**
12227      * Function to override to reformat the data that is sent to
12228      * the template for each node.
12229      * DEPRICATED - use the preparedata event handler.
12230      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
12231      * a JSON object for an UpdateManager bound view).
12232      */
12233     prepareData : function(data, index, record)
12234     {
12235         this.fireEvent("preparedata", this, data, index, record);
12236         return data;
12237     },
12238
12239     onUpdate : function(ds, record){
12240          Roo.log('on update');   
12241         this.clearSelections();
12242         var index = this.store.indexOf(record);
12243         var n = this.nodes[index];
12244         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
12245         n.parentNode.removeChild(n);
12246         this.updateIndexes(index, index);
12247     },
12248
12249     
12250     
12251 // --------- FIXME     
12252     onAdd : function(ds, records, index)
12253     {
12254         Roo.log(['on Add', ds, records, index] );        
12255         this.clearSelections();
12256         if(this.nodes.length == 0){
12257             this.refresh();
12258             return;
12259         }
12260         var n = this.nodes[index];
12261         for(var i = 0, len = records.length; i < len; i++){
12262             var d = this.prepareData(records[i].data, i, records[i]);
12263             if(n){
12264                 this.tpl.insertBefore(n, d);
12265             }else{
12266                 
12267                 this.tpl.append(this.el, d);
12268             }
12269         }
12270         this.updateIndexes(index);
12271     },
12272
12273     onRemove : function(ds, record, index){
12274         Roo.log('onRemove');
12275         this.clearSelections();
12276         var el = this.dataName  ?
12277             this.el.child('.roo-tpl-' + this.dataName) :
12278             this.el; 
12279         
12280         el.dom.removeChild(this.nodes[index]);
12281         this.updateIndexes(index);
12282     },
12283
12284     /**
12285      * Refresh an individual node.
12286      * @param {Number} index
12287      */
12288     refreshNode : function(index){
12289         this.onUpdate(this.store, this.store.getAt(index));
12290     },
12291
12292     updateIndexes : function(startIndex, endIndex){
12293         var ns = this.nodes;
12294         startIndex = startIndex || 0;
12295         endIndex = endIndex || ns.length - 1;
12296         for(var i = startIndex; i <= endIndex; i++){
12297             ns[i].nodeIndex = i;
12298         }
12299     },
12300
12301     /**
12302      * Changes the data store this view uses and refresh the view.
12303      * @param {Store} store
12304      */
12305     setStore : function(store, initial){
12306         if(!initial && this.store){
12307             this.store.un("datachanged", this.refresh);
12308             this.store.un("add", this.onAdd);
12309             this.store.un("remove", this.onRemove);
12310             this.store.un("update", this.onUpdate);
12311             this.store.un("clear", this.refresh);
12312             this.store.un("beforeload", this.onBeforeLoad);
12313             this.store.un("load", this.onLoad);
12314             this.store.un("loadexception", this.onLoad);
12315         }
12316         if(store){
12317           
12318             store.on("datachanged", this.refresh, this);
12319             store.on("add", this.onAdd, this);
12320             store.on("remove", this.onRemove, this);
12321             store.on("update", this.onUpdate, this);
12322             store.on("clear", this.refresh, this);
12323             store.on("beforeload", this.onBeforeLoad, this);
12324             store.on("load", this.onLoad, this);
12325             store.on("loadexception", this.onLoad, this);
12326         }
12327         
12328         if(store){
12329             this.refresh();
12330         }
12331     },
12332     /**
12333      * onbeforeLoad - masks the loading area.
12334      *
12335      */
12336     onBeforeLoad : function(store,opts)
12337     {
12338          Roo.log('onBeforeLoad');   
12339         if (!opts.add) {
12340             this.el.update("");
12341         }
12342         this.el.mask(this.mask ? this.mask : "Loading" ); 
12343     },
12344     onLoad : function ()
12345     {
12346         this.el.unmask();
12347     },
12348     
12349
12350     /**
12351      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
12352      * @param {HTMLElement} node
12353      * @return {HTMLElement} The template node
12354      */
12355     findItemFromChild : function(node){
12356         var el = this.dataName  ?
12357             this.el.child('.roo-tpl-' + this.dataName,true) :
12358             this.el.dom; 
12359         
12360         if(!node || node.parentNode == el){
12361                     return node;
12362             }
12363             var p = node.parentNode;
12364             while(p && p != el){
12365             if(p.parentNode == el){
12366                 return p;
12367             }
12368             p = p.parentNode;
12369         }
12370             return null;
12371     },
12372
12373     /** @ignore */
12374     onClick : function(e){
12375         var item = this.findItemFromChild(e.getTarget());
12376         if(item){
12377             var index = this.indexOf(item);
12378             if(this.onItemClick(item, index, e) !== false){
12379                 this.fireEvent("click", this, index, item, e);
12380             }
12381         }else{
12382             this.clearSelections();
12383         }
12384     },
12385
12386     /** @ignore */
12387     onContextMenu : function(e){
12388         var item = this.findItemFromChild(e.getTarget());
12389         if(item){
12390             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
12391         }
12392     },
12393
12394     /** @ignore */
12395     onDblClick : function(e){
12396         var item = this.findItemFromChild(e.getTarget());
12397         if(item){
12398             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
12399         }
12400     },
12401
12402     onItemClick : function(item, index, e)
12403     {
12404         if(this.fireEvent("beforeclick", this, index, item, e) === false){
12405             return false;
12406         }
12407         if (this.toggleSelect) {
12408             var m = this.isSelected(item) ? 'unselect' : 'select';
12409             Roo.log(m);
12410             var _t = this;
12411             _t[m](item, true, false);
12412             return true;
12413         }
12414         if(this.multiSelect || this.singleSelect){
12415             if(this.multiSelect && e.shiftKey && this.lastSelection){
12416                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
12417             }else{
12418                 this.select(item, this.multiSelect && e.ctrlKey);
12419                 this.lastSelection = item;
12420             }
12421             
12422             if(!this.tickable){
12423                 e.preventDefault();
12424             }
12425             
12426         }
12427         return true;
12428     },
12429
12430     /**
12431      * Get the number of selected nodes.
12432      * @return {Number}
12433      */
12434     getSelectionCount : function(){
12435         return this.selections.length;
12436     },
12437
12438     /**
12439      * Get the currently selected nodes.
12440      * @return {Array} An array of HTMLElements
12441      */
12442     getSelectedNodes : function(){
12443         return this.selections;
12444     },
12445
12446     /**
12447      * Get the indexes of the selected nodes.
12448      * @return {Array}
12449      */
12450     getSelectedIndexes : function(){
12451         var indexes = [], s = this.selections;
12452         for(var i = 0, len = s.length; i < len; i++){
12453             indexes.push(s[i].nodeIndex);
12454         }
12455         return indexes;
12456     },
12457
12458     /**
12459      * Clear all selections
12460      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
12461      */
12462     clearSelections : function(suppressEvent){
12463         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
12464             this.cmp.elements = this.selections;
12465             this.cmp.removeClass(this.selectedClass);
12466             this.selections = [];
12467             if(!suppressEvent){
12468                 this.fireEvent("selectionchange", this, this.selections);
12469             }
12470         }
12471     },
12472
12473     /**
12474      * Returns true if the passed node is selected
12475      * @param {HTMLElement/Number} node The node or node index
12476      * @return {Boolean}
12477      */
12478     isSelected : function(node){
12479         var s = this.selections;
12480         if(s.length < 1){
12481             return false;
12482         }
12483         node = this.getNode(node);
12484         return s.indexOf(node) !== -1;
12485     },
12486
12487     /**
12488      * Selects nodes.
12489      * @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
12490      * @param {Boolean} keepExisting (optional) true to keep existing selections
12491      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12492      */
12493     select : function(nodeInfo, keepExisting, suppressEvent){
12494         if(nodeInfo instanceof Array){
12495             if(!keepExisting){
12496                 this.clearSelections(true);
12497             }
12498             for(var i = 0, len = nodeInfo.length; i < len; i++){
12499                 this.select(nodeInfo[i], true, true);
12500             }
12501             return;
12502         } 
12503         var node = this.getNode(nodeInfo);
12504         if(!node || this.isSelected(node)){
12505             return; // already selected.
12506         }
12507         if(!keepExisting){
12508             this.clearSelections(true);
12509         }
12510         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
12511             Roo.fly(node).addClass(this.selectedClass);
12512             this.selections.push(node);
12513             if(!suppressEvent){
12514                 this.fireEvent("selectionchange", this, this.selections);
12515             }
12516         }
12517         
12518         
12519     },
12520       /**
12521      * Unselects nodes.
12522      * @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
12523      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
12524      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12525      */
12526     unselect : function(nodeInfo, keepExisting, suppressEvent)
12527     {
12528         if(nodeInfo instanceof Array){
12529             Roo.each(this.selections, function(s) {
12530                 this.unselect(s, nodeInfo);
12531             }, this);
12532             return;
12533         }
12534         var node = this.getNode(nodeInfo);
12535         if(!node || !this.isSelected(node)){
12536             Roo.log("not selected");
12537             return; // not selected.
12538         }
12539         // fireevent???
12540         var ns = [];
12541         Roo.each(this.selections, function(s) {
12542             if (s == node ) {
12543                 Roo.fly(node).removeClass(this.selectedClass);
12544
12545                 return;
12546             }
12547             ns.push(s);
12548         },this);
12549         
12550         this.selections= ns;
12551         this.fireEvent("selectionchange", this, this.selections);
12552     },
12553
12554     /**
12555      * Gets a template node.
12556      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12557      * @return {HTMLElement} The node or null if it wasn't found
12558      */
12559     getNode : function(nodeInfo){
12560         if(typeof nodeInfo == "string"){
12561             return document.getElementById(nodeInfo);
12562         }else if(typeof nodeInfo == "number"){
12563             return this.nodes[nodeInfo];
12564         }
12565         return nodeInfo;
12566     },
12567
12568     /**
12569      * Gets a range template nodes.
12570      * @param {Number} startIndex
12571      * @param {Number} endIndex
12572      * @return {Array} An array of nodes
12573      */
12574     getNodes : function(start, end){
12575         var ns = this.nodes;
12576         start = start || 0;
12577         end = typeof end == "undefined" ? ns.length - 1 : end;
12578         var nodes = [];
12579         if(start <= end){
12580             for(var i = start; i <= end; i++){
12581                 nodes.push(ns[i]);
12582             }
12583         } else{
12584             for(var i = start; i >= end; i--){
12585                 nodes.push(ns[i]);
12586             }
12587         }
12588         return nodes;
12589     },
12590
12591     /**
12592      * Finds the index of the passed node
12593      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12594      * @return {Number} The index of the node or -1
12595      */
12596     indexOf : function(node){
12597         node = this.getNode(node);
12598         if(typeof node.nodeIndex == "number"){
12599             return node.nodeIndex;
12600         }
12601         var ns = this.nodes;
12602         for(var i = 0, len = ns.length; i < len; i++){
12603             if(ns[i] == node){
12604                 return i;
12605             }
12606         }
12607         return -1;
12608     }
12609 });
12610 /*
12611  * - LGPL
12612  *
12613  * based on jquery fullcalendar
12614  * 
12615  */
12616
12617 Roo.bootstrap = Roo.bootstrap || {};
12618 /**
12619  * @class Roo.bootstrap.Calendar
12620  * @extends Roo.bootstrap.Component
12621  * Bootstrap Calendar class
12622  * @cfg {Boolean} loadMask (true|false) default false
12623  * @cfg {Object} header generate the user specific header of the calendar, default false
12624
12625  * @constructor
12626  * Create a new Container
12627  * @param {Object} config The config object
12628  */
12629
12630
12631
12632 Roo.bootstrap.Calendar = function(config){
12633     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
12634      this.addEvents({
12635         /**
12636              * @event select
12637              * Fires when a date is selected
12638              * @param {DatePicker} this
12639              * @param {Date} date The selected date
12640              */
12641         'select': true,
12642         /**
12643              * @event monthchange
12644              * Fires when the displayed month changes 
12645              * @param {DatePicker} this
12646              * @param {Date} date The selected month
12647              */
12648         'monthchange': true,
12649         /**
12650              * @event evententer
12651              * Fires when mouse over an event
12652              * @param {Calendar} this
12653              * @param {event} Event
12654              */
12655         'evententer': true,
12656         /**
12657              * @event eventleave
12658              * Fires when the mouse leaves an
12659              * @param {Calendar} this
12660              * @param {event}
12661              */
12662         'eventleave': true,
12663         /**
12664              * @event eventclick
12665              * Fires when the mouse click an
12666              * @param {Calendar} this
12667              * @param {event}
12668              */
12669         'eventclick': true
12670         
12671     });
12672
12673 };
12674
12675 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
12676     
12677      /**
12678      * @cfg {Number} startDay
12679      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
12680      */
12681     startDay : 0,
12682     
12683     loadMask : false,
12684     
12685     header : false,
12686       
12687     getAutoCreate : function(){
12688         
12689         
12690         var fc_button = function(name, corner, style, content ) {
12691             return Roo.apply({},{
12692                 tag : 'span',
12693                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
12694                          (corner.length ?
12695                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
12696                             ''
12697                         ),
12698                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
12699                 unselectable: 'on'
12700             });
12701         };
12702         
12703         var header = {};
12704         
12705         if(!this.header){
12706             header = {
12707                 tag : 'table',
12708                 cls : 'fc-header',
12709                 style : 'width:100%',
12710                 cn : [
12711                     {
12712                         tag: 'tr',
12713                         cn : [
12714                             {
12715                                 tag : 'td',
12716                                 cls : 'fc-header-left',
12717                                 cn : [
12718                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
12719                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
12720                                     { tag: 'span', cls: 'fc-header-space' },
12721                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
12722
12723
12724                                 ]
12725                             },
12726
12727                             {
12728                                 tag : 'td',
12729                                 cls : 'fc-header-center',
12730                                 cn : [
12731                                     {
12732                                         tag: 'span',
12733                                         cls: 'fc-header-title',
12734                                         cn : {
12735                                             tag: 'H2',
12736                                             html : 'month / year'
12737                                         }
12738                                     }
12739
12740                                 ]
12741                             },
12742                             {
12743                                 tag : 'td',
12744                                 cls : 'fc-header-right',
12745                                 cn : [
12746                               /*      fc_button('month', 'left', '', 'month' ),
12747                                     fc_button('week', '', '', 'week' ),
12748                                     fc_button('day', 'right', '', 'day' )
12749                                 */    
12750
12751                                 ]
12752                             }
12753
12754                         ]
12755                     }
12756                 ]
12757             };
12758         }
12759         
12760         header = this.header;
12761         
12762        
12763         var cal_heads = function() {
12764             var ret = [];
12765             // fixme - handle this.
12766             
12767             for (var i =0; i < Date.dayNames.length; i++) {
12768                 var d = Date.dayNames[i];
12769                 ret.push({
12770                     tag: 'th',
12771                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
12772                     html : d.substring(0,3)
12773                 });
12774                 
12775             }
12776             ret[0].cls += ' fc-first';
12777             ret[6].cls += ' fc-last';
12778             return ret;
12779         };
12780         var cal_cell = function(n) {
12781             return  {
12782                 tag: 'td',
12783                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
12784                 cn : [
12785                     {
12786                         cn : [
12787                             {
12788                                 cls: 'fc-day-number',
12789                                 html: 'D'
12790                             },
12791                             {
12792                                 cls: 'fc-day-content',
12793                              
12794                                 cn : [
12795                                      {
12796                                         style: 'position: relative;' // height: 17px;
12797                                     }
12798                                 ]
12799                             }
12800                             
12801                             
12802                         ]
12803                     }
12804                 ]
12805                 
12806             }
12807         };
12808         var cal_rows = function() {
12809             
12810             var ret = []
12811             for (var r = 0; r < 6; r++) {
12812                 var row= {
12813                     tag : 'tr',
12814                     cls : 'fc-week',
12815                     cn : []
12816                 };
12817                 
12818                 for (var i =0; i < Date.dayNames.length; i++) {
12819                     var d = Date.dayNames[i];
12820                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
12821
12822                 }
12823                 row.cn[0].cls+=' fc-first';
12824                 row.cn[0].cn[0].style = 'min-height:90px';
12825                 row.cn[6].cls+=' fc-last';
12826                 ret.push(row);
12827                 
12828             }
12829             ret[0].cls += ' fc-first';
12830             ret[4].cls += ' fc-prev-last';
12831             ret[5].cls += ' fc-last';
12832             return ret;
12833             
12834         };
12835         
12836         var cal_table = {
12837             tag: 'table',
12838             cls: 'fc-border-separate',
12839             style : 'width:100%',
12840             cellspacing  : 0,
12841             cn : [
12842                 { 
12843                     tag: 'thead',
12844                     cn : [
12845                         { 
12846                             tag: 'tr',
12847                             cls : 'fc-first fc-last',
12848                             cn : cal_heads()
12849                         }
12850                     ]
12851                 },
12852                 { 
12853                     tag: 'tbody',
12854                     cn : cal_rows()
12855                 }
12856                   
12857             ]
12858         };
12859          
12860          var cfg = {
12861             cls : 'fc fc-ltr',
12862             cn : [
12863                 header,
12864                 {
12865                     cls : 'fc-content',
12866                     style : "position: relative;",
12867                     cn : [
12868                         {
12869                             cls : 'fc-view fc-view-month fc-grid',
12870                             style : 'position: relative',
12871                             unselectable : 'on',
12872                             cn : [
12873                                 {
12874                                     cls : 'fc-event-container',
12875                                     style : 'position:absolute;z-index:8;top:0;left:0;'
12876                                 },
12877                                 cal_table
12878                             ]
12879                         }
12880                     ]
12881     
12882                 }
12883            ] 
12884             
12885         };
12886         
12887          
12888         
12889         return cfg;
12890     },
12891     
12892     
12893     initEvents : function()
12894     {
12895         if(!this.store){
12896             throw "can not find store for calendar";
12897         }
12898         
12899         var mark = {
12900             tag: "div",
12901             cls:"x-dlg-mask",
12902             style: "text-align:center",
12903             cn: [
12904                 {
12905                     tag: "div",
12906                     style: "background-color:white;width:50%;margin:250 auto",
12907                     cn: [
12908                         {
12909                             tag: "img",
12910                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
12911                         },
12912                         {
12913                             tag: "span",
12914                             html: "Loading"
12915                         }
12916                         
12917                     ]
12918                 }
12919             ]
12920         }
12921         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
12922         
12923         var size = this.el.select('.fc-content', true).first().getSize();
12924         this.maskEl.setSize(size.width, size.height);
12925         this.maskEl.enableDisplayMode("block");
12926         if(!this.loadMask){
12927             this.maskEl.hide();
12928         }
12929         
12930         this.store = Roo.factory(this.store, Roo.data);
12931         this.store.on('load', this.onLoad, this);
12932         this.store.on('beforeload', this.onBeforeLoad, this);
12933         
12934         this.resize();
12935         
12936         this.cells = this.el.select('.fc-day',true);
12937         //Roo.log(this.cells);
12938         this.textNodes = this.el.query('.fc-day-number');
12939         this.cells.addClassOnOver('fc-state-hover');
12940         
12941         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
12942         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
12943         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
12944         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
12945         
12946         this.on('monthchange', this.onMonthChange, this);
12947         
12948         this.update(new Date().clearTime());
12949     },
12950     
12951     resize : function() {
12952         var sz  = this.el.getSize();
12953         
12954         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
12955         this.el.select('.fc-day-content div',true).setHeight(34);
12956     },
12957     
12958     
12959     // private
12960     showPrevMonth : function(e){
12961         this.update(this.activeDate.add("mo", -1));
12962     },
12963     showToday : function(e){
12964         this.update(new Date().clearTime());
12965     },
12966     // private
12967     showNextMonth : function(e){
12968         this.update(this.activeDate.add("mo", 1));
12969     },
12970
12971     // private
12972     showPrevYear : function(){
12973         this.update(this.activeDate.add("y", -1));
12974     },
12975
12976     // private
12977     showNextYear : function(){
12978         this.update(this.activeDate.add("y", 1));
12979     },
12980
12981     
12982    // private
12983     update : function(date)
12984     {
12985         var vd = this.activeDate;
12986         this.activeDate = date;
12987 //        if(vd && this.el){
12988 //            var t = date.getTime();
12989 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
12990 //                Roo.log('using add remove');
12991 //                
12992 //                this.fireEvent('monthchange', this, date);
12993 //                
12994 //                this.cells.removeClass("fc-state-highlight");
12995 //                this.cells.each(function(c){
12996 //                   if(c.dateValue == t){
12997 //                       c.addClass("fc-state-highlight");
12998 //                       setTimeout(function(){
12999 //                            try{c.dom.firstChild.focus();}catch(e){}
13000 //                       }, 50);
13001 //                       return false;
13002 //                   }
13003 //                   return true;
13004 //                });
13005 //                return;
13006 //            }
13007 //        }
13008         
13009         var days = date.getDaysInMonth();
13010         
13011         var firstOfMonth = date.getFirstDateOfMonth();
13012         var startingPos = firstOfMonth.getDay()-this.startDay;
13013         
13014         if(startingPos < this.startDay){
13015             startingPos += 7;
13016         }
13017         
13018         var pm = date.add(Date.MONTH, -1);
13019         var prevStart = pm.getDaysInMonth()-startingPos;
13020 //        
13021         this.cells = this.el.select('.fc-day',true);
13022         this.textNodes = this.el.query('.fc-day-number');
13023         this.cells.addClassOnOver('fc-state-hover');
13024         
13025         var cells = this.cells.elements;
13026         var textEls = this.textNodes;
13027         
13028         Roo.each(cells, function(cell){
13029             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
13030         });
13031         
13032         days += startingPos;
13033
13034         // convert everything to numbers so it's fast
13035         var day = 86400000;
13036         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
13037         //Roo.log(d);
13038         //Roo.log(pm);
13039         //Roo.log(prevStart);
13040         
13041         var today = new Date().clearTime().getTime();
13042         var sel = date.clearTime().getTime();
13043         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
13044         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
13045         var ddMatch = this.disabledDatesRE;
13046         var ddText = this.disabledDatesText;
13047         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
13048         var ddaysText = this.disabledDaysText;
13049         var format = this.format;
13050         
13051         var setCellClass = function(cal, cell){
13052             cell.row = 0;
13053             cell.events = [];
13054             cell.more = [];
13055             //Roo.log('set Cell Class');
13056             cell.title = "";
13057             var t = d.getTime();
13058             
13059             //Roo.log(d);
13060             
13061             cell.dateValue = t;
13062             if(t == today){
13063                 cell.className += " fc-today";
13064                 cell.className += " fc-state-highlight";
13065                 cell.title = cal.todayText;
13066             }
13067             if(t == sel){
13068                 // disable highlight in other month..
13069                 //cell.className += " fc-state-highlight";
13070                 
13071             }
13072             // disabling
13073             if(t < min) {
13074                 cell.className = " fc-state-disabled";
13075                 cell.title = cal.minText;
13076                 return;
13077             }
13078             if(t > max) {
13079                 cell.className = " fc-state-disabled";
13080                 cell.title = cal.maxText;
13081                 return;
13082             }
13083             if(ddays){
13084                 if(ddays.indexOf(d.getDay()) != -1){
13085                     cell.title = ddaysText;
13086                     cell.className = " fc-state-disabled";
13087                 }
13088             }
13089             if(ddMatch && format){
13090                 var fvalue = d.dateFormat(format);
13091                 if(ddMatch.test(fvalue)){
13092                     cell.title = ddText.replace("%0", fvalue);
13093                     cell.className = " fc-state-disabled";
13094                 }
13095             }
13096             
13097             if (!cell.initialClassName) {
13098                 cell.initialClassName = cell.dom.className;
13099             }
13100             
13101             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
13102         };
13103
13104         var i = 0;
13105         
13106         for(; i < startingPos; i++) {
13107             textEls[i].innerHTML = (++prevStart);
13108             d.setDate(d.getDate()+1);
13109             
13110             cells[i].className = "fc-past fc-other-month";
13111             setCellClass(this, cells[i]);
13112         }
13113         
13114         var intDay = 0;
13115         
13116         for(; i < days; i++){
13117             intDay = i - startingPos + 1;
13118             textEls[i].innerHTML = (intDay);
13119             d.setDate(d.getDate()+1);
13120             
13121             cells[i].className = ''; // "x-date-active";
13122             setCellClass(this, cells[i]);
13123         }
13124         var extraDays = 0;
13125         
13126         for(; i < 42; i++) {
13127             textEls[i].innerHTML = (++extraDays);
13128             d.setDate(d.getDate()+1);
13129             
13130             cells[i].className = "fc-future fc-other-month";
13131             setCellClass(this, cells[i]);
13132         }
13133         
13134         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
13135         
13136         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
13137         
13138         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
13139         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
13140         
13141         if(totalRows != 6){
13142             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
13143             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
13144         }
13145         
13146         this.fireEvent('monthchange', this, date);
13147         
13148         
13149         /*
13150         if(!this.internalRender){
13151             var main = this.el.dom.firstChild;
13152             var w = main.offsetWidth;
13153             this.el.setWidth(w + this.el.getBorderWidth("lr"));
13154             Roo.fly(main).setWidth(w);
13155             this.internalRender = true;
13156             // opera does not respect the auto grow header center column
13157             // then, after it gets a width opera refuses to recalculate
13158             // without a second pass
13159             if(Roo.isOpera && !this.secondPass){
13160                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
13161                 this.secondPass = true;
13162                 this.update.defer(10, this, [date]);
13163             }
13164         }
13165         */
13166         
13167     },
13168     
13169     findCell : function(dt) {
13170         dt = dt.clearTime().getTime();
13171         var ret = false;
13172         this.cells.each(function(c){
13173             //Roo.log("check " +c.dateValue + '?=' + dt);
13174             if(c.dateValue == dt){
13175                 ret = c;
13176                 return false;
13177             }
13178             return true;
13179         });
13180         
13181         return ret;
13182     },
13183     
13184     findCells : function(ev) {
13185         var s = ev.start.clone().clearTime().getTime();
13186        // Roo.log(s);
13187         var e= ev.end.clone().clearTime().getTime();
13188        // Roo.log(e);
13189         var ret = [];
13190         this.cells.each(function(c){
13191              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
13192             
13193             if(c.dateValue > e){
13194                 return ;
13195             }
13196             if(c.dateValue < s){
13197                 return ;
13198             }
13199             ret.push(c);
13200         });
13201         
13202         return ret;    
13203     },
13204     
13205 //    findBestRow: function(cells)
13206 //    {
13207 //        var ret = 0;
13208 //        
13209 //        for (var i =0 ; i < cells.length;i++) {
13210 //            ret  = Math.max(cells[i].rows || 0,ret);
13211 //        }
13212 //        return ret;
13213 //        
13214 //    },
13215     
13216     
13217     addItem : function(ev)
13218     {
13219         // look for vertical location slot in
13220         var cells = this.findCells(ev);
13221         
13222 //        ev.row = this.findBestRow(cells);
13223         
13224         // work out the location.
13225         
13226         var crow = false;
13227         var rows = [];
13228         for(var i =0; i < cells.length; i++) {
13229             
13230             cells[i].row = cells[0].row;
13231             
13232             if(i == 0){
13233                 cells[i].row = cells[i].row + 1;
13234             }
13235             
13236             if (!crow) {
13237                 crow = {
13238                     start : cells[i],
13239                     end :  cells[i]
13240                 };
13241                 continue;
13242             }
13243             if (crow.start.getY() == cells[i].getY()) {
13244                 // on same row.
13245                 crow.end = cells[i];
13246                 continue;
13247             }
13248             // different row.
13249             rows.push(crow);
13250             crow = {
13251                 start: cells[i],
13252                 end : cells[i]
13253             };
13254             
13255         }
13256         
13257         rows.push(crow);
13258         ev.els = [];
13259         ev.rows = rows;
13260         ev.cells = cells;
13261         
13262         cells[0].events.push(ev);
13263         
13264         this.calevents.push(ev);
13265     },
13266     
13267     clearEvents: function() {
13268         
13269         if(!this.calevents){
13270             return;
13271         }
13272         
13273         Roo.each(this.cells.elements, function(c){
13274             c.row = 0;
13275             c.events = [];
13276             c.more = [];
13277         });
13278         
13279         Roo.each(this.calevents, function(e) {
13280             Roo.each(e.els, function(el) {
13281                 el.un('mouseenter' ,this.onEventEnter, this);
13282                 el.un('mouseleave' ,this.onEventLeave, this);
13283                 el.remove();
13284             },this);
13285         },this);
13286         
13287         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
13288             e.remove();
13289         });
13290         
13291     },
13292     
13293     renderEvents: function()
13294     {   
13295         var _this = this;
13296         
13297         this.cells.each(function(c) {
13298             
13299             if(c.row < 5){
13300                 return;
13301             }
13302             
13303             var ev = c.events;
13304             
13305             var r = 4;
13306             if(c.row != c.events.length){
13307                 r = 4 - (4 - (c.row - c.events.length));
13308             }
13309             
13310             c.events = ev.slice(0, r);
13311             c.more = ev.slice(r);
13312             
13313             if(c.more.length && c.more.length == 1){
13314                 c.events.push(c.more.pop());
13315             }
13316             
13317             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
13318             
13319         });
13320             
13321         this.cells.each(function(c) {
13322             
13323             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
13324             
13325             
13326             for (var e = 0; e < c.events.length; e++){
13327                 var ev = c.events[e];
13328                 var rows = ev.rows;
13329                 
13330                 for(var i = 0; i < rows.length; i++) {
13331                 
13332                     // how many rows should it span..
13333
13334                     var  cfg = {
13335                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
13336                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
13337
13338                         unselectable : "on",
13339                         cn : [
13340                             {
13341                                 cls: 'fc-event-inner',
13342                                 cn : [
13343     //                                {
13344     //                                  tag:'span',
13345     //                                  cls: 'fc-event-time',
13346     //                                  html : cells.length > 1 ? '' : ev.time
13347     //                                },
13348                                     {
13349                                       tag:'span',
13350                                       cls: 'fc-event-title',
13351                                       html : String.format('{0}', ev.title)
13352                                     }
13353
13354
13355                                 ]
13356                             },
13357                             {
13358                                 cls: 'ui-resizable-handle ui-resizable-e',
13359                                 html : '&nbsp;&nbsp;&nbsp'
13360                             }
13361
13362                         ]
13363                     };
13364
13365                     if (i == 0) {
13366                         cfg.cls += ' fc-event-start';
13367                     }
13368                     if ((i+1) == rows.length) {
13369                         cfg.cls += ' fc-event-end';
13370                     }
13371
13372                     var ctr = _this.el.select('.fc-event-container',true).first();
13373                     var cg = ctr.createChild(cfg);
13374
13375                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
13376                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
13377
13378                     var r = (c.more.length) ? 1 : 0;
13379                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
13380                     cg.setWidth(ebox.right - sbox.x -2);
13381
13382                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
13383                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
13384                     cg.on('click', _this.onEventClick, _this, ev);
13385
13386                     ev.els.push(cg);
13387                     
13388                 }
13389                 
13390             }
13391             
13392             
13393             if(c.more.length){
13394                 var  cfg = {
13395                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
13396                     style : 'position: absolute',
13397                     unselectable : "on",
13398                     cn : [
13399                         {
13400                             cls: 'fc-event-inner',
13401                             cn : [
13402                                 {
13403                                   tag:'span',
13404                                   cls: 'fc-event-title',
13405                                   html : 'More'
13406                                 }
13407
13408
13409                             ]
13410                         },
13411                         {
13412                             cls: 'ui-resizable-handle ui-resizable-e',
13413                             html : '&nbsp;&nbsp;&nbsp'
13414                         }
13415
13416                     ]
13417                 };
13418
13419                 var ctr = _this.el.select('.fc-event-container',true).first();
13420                 var cg = ctr.createChild(cfg);
13421
13422                 var sbox = c.select('.fc-day-content',true).first().getBox();
13423                 var ebox = c.select('.fc-day-content',true).first().getBox();
13424                 //Roo.log(cg);
13425                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
13426                 cg.setWidth(ebox.right - sbox.x -2);
13427
13428                 cg.on('click', _this.onMoreEventClick, _this, c.more);
13429                 
13430             }
13431             
13432         });
13433         
13434         
13435         
13436     },
13437     
13438     onEventEnter: function (e, el,event,d) {
13439         this.fireEvent('evententer', this, el, event);
13440     },
13441     
13442     onEventLeave: function (e, el,event,d) {
13443         this.fireEvent('eventleave', this, el, event);
13444     },
13445     
13446     onEventClick: function (e, el,event,d) {
13447         this.fireEvent('eventclick', this, el, event);
13448     },
13449     
13450     onMonthChange: function () {
13451         this.store.load();
13452     },
13453     
13454     onMoreEventClick: function(e, el, more)
13455     {
13456         var _this = this;
13457         
13458         this.calpopover.placement = 'right';
13459         this.calpopover.setTitle('More');
13460         
13461         this.calpopover.setContent('');
13462         
13463         var ctr = this.calpopover.el.select('.popover-content', true).first();
13464         
13465         Roo.each(more, function(m){
13466             var cfg = {
13467                 cls : 'fc-event-hori fc-event-draggable',
13468                 html : m.title
13469             }
13470             var cg = ctr.createChild(cfg);
13471             
13472             cg.on('click', _this.onEventClick, _this, m);
13473         });
13474         
13475         this.calpopover.show(el);
13476         
13477         
13478     },
13479     
13480     onLoad: function () 
13481     {   
13482         this.calevents = [];
13483         var cal = this;
13484         
13485         if(this.store.getCount() > 0){
13486             this.store.data.each(function(d){
13487                cal.addItem({
13488                     id : d.data.id,
13489                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
13490                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
13491                     time : d.data.start_time,
13492                     title : d.data.title,
13493                     description : d.data.description,
13494                     venue : d.data.venue
13495                 });
13496             });
13497         }
13498         
13499         this.renderEvents();
13500         
13501         if(this.calevents.length && this.loadMask){
13502             this.maskEl.hide();
13503         }
13504     },
13505     
13506     onBeforeLoad: function()
13507     {
13508         this.clearEvents();
13509         if(this.loadMask){
13510             this.maskEl.show();
13511         }
13512     }
13513 });
13514
13515  
13516  /*
13517  * - LGPL
13518  *
13519  * element
13520  * 
13521  */
13522
13523 /**
13524  * @class Roo.bootstrap.Popover
13525  * @extends Roo.bootstrap.Component
13526  * Bootstrap Popover class
13527  * @cfg {String} html contents of the popover   (or false to use children..)
13528  * @cfg {String} title of popover (or false to hide)
13529  * @cfg {String} placement how it is placed
13530  * @cfg {String} trigger click || hover (or false to trigger manually)
13531  * @cfg {String} over what (parent or false to trigger manually.)
13532  * 
13533  * @constructor
13534  * Create a new Popover
13535  * @param {Object} config The config object
13536  */
13537
13538 Roo.bootstrap.Popover = function(config){
13539     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
13540 };
13541
13542 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
13543     
13544     title: 'Fill in a title',
13545     html: false,
13546     
13547     placement : 'right',
13548     trigger : 'hover', // hover
13549     
13550     over: 'parent',
13551     
13552     can_build_overlaid : false,
13553     
13554     getChildContainer : function()
13555     {
13556         return this.el.select('.popover-content',true).first();
13557     },
13558     
13559     getAutoCreate : function(){
13560          Roo.log('make popover?');
13561         var cfg = {
13562            cls : 'popover roo-dynamic',
13563            style: 'display:block',
13564            cn : [
13565                 {
13566                     cls : 'arrow'
13567                 },
13568                 {
13569                     cls : 'popover-inner',
13570                     cn : [
13571                         {
13572                             tag: 'h3',
13573                             cls: 'popover-title',
13574                             html : this.title
13575                         },
13576                         {
13577                             cls : 'popover-content',
13578                             html : this.html
13579                         }
13580                     ]
13581                     
13582                 }
13583            ]
13584         };
13585         
13586         return cfg;
13587     },
13588     setTitle: function(str)
13589     {
13590         this.el.select('.popover-title',true).first().dom.innerHTML = str;
13591     },
13592     setContent: function(str)
13593     {
13594         this.el.select('.popover-content',true).first().dom.innerHTML = str;
13595     },
13596     // as it get's added to the bottom of the page.
13597     onRender : function(ct, position)
13598     {
13599         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
13600         if(!this.el){
13601             var cfg = Roo.apply({},  this.getAutoCreate());
13602             cfg.id = Roo.id();
13603             
13604             if (this.cls) {
13605                 cfg.cls += ' ' + this.cls;
13606             }
13607             if (this.style) {
13608                 cfg.style = this.style;
13609             }
13610             Roo.log("adding to ")
13611             this.el = Roo.get(document.body).createChild(cfg, position);
13612             Roo.log(this.el);
13613         }
13614         this.initEvents();
13615     },
13616     
13617     initEvents : function()
13618     {
13619         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
13620         this.el.enableDisplayMode('block');
13621         this.el.hide();
13622         if (this.over === false) {
13623             return; 
13624         }
13625         if (this.triggers === false) {
13626             return;
13627         }
13628         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13629         var triggers = this.trigger ? this.trigger.split(' ') : [];
13630         Roo.each(triggers, function(trigger) {
13631         
13632             if (trigger == 'click') {
13633                 on_el.on('click', this.toggle, this);
13634             } else if (trigger != 'manual') {
13635                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
13636                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
13637       
13638                 on_el.on(eventIn  ,this.enter, this);
13639                 on_el.on(eventOut, this.leave, this);
13640             }
13641         }, this);
13642         
13643     },
13644     
13645     
13646     // private
13647     timeout : null,
13648     hoverState : null,
13649     
13650     toggle : function () {
13651         this.hoverState == 'in' ? this.leave() : this.enter();
13652     },
13653     
13654     enter : function () {
13655        
13656     
13657         clearTimeout(this.timeout);
13658     
13659         this.hoverState = 'in'
13660     
13661         if (!this.delay || !this.delay.show) {
13662             this.show();
13663             return 
13664         }
13665         var _t = this;
13666         this.timeout = setTimeout(function () {
13667             if (_t.hoverState == 'in') {
13668                 _t.show();
13669             }
13670         }, this.delay.show)
13671     },
13672     leave : function() {
13673         clearTimeout(this.timeout);
13674     
13675         this.hoverState = 'out'
13676     
13677         if (!this.delay || !this.delay.hide) {
13678             this.hide();
13679             return 
13680         }
13681         var _t = this;
13682         this.timeout = setTimeout(function () {
13683             if (_t.hoverState == 'out') {
13684                 _t.hide();
13685             }
13686         }, this.delay.hide)
13687     },
13688     
13689     show : function (on_el)
13690     {
13691         if (!on_el) {
13692             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13693         }
13694         // set content.
13695         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
13696         if (this.html !== false) {
13697             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
13698         }
13699         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
13700         if (!this.title.length) {
13701             this.el.select('.popover-title',true).hide();
13702         }
13703         
13704         var placement = typeof this.placement == 'function' ?
13705             this.placement.call(this, this.el, on_el) :
13706             this.placement;
13707             
13708         var autoToken = /\s?auto?\s?/i;
13709         var autoPlace = autoToken.test(placement);
13710         if (autoPlace) {
13711             placement = placement.replace(autoToken, '') || 'top';
13712         }
13713         
13714         //this.el.detach()
13715         //this.el.setXY([0,0]);
13716         this.el.show();
13717         this.el.dom.style.display='block';
13718         this.el.addClass(placement);
13719         
13720         //this.el.appendTo(on_el);
13721         
13722         var p = this.getPosition();
13723         var box = this.el.getBox();
13724         
13725         if (autoPlace) {
13726             // fixme..
13727         }
13728         var align = Roo.bootstrap.Popover.alignment[placement]
13729         this.el.alignTo(on_el, align[0],align[1]);
13730         //var arrow = this.el.select('.arrow',true).first();
13731         //arrow.set(align[2], 
13732         
13733         this.el.addClass('in');
13734         this.hoverState = null;
13735         
13736         if (this.el.hasClass('fade')) {
13737             // fade it?
13738         }
13739         
13740     },
13741     hide : function()
13742     {
13743         this.el.setXY([0,0]);
13744         this.el.removeClass('in');
13745         this.el.hide();
13746         
13747     }
13748     
13749 });
13750
13751 Roo.bootstrap.Popover.alignment = {
13752     'left' : ['r-l', [-10,0], 'right'],
13753     'right' : ['l-r', [10,0], 'left'],
13754     'bottom' : ['t-b', [0,10], 'top'],
13755     'top' : [ 'b-t', [0,-10], 'bottom']
13756 };
13757
13758  /*
13759  * - LGPL
13760  *
13761  * Progress
13762  * 
13763  */
13764
13765 /**
13766  * @class Roo.bootstrap.Progress
13767  * @extends Roo.bootstrap.Component
13768  * Bootstrap Progress class
13769  * @cfg {Boolean} striped striped of the progress bar
13770  * @cfg {Boolean} active animated of the progress bar
13771  * 
13772  * 
13773  * @constructor
13774  * Create a new Progress
13775  * @param {Object} config The config object
13776  */
13777
13778 Roo.bootstrap.Progress = function(config){
13779     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
13780 };
13781
13782 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
13783     
13784     striped : false,
13785     active: false,
13786     
13787     getAutoCreate : function(){
13788         var cfg = {
13789             tag: 'div',
13790             cls: 'progress'
13791         };
13792         
13793         
13794         if(this.striped){
13795             cfg.cls += ' progress-striped';
13796         }
13797       
13798         if(this.active){
13799             cfg.cls += ' active';
13800         }
13801         
13802         
13803         return cfg;
13804     }
13805    
13806 });
13807
13808  
13809
13810  /*
13811  * - LGPL
13812  *
13813  * ProgressBar
13814  * 
13815  */
13816
13817 /**
13818  * @class Roo.bootstrap.ProgressBar
13819  * @extends Roo.bootstrap.Component
13820  * Bootstrap ProgressBar class
13821  * @cfg {Number} aria_valuenow aria-value now
13822  * @cfg {Number} aria_valuemin aria-value min
13823  * @cfg {Number} aria_valuemax aria-value max
13824  * @cfg {String} label label for the progress bar
13825  * @cfg {String} panel (success | info | warning | danger )
13826  * @cfg {String} role role of the progress bar
13827  * @cfg {String} sr_only text
13828  * 
13829  * 
13830  * @constructor
13831  * Create a new ProgressBar
13832  * @param {Object} config The config object
13833  */
13834
13835 Roo.bootstrap.ProgressBar = function(config){
13836     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
13837 };
13838
13839 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
13840     
13841     aria_valuenow : 0,
13842     aria_valuemin : 0,
13843     aria_valuemax : 100,
13844     label : false,
13845     panel : false,
13846     role : false,
13847     sr_only: false,
13848     
13849     getAutoCreate : function()
13850     {
13851         
13852         var cfg = {
13853             tag: 'div',
13854             cls: 'progress-bar',
13855             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
13856         };
13857         
13858         if(this.sr_only){
13859             cfg.cn = {
13860                 tag: 'span',
13861                 cls: 'sr-only',
13862                 html: this.sr_only
13863             }
13864         }
13865         
13866         if(this.role){
13867             cfg.role = this.role;
13868         }
13869         
13870         if(this.aria_valuenow){
13871             cfg['aria-valuenow'] = this.aria_valuenow;
13872         }
13873         
13874         if(this.aria_valuemin){
13875             cfg['aria-valuemin'] = this.aria_valuemin;
13876         }
13877         
13878         if(this.aria_valuemax){
13879             cfg['aria-valuemax'] = this.aria_valuemax;
13880         }
13881         
13882         if(this.label && !this.sr_only){
13883             cfg.html = this.label;
13884         }
13885         
13886         if(this.panel){
13887             cfg.cls += ' progress-bar-' + this.panel;
13888         }
13889         
13890         return cfg;
13891     },
13892     
13893     update : function(aria_valuenow)
13894     {
13895         this.aria_valuenow = aria_valuenow;
13896         
13897         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
13898     }
13899    
13900 });
13901
13902  
13903
13904  /*
13905  * - LGPL
13906  *
13907  * column
13908  * 
13909  */
13910
13911 /**
13912  * @class Roo.bootstrap.TabGroup
13913  * @extends Roo.bootstrap.Column
13914  * Bootstrap Column class
13915  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
13916  * @cfg {Boolean} carousel true to make the group behave like a carousel
13917  * 
13918  * @constructor
13919  * Create a new TabGroup
13920  * @param {Object} config The config object
13921  */
13922
13923 Roo.bootstrap.TabGroup = function(config){
13924     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
13925     if (!this.navId) {
13926         this.navId = Roo.id();
13927     }
13928     this.tabs = [];
13929     Roo.bootstrap.TabGroup.register(this);
13930     
13931 };
13932
13933 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
13934     
13935     carousel : false,
13936     transition : false,
13937      
13938     getAutoCreate : function()
13939     {
13940         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
13941         
13942         cfg.cls += ' tab-content';
13943         
13944         if (this.carousel) {
13945             cfg.cls += ' carousel slide';
13946             cfg.cn = [{
13947                cls : 'carousel-inner'
13948             }]
13949         }
13950         
13951         
13952         return cfg;
13953     },
13954     getChildContainer : function()
13955     {
13956         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
13957     },
13958     
13959     /**
13960     * register a Navigation item
13961     * @param {Roo.bootstrap.NavItem} the navitem to add
13962     */
13963     register : function(item)
13964     {
13965         this.tabs.push( item);
13966         item.navId = this.navId; // not really needed..
13967     
13968     },
13969     
13970     getActivePanel : function()
13971     {
13972         var r = false;
13973         Roo.each(this.tabs, function(t) {
13974             if (t.active) {
13975                 r = t;
13976                 return false;
13977             }
13978             return null;
13979         });
13980         return r;
13981         
13982     },
13983     getPanelByName : function(n)
13984     {
13985         var r = false;
13986         Roo.each(this.tabs, function(t) {
13987             if (t.tabId == n) {
13988                 r = t;
13989                 return false;
13990             }
13991             return null;
13992         });
13993         return r;
13994     },
13995     indexOfPanel : function(p)
13996     {
13997         var r = false;
13998         Roo.each(this.tabs, function(t,i) {
13999             if (t.tabId == p.tabId) {
14000                 r = i;
14001                 return false;
14002             }
14003             return null;
14004         });
14005         return r;
14006     },
14007     /**
14008      * show a specific panel
14009      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
14010      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
14011      */
14012     showPanel : function (pan)
14013     {
14014         
14015         if (typeof(pan) == 'number') {
14016             pan = this.tabs[pan];
14017         }
14018         if (typeof(pan) == 'string') {
14019             pan = this.getPanelByName(pan);
14020         }
14021         if (pan.tabId == this.getActivePanel().tabId) {
14022             return true;
14023         }
14024         var cur = this.getActivePanel();
14025         
14026         if (false === cur.fireEvent('beforedeactivate')) {
14027             return false;
14028         }
14029         
14030         if (this.carousel) {
14031             this.transition = true;
14032             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
14033             var lr = dir == 'next' ? 'left' : 'right';
14034             pan.el.addClass(dir); // or prev
14035             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
14036             cur.el.addClass(lr); // or right
14037             pan.el.addClass(lr);
14038             
14039             var _this = this;
14040             cur.el.on('transitionend', function() {
14041                 Roo.log("trans end?");
14042                 
14043                 pan.el.removeClass([lr,dir]);
14044                 pan.setActive(true);
14045                 
14046                 cur.el.removeClass([lr]);
14047                 cur.setActive(false);
14048                 
14049                 _this.transition = false;
14050                 
14051             }, this, { single:  true } );
14052             return true;
14053         }
14054         
14055         cur.setActive(false);
14056         pan.setActive(true);
14057         return true;
14058         
14059     },
14060     showPanelNext : function()
14061     {
14062         var i = this.indexOfPanel(this.getActivePanel());
14063         if (i > this.tabs.length) {
14064             return;
14065         }
14066         this.showPanel(this.tabs[i+1]);
14067     },
14068     showPanelPrev : function()
14069     {
14070         var i = this.indexOfPanel(this.getActivePanel());
14071         if (i  < 1) {
14072             return;
14073         }
14074         this.showPanel(this.tabs[i-1]);
14075     }
14076     
14077     
14078   
14079 });
14080
14081  
14082
14083  
14084  
14085 Roo.apply(Roo.bootstrap.TabGroup, {
14086     
14087     groups: {},
14088      /**
14089     * register a Navigation Group
14090     * @param {Roo.bootstrap.NavGroup} the navgroup to add
14091     */
14092     register : function(navgrp)
14093     {
14094         this.groups[navgrp.navId] = navgrp;
14095         
14096     },
14097     /**
14098     * fetch a Navigation Group based on the navigation ID
14099     * if one does not exist , it will get created.
14100     * @param {string} the navgroup to add
14101     * @returns {Roo.bootstrap.NavGroup} the navgroup 
14102     */
14103     get: function(navId) {
14104         if (typeof(this.groups[navId]) == 'undefined') {
14105             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
14106         }
14107         return this.groups[navId] ;
14108     }
14109     
14110     
14111     
14112 });
14113
14114  /*
14115  * - LGPL
14116  *
14117  * TabPanel
14118  * 
14119  */
14120
14121 /**
14122  * @class Roo.bootstrap.TabPanel
14123  * @extends Roo.bootstrap.Component
14124  * Bootstrap TabPanel class
14125  * @cfg {Boolean} active panel active
14126  * @cfg {String} html panel content
14127  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
14128  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
14129  * 
14130  * 
14131  * @constructor
14132  * Create a new TabPanel
14133  * @param {Object} config The config object
14134  */
14135
14136 Roo.bootstrap.TabPanel = function(config){
14137     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
14138     this.addEvents({
14139         /**
14140              * @event changed
14141              * Fires when the active status changes
14142              * @param {Roo.bootstrap.TabPanel} this
14143              * @param {Boolean} state the new state
14144             
14145          */
14146         'changed': true,
14147         /**
14148              * @event beforedeactivate
14149              * Fires before a tab is de-activated - can be used to do validation on a form.
14150              * @param {Roo.bootstrap.TabPanel} this
14151              * @return {Boolean} false if there is an error
14152             
14153          */
14154         'beforedeactivate': true
14155      });
14156     
14157     this.tabId = this.tabId || Roo.id();
14158   
14159 };
14160
14161 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
14162     
14163     active: false,
14164     html: false,
14165     tabId: false,
14166     navId : false,
14167     
14168     getAutoCreate : function(){
14169         var cfg = {
14170             tag: 'div',
14171             // item is needed for carousel - not sure if it has any effect otherwise
14172             cls: 'tab-pane item',
14173             html: this.html || ''
14174         };
14175         
14176         if(this.active){
14177             cfg.cls += ' active';
14178         }
14179         
14180         if(this.tabId){
14181             cfg.tabId = this.tabId;
14182         }
14183         
14184         
14185         return cfg;
14186     },
14187     
14188     initEvents:  function()
14189     {
14190         Roo.log('-------- init events on tab panel ---------');
14191         
14192         var p = this.parent();
14193         this.navId = this.navId || p.navId;
14194         
14195         if (typeof(this.navId) != 'undefined') {
14196             // not really needed.. but just in case.. parent should be a NavGroup.
14197             var tg = Roo.bootstrap.TabGroup.get(this.navId);
14198             Roo.log(['register', tg, this]);
14199             tg.register(this);
14200         }
14201     },
14202     
14203     
14204     onRender : function(ct, position)
14205     {
14206        // Roo.log("Call onRender: " + this.xtype);
14207         
14208         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
14209         
14210         
14211         
14212         
14213         
14214     },
14215     
14216     setActive: function(state)
14217     {
14218         Roo.log("panel - set active " + this.tabId + "=" + state);
14219         
14220         this.active = state;
14221         if (!state) {
14222             this.el.removeClass('active');
14223             
14224         } else  if (!this.el.hasClass('active')) {
14225             this.el.addClass('active');
14226         }
14227         this.fireEvent('changed', this, state);
14228     }
14229     
14230     
14231 });
14232  
14233
14234  
14235
14236  /*
14237  * - LGPL
14238  *
14239  * DateField
14240  * 
14241  */
14242
14243 /**
14244  * @class Roo.bootstrap.DateField
14245  * @extends Roo.bootstrap.Input
14246  * Bootstrap DateField class
14247  * @cfg {Number} weekStart default 0
14248  * @cfg {Number} weekStart default 0
14249  * @cfg {Number} viewMode default empty, (months|years)
14250  * @cfg {Number} minViewMode default empty, (months|years)
14251  * @cfg {Number} startDate default -Infinity
14252  * @cfg {Number} endDate default Infinity
14253  * @cfg {Boolean} todayHighlight default false
14254  * @cfg {Boolean} todayBtn default false
14255  * @cfg {Boolean} calendarWeeks default false
14256  * @cfg {Object} daysOfWeekDisabled default empty
14257  * 
14258  * @cfg {Boolean} keyboardNavigation default true
14259  * @cfg {String} language default en
14260  * 
14261  * @constructor
14262  * Create a new DateField
14263  * @param {Object} config The config object
14264  */
14265
14266 Roo.bootstrap.DateField = function(config){
14267     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
14268      this.addEvents({
14269             /**
14270              * @event show
14271              * Fires when this field show.
14272              * @param {Roo.bootstrap.DateField} this
14273              * @param {Mixed} date The date value
14274              */
14275             show : true,
14276             /**
14277              * @event show
14278              * Fires when this field hide.
14279              * @param {Roo.bootstrap.DateField} this
14280              * @param {Mixed} date The date value
14281              */
14282             hide : true,
14283             /**
14284              * @event select
14285              * Fires when select a date.
14286              * @param {Roo.bootstrap.DateField} this
14287              * @param {Mixed} date The date value
14288              */
14289             select : true
14290         });
14291 };
14292
14293 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
14294     
14295     /**
14296      * @cfg {String} format
14297      * The default date format string which can be overriden for localization support.  The format must be
14298      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
14299      */
14300     format : "m/d/y",
14301     /**
14302      * @cfg {String} altFormats
14303      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
14304      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
14305      */
14306     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
14307     
14308     weekStart : 0,
14309     
14310     viewMode : '',
14311     
14312     minViewMode : '',
14313     
14314     todayHighlight : false,
14315     
14316     todayBtn: false,
14317     
14318     language: 'en',
14319     
14320     keyboardNavigation: true,
14321     
14322     calendarWeeks: false,
14323     
14324     startDate: -Infinity,
14325     
14326     endDate: Infinity,
14327     
14328     daysOfWeekDisabled: [],
14329     
14330     _events: [],
14331     
14332     UTCDate: function()
14333     {
14334         return new Date(Date.UTC.apply(Date, arguments));
14335     },
14336     
14337     UTCToday: function()
14338     {
14339         var today = new Date();
14340         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
14341     },
14342     
14343     getDate: function() {
14344             var d = this.getUTCDate();
14345             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
14346     },
14347     
14348     getUTCDate: function() {
14349             return this.date;
14350     },
14351     
14352     setDate: function(d) {
14353             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
14354     },
14355     
14356     setUTCDate: function(d) {
14357             this.date = d;
14358             this.setValue(this.formatDate(this.date));
14359     },
14360         
14361     onRender: function(ct, position)
14362     {
14363         
14364         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
14365         
14366         this.language = this.language || 'en';
14367         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
14368         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
14369         
14370         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
14371         this.format = this.format || 'm/d/y';
14372         this.isInline = false;
14373         this.isInput = true;
14374         this.component = this.el.select('.add-on', true).first() || false;
14375         this.component = (this.component && this.component.length === 0) ? false : this.component;
14376         this.hasInput = this.component && this.inputEL().length;
14377         
14378         if (typeof(this.minViewMode === 'string')) {
14379             switch (this.minViewMode) {
14380                 case 'months':
14381                     this.minViewMode = 1;
14382                     break;
14383                 case 'years':
14384                     this.minViewMode = 2;
14385                     break;
14386                 default:
14387                     this.minViewMode = 0;
14388                     break;
14389             }
14390         }
14391         
14392         if (typeof(this.viewMode === 'string')) {
14393             switch (this.viewMode) {
14394                 case 'months':
14395                     this.viewMode = 1;
14396                     break;
14397                 case 'years':
14398                     this.viewMode = 2;
14399                     break;
14400                 default:
14401                     this.viewMode = 0;
14402                     break;
14403             }
14404         }
14405                 
14406         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
14407         
14408 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
14409         
14410         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14411         
14412         this.picker().on('mousedown', this.onMousedown, this);
14413         this.picker().on('click', this.onClick, this);
14414         
14415         this.picker().addClass('datepicker-dropdown');
14416         
14417         this.startViewMode = this.viewMode;
14418         
14419         
14420         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
14421             if(!this.calendarWeeks){
14422                 v.remove();
14423                 return;
14424             };
14425             
14426             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
14427             v.attr('colspan', function(i, val){
14428                 return parseInt(val) + 1;
14429             });
14430         })
14431                         
14432         
14433         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
14434         
14435         this.setStartDate(this.startDate);
14436         this.setEndDate(this.endDate);
14437         
14438         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
14439         
14440         this.fillDow();
14441         this.fillMonths();
14442         this.update();
14443         this.showMode();
14444         
14445         if(this.isInline) {
14446             this.show();
14447         }
14448     },
14449     
14450     picker : function()
14451     {
14452         return this.pickerEl;
14453 //        return this.el.select('.datepicker', true).first();
14454     },
14455     
14456     fillDow: function()
14457     {
14458         var dowCnt = this.weekStart;
14459         
14460         var dow = {
14461             tag: 'tr',
14462             cn: [
14463                 
14464             ]
14465         };
14466         
14467         if(this.calendarWeeks){
14468             dow.cn.push({
14469                 tag: 'th',
14470                 cls: 'cw',
14471                 html: '&nbsp;'
14472             })
14473         }
14474         
14475         while (dowCnt < this.weekStart + 7) {
14476             dow.cn.push({
14477                 tag: 'th',
14478                 cls: 'dow',
14479                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
14480             });
14481         }
14482         
14483         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
14484     },
14485     
14486     fillMonths: function()
14487     {    
14488         var i = 0
14489         var months = this.picker().select('>.datepicker-months td', true).first();
14490         
14491         months.dom.innerHTML = '';
14492         
14493         while (i < 12) {
14494             var month = {
14495                 tag: 'span',
14496                 cls: 'month',
14497                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
14498             }
14499             
14500             months.createChild(month);
14501         }
14502         
14503     },
14504     
14505     update: function()
14506     {
14507         
14508         this.date = (typeof(this.date) === 'undefined' || !this.date.length) ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
14509         
14510         if (this.date < this.startDate) {
14511             this.viewDate = new Date(this.startDate);
14512         } else if (this.date > this.endDate) {
14513             this.viewDate = new Date(this.endDate);
14514         } else {
14515             this.viewDate = new Date(this.date);
14516         }
14517         
14518         this.fill();
14519     },
14520     
14521     fill: function() 
14522     {
14523         var d = new Date(this.viewDate),
14524                 year = d.getUTCFullYear(),
14525                 month = d.getUTCMonth(),
14526                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
14527                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
14528                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
14529                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
14530                 currentDate = this.date && this.date.valueOf(),
14531                 today = this.UTCToday();
14532         
14533         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
14534         
14535 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
14536         
14537 //        this.picker.select('>tfoot th.today').
14538 //                                              .text(dates[this.language].today)
14539 //                                              .toggle(this.todayBtn !== false);
14540     
14541         this.updateNavArrows();
14542         this.fillMonths();
14543                                                 
14544         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
14545         
14546         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
14547          
14548         prevMonth.setUTCDate(day);
14549         
14550         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
14551         
14552         var nextMonth = new Date(prevMonth);
14553         
14554         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
14555         
14556         nextMonth = nextMonth.valueOf();
14557         
14558         var fillMonths = false;
14559         
14560         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
14561         
14562         while(prevMonth.valueOf() < nextMonth) {
14563             var clsName = '';
14564             
14565             if (prevMonth.getUTCDay() === this.weekStart) {
14566                 if(fillMonths){
14567                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
14568                 }
14569                     
14570                 fillMonths = {
14571                     tag: 'tr',
14572                     cn: []
14573                 };
14574                 
14575                 if(this.calendarWeeks){
14576                     // ISO 8601: First week contains first thursday.
14577                     // ISO also states week starts on Monday, but we can be more abstract here.
14578                     var
14579                     // Start of current week: based on weekstart/current date
14580                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
14581                     // Thursday of this week
14582                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
14583                     // First Thursday of year, year from thursday
14584                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
14585                     // Calendar week: ms between thursdays, div ms per day, div 7 days
14586                     calWeek =  (th - yth) / 864e5 / 7 + 1;
14587                     
14588                     fillMonths.cn.push({
14589                         tag: 'td',
14590                         cls: 'cw',
14591                         html: calWeek
14592                     });
14593                 }
14594             }
14595             
14596             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
14597                 clsName += ' old';
14598             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
14599                 clsName += ' new';
14600             }
14601             if (this.todayHighlight &&
14602                 prevMonth.getUTCFullYear() == today.getFullYear() &&
14603                 prevMonth.getUTCMonth() == today.getMonth() &&
14604                 prevMonth.getUTCDate() == today.getDate()) {
14605                 clsName += ' today';
14606             }
14607             
14608             if (currentDate && prevMonth.valueOf() === currentDate) {
14609                 clsName += ' active';
14610             }
14611             
14612             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
14613                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
14614                     clsName += ' disabled';
14615             }
14616             
14617             fillMonths.cn.push({
14618                 tag: 'td',
14619                 cls: 'day ' + clsName,
14620                 html: prevMonth.getDate()
14621             })
14622             
14623             prevMonth.setDate(prevMonth.getDate()+1);
14624         }
14625           
14626         var currentYear = this.date && this.date.getUTCFullYear();
14627         var currentMonth = this.date && this.date.getUTCMonth();
14628         
14629         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
14630         
14631         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
14632             v.removeClass('active');
14633             
14634             if(currentYear === year && k === currentMonth){
14635                 v.addClass('active');
14636             }
14637             
14638             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
14639                 v.addClass('disabled');
14640             }
14641             
14642         });
14643         
14644         
14645         year = parseInt(year/10, 10) * 10;
14646         
14647         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
14648         
14649         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
14650         
14651         year -= 1;
14652         for (var i = -1; i < 11; i++) {
14653             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
14654                 tag: 'span',
14655                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
14656                 html: year
14657             })
14658             
14659             year += 1;
14660         }
14661     },
14662     
14663     showMode: function(dir) 
14664     {
14665         if (dir) {
14666             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
14667         }
14668         Roo.each(this.picker().select('>div',true).elements, function(v){
14669             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14670             v.hide();
14671         });
14672         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
14673     },
14674     
14675     place: function()
14676     {
14677         if(this.isInline) return;
14678         
14679         this.picker().removeClass(['bottom', 'top']);
14680         
14681         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
14682             /*
14683              * place to the top of element!
14684              *
14685              */
14686             
14687             this.picker().addClass('top');
14688             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
14689             
14690             return;
14691         }
14692         
14693         this.picker().addClass('bottom');
14694         
14695         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
14696     },
14697     
14698     parseDate : function(value)
14699     {
14700         if(!value || value instanceof Date){
14701             return value;
14702         }
14703         var v = Date.parseDate(value, this.format);
14704         if (!v && this.useIso) {
14705             v = Date.parseDate(value, 'Y-m-d');
14706         }
14707         if(!v && this.altFormats){
14708             if(!this.altFormatsArray){
14709                 this.altFormatsArray = this.altFormats.split("|");
14710             }
14711             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
14712                 v = Date.parseDate(value, this.altFormatsArray[i]);
14713             }
14714         }
14715         return v;
14716     },
14717     
14718     formatDate : function(date, fmt)
14719     {
14720         return (!date || !(date instanceof Date)) ?
14721         date : date.dateFormat(fmt || this.format);
14722     },
14723     
14724     onFocus : function()
14725     {
14726         Roo.bootstrap.DateField.superclass.onFocus.call(this);
14727         this.show();
14728     },
14729     
14730     onBlur : function()
14731     {
14732         Roo.bootstrap.DateField.superclass.onBlur.call(this);
14733         
14734         var d = this.inputEl().getValue();
14735         
14736         this.setValue(d);
14737                 
14738         this.hide();
14739     },
14740     
14741     show : function()
14742     {
14743         this.picker().show();
14744         this.update();
14745         this.place();
14746         
14747         this.fireEvent('show', this, this.date);
14748     },
14749     
14750     hide : function()
14751     {
14752         if(this.isInline) return;
14753         this.picker().hide();
14754         this.viewMode = this.startViewMode;
14755         this.showMode();
14756         
14757         this.fireEvent('hide', this, this.date);
14758         
14759     },
14760     
14761     onMousedown: function(e)
14762     {
14763         e.stopPropagation();
14764         e.preventDefault();
14765     },
14766     
14767     keyup: function(e)
14768     {
14769         Roo.bootstrap.DateField.superclass.keyup.call(this);
14770         this.update();
14771     },
14772
14773     setValue: function(v)
14774     {
14775         var d = new Date(v);
14776         
14777         if(isNaN(d.getTime())){
14778             this.date = '';
14779             Roo.bootstrap.DateField.superclass.setValue.call(this, '');
14780             return;
14781         }
14782         
14783         v = this.formatDate(d);
14784         
14785         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
14786         
14787         this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
14788
14789         this.update();
14790
14791         this.fireEvent('select', this, this.date);
14792         
14793     },
14794     
14795     getValue: function()
14796     {
14797         return this.formatDate(this.date);
14798     },
14799     
14800     fireKey: function(e)
14801     {
14802         if (!this.picker().isVisible()){
14803             if (e.keyCode == 27) // allow escape to hide and re-show picker
14804                 this.show();
14805             return;
14806         }
14807         
14808         var dateChanged = false,
14809         dir, day, month,
14810         newDate, newViewDate;
14811         
14812         switch(e.keyCode){
14813             case 27: // escape
14814                 this.hide();
14815                 e.preventDefault();
14816                 break;
14817             case 37: // left
14818             case 39: // right
14819                 if (!this.keyboardNavigation) break;
14820                 dir = e.keyCode == 37 ? -1 : 1;
14821                 
14822                 if (e.ctrlKey){
14823                     newDate = this.moveYear(this.date, dir);
14824                     newViewDate = this.moveYear(this.viewDate, dir);
14825                 } else if (e.shiftKey){
14826                     newDate = this.moveMonth(this.date, dir);
14827                     newViewDate = this.moveMonth(this.viewDate, dir);
14828                 } else {
14829                     newDate = new Date(this.date);
14830                     newDate.setUTCDate(this.date.getUTCDate() + dir);
14831                     newViewDate = new Date(this.viewDate);
14832                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
14833                 }
14834                 if (this.dateWithinRange(newDate)){
14835                     this.date = newDate;
14836                     this.viewDate = newViewDate;
14837                     this.setValue(this.formatDate(this.date));
14838 //                    this.update();
14839                     e.preventDefault();
14840                     dateChanged = true;
14841                 }
14842                 break;
14843             case 38: // up
14844             case 40: // down
14845                 if (!this.keyboardNavigation) break;
14846                 dir = e.keyCode == 38 ? -1 : 1;
14847                 if (e.ctrlKey){
14848                     newDate = this.moveYear(this.date, dir);
14849                     newViewDate = this.moveYear(this.viewDate, dir);
14850                 } else if (e.shiftKey){
14851                     newDate = this.moveMonth(this.date, dir);
14852                     newViewDate = this.moveMonth(this.viewDate, dir);
14853                 } else {
14854                     newDate = new Date(this.date);
14855                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
14856                     newViewDate = new Date(this.viewDate);
14857                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
14858                 }
14859                 if (this.dateWithinRange(newDate)){
14860                     this.date = newDate;
14861                     this.viewDate = newViewDate;
14862                     this.setValue(this.formatDate(this.date));
14863 //                    this.update();
14864                     e.preventDefault();
14865                     dateChanged = true;
14866                 }
14867                 break;
14868             case 13: // enter
14869                 this.setValue(this.formatDate(this.date));
14870                 this.hide();
14871                 e.preventDefault();
14872                 break;
14873             case 9: // tab
14874                 this.setValue(this.formatDate(this.date));
14875                 this.hide();
14876                 break;
14877             case 16: // shift
14878             case 17: // ctrl
14879             case 18: // alt
14880                 break;
14881             default :
14882                 this.hide();
14883                 
14884         }
14885     },
14886     
14887     
14888     onClick: function(e) 
14889     {
14890         e.stopPropagation();
14891         e.preventDefault();
14892         
14893         var target = e.getTarget();
14894         
14895         if(target.nodeName.toLowerCase() === 'i'){
14896             target = Roo.get(target).dom.parentNode;
14897         }
14898         
14899         var nodeName = target.nodeName;
14900         var className = target.className;
14901         var html = target.innerHTML;
14902         
14903         switch(nodeName.toLowerCase()) {
14904             case 'th':
14905                 switch(className) {
14906                     case 'switch':
14907                         this.showMode(1);
14908                         break;
14909                     case 'prev':
14910                     case 'next':
14911                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
14912                         switch(this.viewMode){
14913                                 case 0:
14914                                         this.viewDate = this.moveMonth(this.viewDate, dir);
14915                                         break;
14916                                 case 1:
14917                                 case 2:
14918                                         this.viewDate = this.moveYear(this.viewDate, dir);
14919                                         break;
14920                         }
14921                         this.fill();
14922                         break;
14923                     case 'today':
14924                         var date = new Date();
14925                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
14926 //                        this.fill()
14927                         this.setValue(this.formatDate(this.date));
14928                         
14929                         this.hide();
14930                         break;
14931                 }
14932                 break;
14933             case 'span':
14934                 if (className.indexOf('disabled') === -1) {
14935                     this.viewDate.setUTCDate(1);
14936                     if (className.indexOf('month') !== -1) {
14937                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
14938                     } else {
14939                         var year = parseInt(html, 10) || 0;
14940                         this.viewDate.setUTCFullYear(year);
14941                         
14942                     }
14943                     this.showMode(-1);
14944                     this.fill();
14945                 }
14946                 break;
14947                 
14948             case 'td':
14949                 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
14950                     var day = parseInt(html, 10) || 1;
14951                     var year = this.viewDate.getUTCFullYear(),
14952                         month = this.viewDate.getUTCMonth();
14953
14954                     if (className.indexOf('old') !== -1) {
14955                         if(month === 0 ){
14956                             month = 11;
14957                             year -= 1;
14958                         }else{
14959                             month -= 1;
14960                         }
14961                     } else if (className.indexOf('new') !== -1) {
14962                         if (month == 11) {
14963                             month = 0;
14964                             year += 1;
14965                         } else {
14966                             month += 1;
14967                         }
14968                     }
14969                     this.date = this.UTCDate(year, month, day,0,0,0,0);
14970                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
14971 //                    this.fill();
14972                     this.setValue(this.formatDate(this.date));
14973                     this.hide();
14974                 }
14975                 break;
14976         }
14977     },
14978     
14979     setStartDate: function(startDate)
14980     {
14981         this.startDate = startDate || -Infinity;
14982         if (this.startDate !== -Infinity) {
14983             this.startDate = this.parseDate(this.startDate);
14984         }
14985         this.update();
14986         this.updateNavArrows();
14987     },
14988
14989     setEndDate: function(endDate)
14990     {
14991         this.endDate = endDate || Infinity;
14992         if (this.endDate !== Infinity) {
14993             this.endDate = this.parseDate(this.endDate);
14994         }
14995         this.update();
14996         this.updateNavArrows();
14997     },
14998     
14999     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
15000     {
15001         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
15002         if (typeof(this.daysOfWeekDisabled) !== 'object') {
15003             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
15004         }
15005         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
15006             return parseInt(d, 10);
15007         });
15008         this.update();
15009         this.updateNavArrows();
15010     },
15011     
15012     updateNavArrows: function() 
15013     {
15014         var d = new Date(this.viewDate),
15015         year = d.getUTCFullYear(),
15016         month = d.getUTCMonth();
15017         
15018         Roo.each(this.picker().select('.prev', true).elements, function(v){
15019             v.show();
15020             switch (this.viewMode) {
15021                 case 0:
15022
15023                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
15024                         v.hide();
15025                     }
15026                     break;
15027                 case 1:
15028                 case 2:
15029                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
15030                         v.hide();
15031                     }
15032                     break;
15033             }
15034         });
15035         
15036         Roo.each(this.picker().select('.next', true).elements, function(v){
15037             v.show();
15038             switch (this.viewMode) {
15039                 case 0:
15040
15041                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
15042                         v.hide();
15043                     }
15044                     break;
15045                 case 1:
15046                 case 2:
15047                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
15048                         v.hide();
15049                     }
15050                     break;
15051             }
15052         })
15053     },
15054     
15055     moveMonth: function(date, dir)
15056     {
15057         if (!dir) return date;
15058         var new_date = new Date(date.valueOf()),
15059         day = new_date.getUTCDate(),
15060         month = new_date.getUTCMonth(),
15061         mag = Math.abs(dir),
15062         new_month, test;
15063         dir = dir > 0 ? 1 : -1;
15064         if (mag == 1){
15065             test = dir == -1
15066             // If going back one month, make sure month is not current month
15067             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
15068             ? function(){
15069                 return new_date.getUTCMonth() == month;
15070             }
15071             // If going forward one month, make sure month is as expected
15072             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
15073             : function(){
15074                 return new_date.getUTCMonth() != new_month;
15075             };
15076             new_month = month + dir;
15077             new_date.setUTCMonth(new_month);
15078             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
15079             if (new_month < 0 || new_month > 11)
15080                 new_month = (new_month + 12) % 12;
15081         } else {
15082             // For magnitudes >1, move one month at a time...
15083             for (var i=0; i<mag; i++)
15084                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
15085                 new_date = this.moveMonth(new_date, dir);
15086             // ...then reset the day, keeping it in the new month
15087             new_month = new_date.getUTCMonth();
15088             new_date.setUTCDate(day);
15089             test = function(){
15090                 return new_month != new_date.getUTCMonth();
15091             };
15092         }
15093         // Common date-resetting loop -- if date is beyond end of month, make it
15094         // end of month
15095         while (test()){
15096             new_date.setUTCDate(--day);
15097             new_date.setUTCMonth(new_month);
15098         }
15099         return new_date;
15100     },
15101
15102     moveYear: function(date, dir)
15103     {
15104         return this.moveMonth(date, dir*12);
15105     },
15106
15107     dateWithinRange: function(date)
15108     {
15109         return date >= this.startDate && date <= this.endDate;
15110     },
15111
15112     
15113     remove: function() 
15114     {
15115         this.picker().remove();
15116     }
15117    
15118 });
15119
15120 Roo.apply(Roo.bootstrap.DateField,  {
15121     
15122     head : {
15123         tag: 'thead',
15124         cn: [
15125         {
15126             tag: 'tr',
15127             cn: [
15128             {
15129                 tag: 'th',
15130                 cls: 'prev',
15131                 html: '<i class="fa fa-arrow-left"/>'
15132             },
15133             {
15134                 tag: 'th',
15135                 cls: 'switch',
15136                 colspan: '5'
15137             },
15138             {
15139                 tag: 'th',
15140                 cls: 'next',
15141                 html: '<i class="fa fa-arrow-right"/>'
15142             }
15143
15144             ]
15145         }
15146         ]
15147     },
15148     
15149     content : {
15150         tag: 'tbody',
15151         cn: [
15152         {
15153             tag: 'tr',
15154             cn: [
15155             {
15156                 tag: 'td',
15157                 colspan: '7'
15158             }
15159             ]
15160         }
15161         ]
15162     },
15163     
15164     footer : {
15165         tag: 'tfoot',
15166         cn: [
15167         {
15168             tag: 'tr',
15169             cn: [
15170             {
15171                 tag: 'th',
15172                 colspan: '7',
15173                 cls: 'today'
15174             }
15175                     
15176             ]
15177         }
15178         ]
15179     },
15180     
15181     dates:{
15182         en: {
15183             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
15184             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
15185             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
15186             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
15187             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
15188             today: "Today"
15189         }
15190     },
15191     
15192     modes: [
15193     {
15194         clsName: 'days',
15195         navFnc: 'Month',
15196         navStep: 1
15197     },
15198     {
15199         clsName: 'months',
15200         navFnc: 'FullYear',
15201         navStep: 1
15202     },
15203     {
15204         clsName: 'years',
15205         navFnc: 'FullYear',
15206         navStep: 10
15207     }]
15208 });
15209
15210 Roo.apply(Roo.bootstrap.DateField,  {
15211   
15212     template : {
15213         tag: 'div',
15214         cls: 'datepicker dropdown-menu',
15215         cn: [
15216         {
15217             tag: 'div',
15218             cls: 'datepicker-days',
15219             cn: [
15220             {
15221                 tag: 'table',
15222                 cls: 'table-condensed',
15223                 cn:[
15224                 Roo.bootstrap.DateField.head,
15225                 {
15226                     tag: 'tbody'
15227                 },
15228                 Roo.bootstrap.DateField.footer
15229                 ]
15230             }
15231             ]
15232         },
15233         {
15234             tag: 'div',
15235             cls: 'datepicker-months',
15236             cn: [
15237             {
15238                 tag: 'table',
15239                 cls: 'table-condensed',
15240                 cn:[
15241                 Roo.bootstrap.DateField.head,
15242                 Roo.bootstrap.DateField.content,
15243                 Roo.bootstrap.DateField.footer
15244                 ]
15245             }
15246             ]
15247         },
15248         {
15249             tag: 'div',
15250             cls: 'datepicker-years',
15251             cn: [
15252             {
15253                 tag: 'table',
15254                 cls: 'table-condensed',
15255                 cn:[
15256                 Roo.bootstrap.DateField.head,
15257                 Roo.bootstrap.DateField.content,
15258                 Roo.bootstrap.DateField.footer
15259                 ]
15260             }
15261             ]
15262         }
15263         ]
15264     }
15265 });
15266
15267  
15268
15269  /*
15270  * - LGPL
15271  *
15272  * TimeField
15273  * 
15274  */
15275
15276 /**
15277  * @class Roo.bootstrap.TimeField
15278  * @extends Roo.bootstrap.Input
15279  * Bootstrap DateField class
15280  * 
15281  * 
15282  * @constructor
15283  * Create a new TimeField
15284  * @param {Object} config The config object
15285  */
15286
15287 Roo.bootstrap.TimeField = function(config){
15288     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
15289     this.addEvents({
15290             /**
15291              * @event show
15292              * Fires when this field show.
15293              * @param {Roo.bootstrap.DateField} this
15294              * @param {Mixed} date The date value
15295              */
15296             show : true,
15297             /**
15298              * @event show
15299              * Fires when this field hide.
15300              * @param {Roo.bootstrap.DateField} this
15301              * @param {Mixed} date The date value
15302              */
15303             hide : true,
15304             /**
15305              * @event select
15306              * Fires when select a date.
15307              * @param {Roo.bootstrap.DateField} this
15308              * @param {Mixed} date The date value
15309              */
15310             select : true
15311         });
15312 };
15313
15314 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
15315     
15316     /**
15317      * @cfg {String} format
15318      * The default time format string which can be overriden for localization support.  The format must be
15319      * valid according to {@link Date#parseDate} (defaults to 'H:i').
15320      */
15321     format : "H:i",
15322        
15323     onRender: function(ct, position)
15324     {
15325         
15326         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
15327                 
15328         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
15329         
15330         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15331         
15332         this.pop = this.picker().select('>.datepicker-time',true).first();
15333         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block' 
15334         
15335         this.picker().on('mousedown', this.onMousedown, this);
15336         this.picker().on('click', this.onClick, this);
15337         
15338         this.picker().addClass('datepicker-dropdown');
15339     
15340         this.fillTime();
15341         this.update();
15342             
15343         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
15344         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
15345         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
15346         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
15347         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
15348         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
15349
15350     },
15351     
15352     fireKey: function(e){
15353         if (!this.picker().isVisible()){
15354             if (e.keyCode == 27) // allow escape to hide and re-show picker
15355                 this.show();
15356             return;
15357         }
15358
15359         e.preventDefault();
15360         
15361         switch(e.keyCode){
15362             case 27: // escape
15363                 this.hide();
15364                 break;
15365             case 37: // left
15366             case 39: // right
15367                 this.onTogglePeriod();
15368                 break;
15369             case 38: // up
15370                 this.onIncrementMinutes();
15371                 break;
15372             case 40: // down
15373                 this.onDecrementMinutes();
15374                 break;
15375             case 13: // enter
15376             case 9: // tab
15377                 this.setTime();
15378                 break;
15379         }
15380     },
15381     
15382     onClick: function(e) {
15383         e.stopPropagation();
15384         e.preventDefault();
15385     },
15386     
15387     picker : function()
15388     {
15389         return this.el.select('.datepicker', true).first();
15390     },
15391     
15392     fillTime: function()
15393     {    
15394         var time = this.pop.select('tbody', true).first();
15395         
15396         time.dom.innerHTML = '';
15397         
15398         time.createChild({
15399             tag: 'tr',
15400             cn: [
15401                 {
15402                     tag: 'td',
15403                     cn: [
15404                         {
15405                             tag: 'a',
15406                             href: '#',
15407                             cls: 'btn',
15408                             cn: [
15409                                 {
15410                                     tag: 'span',
15411                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
15412                                 }
15413                             ]
15414                         } 
15415                     ]
15416                 },
15417                 {
15418                     tag: 'td',
15419                     cls: 'separator'
15420                 },
15421                 {
15422                     tag: 'td',
15423                     cn: [
15424                         {
15425                             tag: 'a',
15426                             href: '#',
15427                             cls: 'btn',
15428                             cn: [
15429                                 {
15430                                     tag: 'span',
15431                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
15432                                 }
15433                             ]
15434                         }
15435                     ]
15436                 },
15437                 {
15438                     tag: 'td',
15439                     cls: 'separator'
15440                 }
15441             ]
15442         });
15443         
15444         time.createChild({
15445             tag: 'tr',
15446             cn: [
15447                 {
15448                     tag: 'td',
15449                     cn: [
15450                         {
15451                             tag: 'span',
15452                             cls: 'timepicker-hour',
15453                             html: '00'
15454                         }  
15455                     ]
15456                 },
15457                 {
15458                     tag: 'td',
15459                     cls: 'separator',
15460                     html: ':'
15461                 },
15462                 {
15463                     tag: 'td',
15464                     cn: [
15465                         {
15466                             tag: 'span',
15467                             cls: 'timepicker-minute',
15468                             html: '00'
15469                         }  
15470                     ]
15471                 },
15472                 {
15473                     tag: 'td',
15474                     cls: 'separator'
15475                 },
15476                 {
15477                     tag: 'td',
15478                     cn: [
15479                         {
15480                             tag: 'button',
15481                             type: 'button',
15482                             cls: 'btn btn-primary period',
15483                             html: 'AM'
15484                             
15485                         }
15486                     ]
15487                 }
15488             ]
15489         });
15490         
15491         time.createChild({
15492             tag: 'tr',
15493             cn: [
15494                 {
15495                     tag: 'td',
15496                     cn: [
15497                         {
15498                             tag: 'a',
15499                             href: '#',
15500                             cls: 'btn',
15501                             cn: [
15502                                 {
15503                                     tag: 'span',
15504                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
15505                                 }
15506                             ]
15507                         }
15508                     ]
15509                 },
15510                 {
15511                     tag: 'td',
15512                     cls: 'separator'
15513                 },
15514                 {
15515                     tag: 'td',
15516                     cn: [
15517                         {
15518                             tag: 'a',
15519                             href: '#',
15520                             cls: 'btn',
15521                             cn: [
15522                                 {
15523                                     tag: 'span',
15524                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
15525                                 }
15526                             ]
15527                         }
15528                     ]
15529                 },
15530                 {
15531                     tag: 'td',
15532                     cls: 'separator'
15533                 }
15534             ]
15535         });
15536         
15537     },
15538     
15539     update: function()
15540     {
15541         
15542         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
15543         
15544         this.fill();
15545     },
15546     
15547     fill: function() 
15548     {
15549         var hours = this.time.getHours();
15550         var minutes = this.time.getMinutes();
15551         var period = 'AM';
15552         
15553         if(hours > 11){
15554             period = 'PM';
15555         }
15556         
15557         if(hours == 0){
15558             hours = 12;
15559         }
15560         
15561         
15562         if(hours > 12){
15563             hours = hours - 12;
15564         }
15565         
15566         if(hours < 10){
15567             hours = '0' + hours;
15568         }
15569         
15570         if(minutes < 10){
15571             minutes = '0' + minutes;
15572         }
15573         
15574         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
15575         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
15576         this.pop.select('button', true).first().dom.innerHTML = period;
15577         
15578     },
15579     
15580     place: function()
15581     {   
15582         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
15583         
15584         var cls = ['bottom'];
15585         
15586         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
15587             cls.pop();
15588             cls.push('top');
15589         }
15590         
15591         cls.push('right');
15592         
15593         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
15594             cls.pop();
15595             cls.push('left');
15596         }
15597         
15598         this.picker().addClass(cls.join('-'));
15599         
15600         var _this = this;
15601         
15602         Roo.each(cls, function(c){
15603             if(c == 'bottom'){
15604                 _this.picker().setTop(_this.inputEl().getHeight());
15605                 return;
15606             }
15607             if(c == 'top'){
15608                 _this.picker().setTop(0 - _this.picker().getHeight());
15609                 return;
15610             }
15611             
15612             if(c == 'left'){
15613                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
15614                 return;
15615             }
15616             if(c == 'right'){
15617                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
15618                 return;
15619             }
15620         });
15621         
15622     },
15623   
15624     onFocus : function()
15625     {
15626         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
15627         this.show();
15628     },
15629     
15630     onBlur : function()
15631     {
15632         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
15633         this.hide();
15634     },
15635     
15636     show : function()
15637     {
15638         this.picker().show();
15639         this.pop.show();
15640         this.update();
15641         this.place();
15642         
15643         this.fireEvent('show', this, this.date);
15644     },
15645     
15646     hide : function()
15647     {
15648         this.picker().hide();
15649         this.pop.hide();
15650         
15651         this.fireEvent('hide', this, this.date);
15652     },
15653     
15654     setTime : function()
15655     {
15656         this.hide();
15657         this.setValue(this.time.format(this.format));
15658         
15659         this.fireEvent('select', this, this.date);
15660         
15661         
15662     },
15663     
15664     onMousedown: function(e){
15665         e.stopPropagation();
15666         e.preventDefault();
15667     },
15668     
15669     onIncrementHours: function()
15670     {
15671         Roo.log('onIncrementHours');
15672         this.time = this.time.add(Date.HOUR, 1);
15673         this.update();
15674         
15675     },
15676     
15677     onDecrementHours: function()
15678     {
15679         Roo.log('onDecrementHours');
15680         this.time = this.time.add(Date.HOUR, -1);
15681         this.update();
15682     },
15683     
15684     onIncrementMinutes: function()
15685     {
15686         Roo.log('onIncrementMinutes');
15687         this.time = this.time.add(Date.MINUTE, 1);
15688         this.update();
15689     },
15690     
15691     onDecrementMinutes: function()
15692     {
15693         Roo.log('onDecrementMinutes');
15694         this.time = this.time.add(Date.MINUTE, -1);
15695         this.update();
15696     },
15697     
15698     onTogglePeriod: function()
15699     {
15700         Roo.log('onTogglePeriod');
15701         this.time = this.time.add(Date.HOUR, 12);
15702         this.update();
15703     }
15704     
15705    
15706 });
15707
15708 Roo.apply(Roo.bootstrap.TimeField,  {
15709     
15710     content : {
15711         tag: 'tbody',
15712         cn: [
15713             {
15714                 tag: 'tr',
15715                 cn: [
15716                 {
15717                     tag: 'td',
15718                     colspan: '7'
15719                 }
15720                 ]
15721             }
15722         ]
15723     },
15724     
15725     footer : {
15726         tag: 'tfoot',
15727         cn: [
15728             {
15729                 tag: 'tr',
15730                 cn: [
15731                 {
15732                     tag: 'th',
15733                     colspan: '7',
15734                     cls: '',
15735                     cn: [
15736                         {
15737                             tag: 'button',
15738                             cls: 'btn btn-info ok',
15739                             html: 'OK'
15740                         }
15741                     ]
15742                 }
15743
15744                 ]
15745             }
15746         ]
15747     }
15748 });
15749
15750 Roo.apply(Roo.bootstrap.TimeField,  {
15751   
15752     template : {
15753         tag: 'div',
15754         cls: 'datepicker dropdown-menu',
15755         cn: [
15756             {
15757                 tag: 'div',
15758                 cls: 'datepicker-time',
15759                 cn: [
15760                 {
15761                     tag: 'table',
15762                     cls: 'table-condensed',
15763                     cn:[
15764                     Roo.bootstrap.TimeField.content,
15765                     Roo.bootstrap.TimeField.footer
15766                     ]
15767                 }
15768                 ]
15769             }
15770         ]
15771     }
15772 });
15773
15774  
15775
15776  /*
15777  * - LGPL
15778  *
15779  * CheckBox
15780  * 
15781  */
15782
15783 /**
15784  * @class Roo.bootstrap.CheckBox
15785  * @extends Roo.bootstrap.Input
15786  * Bootstrap CheckBox class
15787  * 
15788  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
15789  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
15790  * @cfg {String} boxLabel The text that appears beside the checkbox
15791  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
15792  * @cfg {Boolean} checked initnal the element
15793  * 
15794  * 
15795  * @constructor
15796  * Create a new CheckBox
15797  * @param {Object} config The config object
15798  */
15799
15800 Roo.bootstrap.CheckBox = function(config){
15801     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
15802    
15803         this.addEvents({
15804             /**
15805             * @event check
15806             * Fires when the element is checked or unchecked.
15807             * @param {Roo.bootstrap.CheckBox} this This input
15808             * @param {Boolean} checked The new checked value
15809             */
15810            check : true
15811         });
15812 };
15813
15814 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
15815     
15816     inputType: 'checkbox',
15817     inputValue: 1,
15818     valueOff: 0,
15819     boxLabel: false,
15820     checked: false,
15821     weight : false,
15822     
15823     getAutoCreate : function()
15824     {
15825         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
15826         
15827         var id = Roo.id();
15828         
15829         var cfg = {};
15830         
15831         cfg.cls = 'form-group checkbox' //input-group
15832         
15833         
15834         
15835         
15836         var input =  {
15837             tag: 'input',
15838             id : id,
15839             type : this.inputType,
15840             value : (!this.checked) ? this.valueOff : this.inputValue,
15841             cls : 'roo-checkbox', //'form-box',
15842             placeholder : this.placeholder || ''
15843             
15844         };
15845         
15846         if (this.weight) { // Validity check?
15847             cfg.cls += " checkbox-" + this.weight;
15848         }
15849         
15850         if (this.disabled) {
15851             input.disabled=true;
15852         }
15853         
15854         if(this.checked){
15855             input.checked = this.checked;
15856         }
15857         
15858         if (this.name) {
15859             input.name = this.name;
15860         }
15861         
15862         if (this.size) {
15863             input.cls += ' input-' + this.size;
15864         }
15865         
15866         var settings=this;
15867         ['xs','sm','md','lg'].map(function(size){
15868             if (settings[size]) {
15869                 cfg.cls += ' col-' + size + '-' + settings[size];
15870             }
15871         });
15872         
15873        
15874         
15875         var inputblock = input;
15876         
15877         
15878         
15879         
15880         if (this.before || this.after) {
15881             
15882             inputblock = {
15883                 cls : 'input-group',
15884                 cn :  [] 
15885             };
15886             if (this.before) {
15887                 inputblock.cn.push({
15888                     tag :'span',
15889                     cls : 'input-group-addon',
15890                     html : this.before
15891                 });
15892             }
15893             inputblock.cn.push(input);
15894             if (this.after) {
15895                 inputblock.cn.push({
15896                     tag :'span',
15897                     cls : 'input-group-addon',
15898                     html : this.after
15899                 });
15900             }
15901             
15902         };
15903         
15904         if (align ==='left' && this.fieldLabel.length) {
15905                 Roo.log("left and has label");
15906                 cfg.cn = [
15907                     
15908                     {
15909                         tag: 'label',
15910                         'for' :  id,
15911                         cls : 'control-label col-md-' + this.labelWidth,
15912                         html : this.fieldLabel
15913                         
15914                     },
15915                     {
15916                         cls : "col-md-" + (12 - this.labelWidth), 
15917                         cn: [
15918                             inputblock
15919                         ]
15920                     }
15921                     
15922                 ];
15923         } else if ( this.fieldLabel.length) {
15924                 Roo.log(" label");
15925                 cfg.cn = [
15926                    
15927                     {
15928                         tag: this.boxLabel ? 'span' : 'label',
15929                         'for': id,
15930                         cls: 'control-label box-input-label',
15931                         //cls : 'input-group-addon',
15932                         html : this.fieldLabel
15933                         
15934                     },
15935                     
15936                     inputblock
15937                     
15938                 ];
15939
15940         } else {
15941             
15942                 Roo.log(" no label && no align");
15943                 cfg.cn = [  inputblock ] ;
15944                 
15945                 
15946         };
15947          if(this.boxLabel){
15948             cfg.cn.push( {
15949                 tag: 'label',
15950                 'for': id,
15951                 cls: 'box-label',
15952                 html: this.boxLabel
15953                 
15954             });
15955         }
15956         
15957         
15958        
15959         return cfg;
15960         
15961     },
15962     
15963     /**
15964      * return the real input element.
15965      */
15966     inputEl: function ()
15967     {
15968         return this.el.select('input.roo-checkbox',true).first();
15969     },
15970     
15971     label: function()
15972     {
15973         return this.el.select('label.control-label',true).first();
15974     },
15975     
15976     initEvents : function()
15977     {
15978 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
15979         
15980         this.inputEl().on('click', this.onClick,  this);
15981         
15982     },
15983     
15984     onClick : function()
15985     {   
15986         this.setChecked(!this.checked);
15987     },
15988     
15989     setChecked : function(state,suppressEvent)
15990     {
15991         this.checked = state;
15992         
15993         this.inputEl().dom.checked = state;
15994         
15995         if(suppressEvent !== true){
15996             this.fireEvent('check', this, state);
15997         }
15998         
15999         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
16000         
16001     },
16002     
16003     setValue : function(v,suppressEvent)
16004     {
16005         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
16006     }
16007     
16008 });
16009
16010  
16011 /*
16012  * - LGPL
16013  *
16014  * Radio
16015  * 
16016  */
16017
16018 /**
16019  * @class Roo.bootstrap.Radio
16020  * @extends Roo.bootstrap.CheckBox
16021  * Bootstrap Radio class
16022
16023  * @constructor
16024  * Create a new Radio
16025  * @param {Object} config The config object
16026  */
16027
16028 Roo.bootstrap.Radio = function(config){
16029     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
16030    
16031 };
16032
16033 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
16034     
16035     inputType: 'radio',
16036     inputValue: '',
16037     valueOff: '',
16038     
16039     getAutoCreate : function()
16040     {
16041         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
16042         
16043         var id = Roo.id();
16044         
16045         var cfg = {};
16046         
16047         cfg.cls = 'form-group radio' //input-group
16048         
16049         var input =  {
16050             tag: 'input',
16051             id : id,
16052             type : this.inputType,
16053             value : (!this.checked) ? this.valueOff : this.inputValue,
16054             cls : 'roo-radio',
16055             placeholder : this.placeholder || ''
16056             
16057         };
16058           if (this.weight) { // Validity check?
16059             cfg.cls += " radio-" + this.weight;
16060         }
16061         if (this.disabled) {
16062             input.disabled=true;
16063         }
16064         
16065         if(this.checked){
16066             input.checked = this.checked;
16067         }
16068         
16069         if (this.name) {
16070             input.name = this.name;
16071         }
16072         
16073         if (this.size) {
16074             input.cls += ' input-' + this.size;
16075         }
16076         
16077         var settings=this;
16078         ['xs','sm','md','lg'].map(function(size){
16079             if (settings[size]) {
16080                 cfg.cls += ' col-' + size + '-' + settings[size];
16081             }
16082         });
16083         
16084         var inputblock = input;
16085         
16086         if (this.before || this.after) {
16087             
16088             inputblock = {
16089                 cls : 'input-group',
16090                 cn :  [] 
16091             };
16092             if (this.before) {
16093                 inputblock.cn.push({
16094                     tag :'span',
16095                     cls : 'input-group-addon',
16096                     html : this.before
16097                 });
16098             }
16099             inputblock.cn.push(input);
16100             if (this.after) {
16101                 inputblock.cn.push({
16102                     tag :'span',
16103                     cls : 'input-group-addon',
16104                     html : this.after
16105                 });
16106             }
16107             
16108         };
16109         
16110         if (align ==='left' && this.fieldLabel.length) {
16111                 Roo.log("left and has label");
16112                 cfg.cn = [
16113                     
16114                     {
16115                         tag: 'label',
16116                         'for' :  id,
16117                         cls : 'control-label col-md-' + this.labelWidth,
16118                         html : this.fieldLabel
16119                         
16120                     },
16121                     {
16122                         cls : "col-md-" + (12 - this.labelWidth), 
16123                         cn: [
16124                             inputblock
16125                         ]
16126                     }
16127                     
16128                 ];
16129         } else if ( this.fieldLabel.length) {
16130                 Roo.log(" label");
16131                  cfg.cn = [
16132                    
16133                     {
16134                         tag: 'label',
16135                         'for': id,
16136                         cls: 'control-label box-input-label',
16137                         //cls : 'input-group-addon',
16138                         html : this.fieldLabel
16139                         
16140                     },
16141                     
16142                     inputblock
16143                     
16144                 ];
16145
16146         } else {
16147             
16148                    Roo.log(" no label && no align");
16149                 cfg.cn = [
16150                     
16151                         inputblock
16152                     
16153                 ];
16154                 
16155                 
16156         };
16157         
16158         if(this.boxLabel){
16159             cfg.cn.push({
16160                 tag: 'label',
16161                 'for': id,
16162                 cls: 'box-label',
16163                 html: this.boxLabel
16164             })
16165         }
16166         
16167         return cfg;
16168         
16169     },
16170     inputEl: function ()
16171     {
16172         return this.el.select('input.roo-radio',true).first();
16173     },
16174     onClick : function()
16175     {   
16176         this.setChecked(true);
16177     },
16178     
16179     setChecked : function(state,suppressEvent)
16180     {
16181         if(state){
16182             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
16183                 v.dom.checked = false;
16184             });
16185         }
16186         
16187         this.checked = state;
16188         this.inputEl().dom.checked = state;
16189         
16190         if(suppressEvent !== true){
16191             this.fireEvent('check', this, state);
16192         }
16193         
16194         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
16195         
16196     },
16197     
16198     getGroupValue : function()
16199     {
16200         var value = ''
16201         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
16202             if(v.dom.checked == true){
16203                 value = v.dom.value;
16204             }
16205         });
16206         
16207         return value;
16208     },
16209     
16210     /**
16211      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
16212      * @return {Mixed} value The field value
16213      */
16214     getValue : function(){
16215         return this.getGroupValue();
16216     }
16217     
16218 });
16219
16220  
16221 //<script type="text/javascript">
16222
16223 /*
16224  * Based  Ext JS Library 1.1.1
16225  * Copyright(c) 2006-2007, Ext JS, LLC.
16226  * LGPL
16227  *
16228  */
16229  
16230 /**
16231  * @class Roo.HtmlEditorCore
16232  * @extends Roo.Component
16233  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
16234  *
16235  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
16236  */
16237
16238 Roo.HtmlEditorCore = function(config){
16239     
16240     
16241     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
16242     this.addEvents({
16243         /**
16244          * @event initialize
16245          * Fires when the editor is fully initialized (including the iframe)
16246          * @param {Roo.HtmlEditorCore} this
16247          */
16248         initialize: true,
16249         /**
16250          * @event activate
16251          * Fires when the editor is first receives the focus. Any insertion must wait
16252          * until after this event.
16253          * @param {Roo.HtmlEditorCore} this
16254          */
16255         activate: true,
16256          /**
16257          * @event beforesync
16258          * Fires before the textarea is updated with content from the editor iframe. Return false
16259          * to cancel the sync.
16260          * @param {Roo.HtmlEditorCore} this
16261          * @param {String} html
16262          */
16263         beforesync: true,
16264          /**
16265          * @event beforepush
16266          * Fires before the iframe editor is updated with content from the textarea. Return false
16267          * to cancel the push.
16268          * @param {Roo.HtmlEditorCore} this
16269          * @param {String} html
16270          */
16271         beforepush: true,
16272          /**
16273          * @event sync
16274          * Fires when the textarea is updated with content from the editor iframe.
16275          * @param {Roo.HtmlEditorCore} this
16276          * @param {String} html
16277          */
16278         sync: true,
16279          /**
16280          * @event push
16281          * Fires when the iframe editor is updated with content from the textarea.
16282          * @param {Roo.HtmlEditorCore} this
16283          * @param {String} html
16284          */
16285         push: true,
16286         
16287         /**
16288          * @event editorevent
16289          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
16290          * @param {Roo.HtmlEditorCore} this
16291          */
16292         editorevent: true
16293     });
16294      
16295 };
16296
16297
16298 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
16299
16300
16301      /**
16302      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
16303      */
16304     
16305     owner : false,
16306     
16307      /**
16308      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
16309      *                        Roo.resizable.
16310      */
16311     resizable : false,
16312      /**
16313      * @cfg {Number} height (in pixels)
16314      */   
16315     height: 300,
16316    /**
16317      * @cfg {Number} width (in pixels)
16318      */   
16319     width: 500,
16320     
16321     /**
16322      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
16323      * 
16324      */
16325     stylesheets: false,
16326     
16327     // id of frame..
16328     frameId: false,
16329     
16330     // private properties
16331     validationEvent : false,
16332     deferHeight: true,
16333     initialized : false,
16334     activated : false,
16335     sourceEditMode : false,
16336     onFocus : Roo.emptyFn,
16337     iframePad:3,
16338     hideMode:'offsets',
16339     
16340     clearUp: true,
16341     
16342      
16343     
16344
16345     /**
16346      * Protected method that will not generally be called directly. It
16347      * is called when the editor initializes the iframe with HTML contents. Override this method if you
16348      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
16349      */
16350     getDocMarkup : function(){
16351         // body styles..
16352         var st = '';
16353         Roo.log(this.stylesheets);
16354         
16355         // inherit styels from page...?? 
16356         if (this.stylesheets === false) {
16357             
16358             Roo.get(document.head).select('style').each(function(node) {
16359                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16360             });
16361             
16362             Roo.get(document.head).select('link').each(function(node) { 
16363                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16364             });
16365             
16366         } else if (!this.stylesheets.length) {
16367                 // simple..
16368                 st = '<style type="text/css">' +
16369                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16370                    '</style>';
16371         } else {
16372             Roo.each(this.stylesheets, function(s) {
16373                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
16374             });
16375             
16376         }
16377         
16378         st +=  '<style type="text/css">' +
16379             'IMG { cursor: pointer } ' +
16380         '</style>';
16381
16382         
16383         return '<html><head>' + st  +
16384             //<style type="text/css">' +
16385             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16386             //'</style>' +
16387             ' </head><body class="roo-htmleditor-body"></body></html>';
16388     },
16389
16390     // private
16391     onRender : function(ct, position)
16392     {
16393         var _t = this;
16394         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
16395         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
16396         
16397         
16398         this.el.dom.style.border = '0 none';
16399         this.el.dom.setAttribute('tabIndex', -1);
16400         this.el.addClass('x-hidden hide');
16401         
16402         
16403         
16404         if(Roo.isIE){ // fix IE 1px bogus margin
16405             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
16406         }
16407        
16408         
16409         this.frameId = Roo.id();
16410         
16411          
16412         
16413         var iframe = this.owner.wrap.createChild({
16414             tag: 'iframe',
16415             cls: 'form-control', // bootstrap..
16416             id: this.frameId,
16417             name: this.frameId,
16418             frameBorder : 'no',
16419             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
16420         }, this.el
16421         );
16422         
16423         
16424         this.iframe = iframe.dom;
16425
16426          this.assignDocWin();
16427         
16428         this.doc.designMode = 'on';
16429        
16430         this.doc.open();
16431         this.doc.write(this.getDocMarkup());
16432         this.doc.close();
16433
16434         
16435         var task = { // must defer to wait for browser to be ready
16436             run : function(){
16437                 //console.log("run task?" + this.doc.readyState);
16438                 this.assignDocWin();
16439                 if(this.doc.body || this.doc.readyState == 'complete'){
16440                     try {
16441                         this.doc.designMode="on";
16442                     } catch (e) {
16443                         return;
16444                     }
16445                     Roo.TaskMgr.stop(task);
16446                     this.initEditor.defer(10, this);
16447                 }
16448             },
16449             interval : 10,
16450             duration: 10000,
16451             scope: this
16452         };
16453         Roo.TaskMgr.start(task);
16454
16455         
16456          
16457     },
16458
16459     // private
16460     onResize : function(w, h)
16461     {
16462          Roo.log('resize: ' +w + ',' + h );
16463         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
16464         if(!this.iframe){
16465             return;
16466         }
16467         if(typeof w == 'number'){
16468             
16469             this.iframe.style.width = w + 'px';
16470         }
16471         if(typeof h == 'number'){
16472             
16473             this.iframe.style.height = h + 'px';
16474             if(this.doc){
16475                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
16476             }
16477         }
16478         
16479     },
16480
16481     /**
16482      * Toggles the editor between standard and source edit mode.
16483      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
16484      */
16485     toggleSourceEdit : function(sourceEditMode){
16486         
16487         this.sourceEditMode = sourceEditMode === true;
16488         
16489         if(this.sourceEditMode){
16490  
16491             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
16492             
16493         }else{
16494             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
16495             //this.iframe.className = '';
16496             this.deferFocus();
16497         }
16498         //this.setSize(this.owner.wrap.getSize());
16499         //this.fireEvent('editmodechange', this, this.sourceEditMode);
16500     },
16501
16502     
16503   
16504
16505     /**
16506      * Protected method that will not generally be called directly. If you need/want
16507      * custom HTML cleanup, this is the method you should override.
16508      * @param {String} html The HTML to be cleaned
16509      * return {String} The cleaned HTML
16510      */
16511     cleanHtml : function(html){
16512         html = String(html);
16513         if(html.length > 5){
16514             if(Roo.isSafari){ // strip safari nonsense
16515                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
16516             }
16517         }
16518         if(html == '&nbsp;'){
16519             html = '';
16520         }
16521         return html;
16522     },
16523
16524     /**
16525      * HTML Editor -> Textarea
16526      * Protected method that will not generally be called directly. Syncs the contents
16527      * of the editor iframe with the textarea.
16528      */
16529     syncValue : function(){
16530         if(this.initialized){
16531             var bd = (this.doc.body || this.doc.documentElement);
16532             //this.cleanUpPaste(); -- this is done else where and causes havoc..
16533             var html = bd.innerHTML;
16534             if(Roo.isSafari){
16535                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
16536                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
16537                 if(m && m[1]){
16538                     html = '<div style="'+m[0]+'">' + html + '</div>';
16539                 }
16540             }
16541             html = this.cleanHtml(html);
16542             // fix up the special chars.. normaly like back quotes in word...
16543             // however we do not want to do this with chinese..
16544             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
16545                 var cc = b.charCodeAt();
16546                 if (
16547                     (cc >= 0x4E00 && cc < 0xA000 ) ||
16548                     (cc >= 0x3400 && cc < 0x4E00 ) ||
16549                     (cc >= 0xf900 && cc < 0xfb00 )
16550                 ) {
16551                         return b;
16552                 }
16553                 return "&#"+cc+";" 
16554             });
16555             if(this.owner.fireEvent('beforesync', this, html) !== false){
16556                 this.el.dom.value = html;
16557                 this.owner.fireEvent('sync', this, html);
16558             }
16559         }
16560     },
16561
16562     /**
16563      * Protected method that will not generally be called directly. Pushes the value of the textarea
16564      * into the iframe editor.
16565      */
16566     pushValue : function(){
16567         if(this.initialized){
16568             var v = this.el.dom.value.trim();
16569             
16570 //            if(v.length < 1){
16571 //                v = '&#160;';
16572 //            }
16573             
16574             if(this.owner.fireEvent('beforepush', this, v) !== false){
16575                 var d = (this.doc.body || this.doc.documentElement);
16576                 d.innerHTML = v;
16577                 this.cleanUpPaste();
16578                 this.el.dom.value = d.innerHTML;
16579                 this.owner.fireEvent('push', this, v);
16580             }
16581         }
16582     },
16583
16584     // private
16585     deferFocus : function(){
16586         this.focus.defer(10, this);
16587     },
16588
16589     // doc'ed in Field
16590     focus : function(){
16591         if(this.win && !this.sourceEditMode){
16592             this.win.focus();
16593         }else{
16594             this.el.focus();
16595         }
16596     },
16597     
16598     assignDocWin: function()
16599     {
16600         var iframe = this.iframe;
16601         
16602          if(Roo.isIE){
16603             this.doc = iframe.contentWindow.document;
16604             this.win = iframe.contentWindow;
16605         } else {
16606 //            if (!Roo.get(this.frameId)) {
16607 //                return;
16608 //            }
16609 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
16610 //            this.win = Roo.get(this.frameId).dom.contentWindow;
16611             
16612             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
16613                 return;
16614             }
16615             
16616             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
16617             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
16618         }
16619     },
16620     
16621     // private
16622     initEditor : function(){
16623         //console.log("INIT EDITOR");
16624         this.assignDocWin();
16625         
16626         
16627         
16628         this.doc.designMode="on";
16629         this.doc.open();
16630         this.doc.write(this.getDocMarkup());
16631         this.doc.close();
16632         
16633         var dbody = (this.doc.body || this.doc.documentElement);
16634         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
16635         // this copies styles from the containing element into thsi one..
16636         // not sure why we need all of this..
16637         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
16638         
16639         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
16640         //ss['background-attachment'] = 'fixed'; // w3c
16641         dbody.bgProperties = 'fixed'; // ie
16642         //Roo.DomHelper.applyStyles(dbody, ss);
16643         Roo.EventManager.on(this.doc, {
16644             //'mousedown': this.onEditorEvent,
16645             'mouseup': this.onEditorEvent,
16646             'dblclick': this.onEditorEvent,
16647             'click': this.onEditorEvent,
16648             'keyup': this.onEditorEvent,
16649             buffer:100,
16650             scope: this
16651         });
16652         if(Roo.isGecko){
16653             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
16654         }
16655         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
16656             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
16657         }
16658         this.initialized = true;
16659
16660         this.owner.fireEvent('initialize', this);
16661         this.pushValue();
16662     },
16663
16664     // private
16665     onDestroy : function(){
16666         
16667         
16668         
16669         if(this.rendered){
16670             
16671             //for (var i =0; i < this.toolbars.length;i++) {
16672             //    // fixme - ask toolbars for heights?
16673             //    this.toolbars[i].onDestroy();
16674            // }
16675             
16676             //this.wrap.dom.innerHTML = '';
16677             //this.wrap.remove();
16678         }
16679     },
16680
16681     // private
16682     onFirstFocus : function(){
16683         
16684         this.assignDocWin();
16685         
16686         
16687         this.activated = true;
16688          
16689     
16690         if(Roo.isGecko){ // prevent silly gecko errors
16691             this.win.focus();
16692             var s = this.win.getSelection();
16693             if(!s.focusNode || s.focusNode.nodeType != 3){
16694                 var r = s.getRangeAt(0);
16695                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
16696                 r.collapse(true);
16697                 this.deferFocus();
16698             }
16699             try{
16700                 this.execCmd('useCSS', true);
16701                 this.execCmd('styleWithCSS', false);
16702             }catch(e){}
16703         }
16704         this.owner.fireEvent('activate', this);
16705     },
16706
16707     // private
16708     adjustFont: function(btn){
16709         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
16710         //if(Roo.isSafari){ // safari
16711         //    adjust *= 2;
16712        // }
16713         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
16714         if(Roo.isSafari){ // safari
16715             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
16716             v =  (v < 10) ? 10 : v;
16717             v =  (v > 48) ? 48 : v;
16718             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
16719             
16720         }
16721         
16722         
16723         v = Math.max(1, v+adjust);
16724         
16725         this.execCmd('FontSize', v  );
16726     },
16727
16728     onEditorEvent : function(e){
16729         this.owner.fireEvent('editorevent', this, e);
16730       //  this.updateToolbar();
16731         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
16732     },
16733
16734     insertTag : function(tg)
16735     {
16736         // could be a bit smarter... -> wrap the current selected tRoo..
16737         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
16738             
16739             range = this.createRange(this.getSelection());
16740             var wrappingNode = this.doc.createElement(tg.toLowerCase());
16741             wrappingNode.appendChild(range.extractContents());
16742             range.insertNode(wrappingNode);
16743
16744             return;
16745             
16746             
16747             
16748         }
16749         this.execCmd("formatblock",   tg);
16750         
16751     },
16752     
16753     insertText : function(txt)
16754     {
16755         
16756         
16757         var range = this.createRange();
16758         range.deleteContents();
16759                //alert(Sender.getAttribute('label'));
16760                
16761         range.insertNode(this.doc.createTextNode(txt));
16762     } ,
16763     
16764      
16765
16766     /**
16767      * Executes a Midas editor command on the editor document and performs necessary focus and
16768      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
16769      * @param {String} cmd The Midas command
16770      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16771      */
16772     relayCmd : function(cmd, value){
16773         this.win.focus();
16774         this.execCmd(cmd, value);
16775         this.owner.fireEvent('editorevent', this);
16776         //this.updateToolbar();
16777         this.owner.deferFocus();
16778     },
16779
16780     /**
16781      * Executes a Midas editor command directly on the editor document.
16782      * For visual commands, you should use {@link #relayCmd} instead.
16783      * <b>This should only be called after the editor is initialized.</b>
16784      * @param {String} cmd The Midas command
16785      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16786      */
16787     execCmd : function(cmd, value){
16788         this.doc.execCommand(cmd, false, value === undefined ? null : value);
16789         this.syncValue();
16790     },
16791  
16792  
16793    
16794     /**
16795      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
16796      * to insert tRoo.
16797      * @param {String} text | dom node.. 
16798      */
16799     insertAtCursor : function(text)
16800     {
16801         
16802         
16803         
16804         if(!this.activated){
16805             return;
16806         }
16807         /*
16808         if(Roo.isIE){
16809             this.win.focus();
16810             var r = this.doc.selection.createRange();
16811             if(r){
16812                 r.collapse(true);
16813                 r.pasteHTML(text);
16814                 this.syncValue();
16815                 this.deferFocus();
16816             
16817             }
16818             return;
16819         }
16820         */
16821         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
16822             this.win.focus();
16823             
16824             
16825             // from jquery ui (MIT licenced)
16826             var range, node;
16827             var win = this.win;
16828             
16829             if (win.getSelection && win.getSelection().getRangeAt) {
16830                 range = win.getSelection().getRangeAt(0);
16831                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
16832                 range.insertNode(node);
16833             } else if (win.document.selection && win.document.selection.createRange) {
16834                 // no firefox support
16835                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
16836                 win.document.selection.createRange().pasteHTML(txt);
16837             } else {
16838                 // no firefox support
16839                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
16840                 this.execCmd('InsertHTML', txt);
16841             } 
16842             
16843             this.syncValue();
16844             
16845             this.deferFocus();
16846         }
16847     },
16848  // private
16849     mozKeyPress : function(e){
16850         if(e.ctrlKey){
16851             var c = e.getCharCode(), cmd;
16852           
16853             if(c > 0){
16854                 c = String.fromCharCode(c).toLowerCase();
16855                 switch(c){
16856                     case 'b':
16857                         cmd = 'bold';
16858                         break;
16859                     case 'i':
16860                         cmd = 'italic';
16861                         break;
16862                     
16863                     case 'u':
16864                         cmd = 'underline';
16865                         break;
16866                     
16867                     case 'v':
16868                         this.cleanUpPaste.defer(100, this);
16869                         return;
16870                         
16871                 }
16872                 if(cmd){
16873                     this.win.focus();
16874                     this.execCmd(cmd);
16875                     this.deferFocus();
16876                     e.preventDefault();
16877                 }
16878                 
16879             }
16880         }
16881     },
16882
16883     // private
16884     fixKeys : function(){ // load time branching for fastest keydown performance
16885         if(Roo.isIE){
16886             return function(e){
16887                 var k = e.getKey(), r;
16888                 if(k == e.TAB){
16889                     e.stopEvent();
16890                     r = this.doc.selection.createRange();
16891                     if(r){
16892                         r.collapse(true);
16893                         r.pasteHTML('&#160;&#160;&#160;&#160;');
16894                         this.deferFocus();
16895                     }
16896                     return;
16897                 }
16898                 
16899                 if(k == e.ENTER){
16900                     r = this.doc.selection.createRange();
16901                     if(r){
16902                         var target = r.parentElement();
16903                         if(!target || target.tagName.toLowerCase() != 'li'){
16904                             e.stopEvent();
16905                             r.pasteHTML('<br />');
16906                             r.collapse(false);
16907                             r.select();
16908                         }
16909                     }
16910                 }
16911                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
16912                     this.cleanUpPaste.defer(100, this);
16913                     return;
16914                 }
16915                 
16916                 
16917             };
16918         }else if(Roo.isOpera){
16919             return function(e){
16920                 var k = e.getKey();
16921                 if(k == e.TAB){
16922                     e.stopEvent();
16923                     this.win.focus();
16924                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
16925                     this.deferFocus();
16926                 }
16927                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
16928                     this.cleanUpPaste.defer(100, this);
16929                     return;
16930                 }
16931                 
16932             };
16933         }else if(Roo.isSafari){
16934             return function(e){
16935                 var k = e.getKey();
16936                 
16937                 if(k == e.TAB){
16938                     e.stopEvent();
16939                     this.execCmd('InsertText','\t');
16940                     this.deferFocus();
16941                     return;
16942                 }
16943                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
16944                     this.cleanUpPaste.defer(100, this);
16945                     return;
16946                 }
16947                 
16948              };
16949         }
16950     }(),
16951     
16952     getAllAncestors: function()
16953     {
16954         var p = this.getSelectedNode();
16955         var a = [];
16956         if (!p) {
16957             a.push(p); // push blank onto stack..
16958             p = this.getParentElement();
16959         }
16960         
16961         
16962         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
16963             a.push(p);
16964             p = p.parentNode;
16965         }
16966         a.push(this.doc.body);
16967         return a;
16968     },
16969     lastSel : false,
16970     lastSelNode : false,
16971     
16972     
16973     getSelection : function() 
16974     {
16975         this.assignDocWin();
16976         return Roo.isIE ? this.doc.selection : this.win.getSelection();
16977     },
16978     
16979     getSelectedNode: function() 
16980     {
16981         // this may only work on Gecko!!!
16982         
16983         // should we cache this!!!!
16984         
16985         
16986         
16987          
16988         var range = this.createRange(this.getSelection()).cloneRange();
16989         
16990         if (Roo.isIE) {
16991             var parent = range.parentElement();
16992             while (true) {
16993                 var testRange = range.duplicate();
16994                 testRange.moveToElementText(parent);
16995                 if (testRange.inRange(range)) {
16996                     break;
16997                 }
16998                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
16999                     break;
17000                 }
17001                 parent = parent.parentElement;
17002             }
17003             return parent;
17004         }
17005         
17006         // is ancestor a text element.
17007         var ac =  range.commonAncestorContainer;
17008         if (ac.nodeType == 3) {
17009             ac = ac.parentNode;
17010         }
17011         
17012         var ar = ac.childNodes;
17013          
17014         var nodes = [];
17015         var other_nodes = [];
17016         var has_other_nodes = false;
17017         for (var i=0;i<ar.length;i++) {
17018             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
17019                 continue;
17020             }
17021             // fullly contained node.
17022             
17023             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
17024                 nodes.push(ar[i]);
17025                 continue;
17026             }
17027             
17028             // probably selected..
17029             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
17030                 other_nodes.push(ar[i]);
17031                 continue;
17032             }
17033             // outer..
17034             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
17035                 continue;
17036             }
17037             
17038             
17039             has_other_nodes = true;
17040         }
17041         if (!nodes.length && other_nodes.length) {
17042             nodes= other_nodes;
17043         }
17044         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
17045             return false;
17046         }
17047         
17048         return nodes[0];
17049     },
17050     createRange: function(sel)
17051     {
17052         // this has strange effects when using with 
17053         // top toolbar - not sure if it's a great idea.
17054         //this.editor.contentWindow.focus();
17055         if (typeof sel != "undefined") {
17056             try {
17057                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
17058             } catch(e) {
17059                 return this.doc.createRange();
17060             }
17061         } else {
17062             return this.doc.createRange();
17063         }
17064     },
17065     getParentElement: function()
17066     {
17067         
17068         this.assignDocWin();
17069         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
17070         
17071         var range = this.createRange(sel);
17072          
17073         try {
17074             var p = range.commonAncestorContainer;
17075             while (p.nodeType == 3) { // text node
17076                 p = p.parentNode;
17077             }
17078             return p;
17079         } catch (e) {
17080             return null;
17081         }
17082     
17083     },
17084     /***
17085      *
17086      * Range intersection.. the hard stuff...
17087      *  '-1' = before
17088      *  '0' = hits..
17089      *  '1' = after.
17090      *         [ -- selected range --- ]
17091      *   [fail]                        [fail]
17092      *
17093      *    basically..
17094      *      if end is before start or  hits it. fail.
17095      *      if start is after end or hits it fail.
17096      *
17097      *   if either hits (but other is outside. - then it's not 
17098      *   
17099      *    
17100      **/
17101     
17102     
17103     // @see http://www.thismuchiknow.co.uk/?p=64.
17104     rangeIntersectsNode : function(range, node)
17105     {
17106         var nodeRange = node.ownerDocument.createRange();
17107         try {
17108             nodeRange.selectNode(node);
17109         } catch (e) {
17110             nodeRange.selectNodeContents(node);
17111         }
17112     
17113         var rangeStartRange = range.cloneRange();
17114         rangeStartRange.collapse(true);
17115     
17116         var rangeEndRange = range.cloneRange();
17117         rangeEndRange.collapse(false);
17118     
17119         var nodeStartRange = nodeRange.cloneRange();
17120         nodeStartRange.collapse(true);
17121     
17122         var nodeEndRange = nodeRange.cloneRange();
17123         nodeEndRange.collapse(false);
17124     
17125         return rangeStartRange.compareBoundaryPoints(
17126                  Range.START_TO_START, nodeEndRange) == -1 &&
17127                rangeEndRange.compareBoundaryPoints(
17128                  Range.START_TO_START, nodeStartRange) == 1;
17129         
17130          
17131     },
17132     rangeCompareNode : function(range, node)
17133     {
17134         var nodeRange = node.ownerDocument.createRange();
17135         try {
17136             nodeRange.selectNode(node);
17137         } catch (e) {
17138             nodeRange.selectNodeContents(node);
17139         }
17140         
17141         
17142         range.collapse(true);
17143     
17144         nodeRange.collapse(true);
17145      
17146         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
17147         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
17148          
17149         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
17150         
17151         var nodeIsBefore   =  ss == 1;
17152         var nodeIsAfter    = ee == -1;
17153         
17154         if (nodeIsBefore && nodeIsAfter)
17155             return 0; // outer
17156         if (!nodeIsBefore && nodeIsAfter)
17157             return 1; //right trailed.
17158         
17159         if (nodeIsBefore && !nodeIsAfter)
17160             return 2;  // left trailed.
17161         // fully contined.
17162         return 3;
17163     },
17164
17165     // private? - in a new class?
17166     cleanUpPaste :  function()
17167     {
17168         // cleans up the whole document..
17169         Roo.log('cleanuppaste');
17170         
17171         this.cleanUpChildren(this.doc.body);
17172         var clean = this.cleanWordChars(this.doc.body.innerHTML);
17173         if (clean != this.doc.body.innerHTML) {
17174             this.doc.body.innerHTML = clean;
17175         }
17176         
17177     },
17178     
17179     cleanWordChars : function(input) {// change the chars to hex code
17180         var he = Roo.HtmlEditorCore;
17181         
17182         var output = input;
17183         Roo.each(he.swapCodes, function(sw) { 
17184             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
17185             
17186             output = output.replace(swapper, sw[1]);
17187         });
17188         
17189         return output;
17190     },
17191     
17192     
17193     cleanUpChildren : function (n)
17194     {
17195         if (!n.childNodes.length) {
17196             return;
17197         }
17198         for (var i = n.childNodes.length-1; i > -1 ; i--) {
17199            this.cleanUpChild(n.childNodes[i]);
17200         }
17201     },
17202     
17203     
17204         
17205     
17206     cleanUpChild : function (node)
17207     {
17208         var ed = this;
17209         //console.log(node);
17210         if (node.nodeName == "#text") {
17211             // clean up silly Windows -- stuff?
17212             return; 
17213         }
17214         if (node.nodeName == "#comment") {
17215             node.parentNode.removeChild(node);
17216             // clean up silly Windows -- stuff?
17217             return; 
17218         }
17219         
17220         if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1 && this.clearUp) {
17221             // remove node.
17222             node.parentNode.removeChild(node);
17223             return;
17224             
17225         }
17226         
17227         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
17228         
17229         // remove <a name=....> as rendering on yahoo mailer is borked with this.
17230         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
17231         
17232         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
17233         //    remove_keep_children = true;
17234         //}
17235         
17236         if (remove_keep_children) {
17237             this.cleanUpChildren(node);
17238             // inserts everything just before this node...
17239             while (node.childNodes.length) {
17240                 var cn = node.childNodes[0];
17241                 node.removeChild(cn);
17242                 node.parentNode.insertBefore(cn, node);
17243             }
17244             node.parentNode.removeChild(node);
17245             return;
17246         }
17247         
17248         if (!node.attributes || !node.attributes.length) {
17249             this.cleanUpChildren(node);
17250             return;
17251         }
17252         
17253         function cleanAttr(n,v)
17254         {
17255             
17256             if (v.match(/^\./) || v.match(/^\//)) {
17257                 return;
17258             }
17259             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
17260                 return;
17261             }
17262             if (v.match(/^#/)) {
17263                 return;
17264             }
17265 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
17266             node.removeAttribute(n);
17267             
17268         }
17269         
17270         function cleanStyle(n,v)
17271         {
17272             if (v.match(/expression/)) { //XSS?? should we even bother..
17273                 node.removeAttribute(n);
17274                 return;
17275             }
17276             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
17277             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
17278             
17279             
17280             var parts = v.split(/;/);
17281             var clean = [];
17282             
17283             Roo.each(parts, function(p) {
17284                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
17285                 if (!p.length) {
17286                     return true;
17287                 }
17288                 var l = p.split(':').shift().replace(/\s+/g,'');
17289                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
17290                 
17291                 if ( cblack.indexOf(l) > -1) {
17292 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17293                     //node.removeAttribute(n);
17294                     return true;
17295                 }
17296                 //Roo.log()
17297                 // only allow 'c whitelisted system attributes'
17298                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
17299 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17300                     //node.removeAttribute(n);
17301                     return true;
17302                 }
17303                 
17304                 
17305                  
17306                 
17307                 clean.push(p);
17308                 return true;
17309             });
17310             if (clean.length) { 
17311                 node.setAttribute(n, clean.join(';'));
17312             } else {
17313                 node.removeAttribute(n);
17314             }
17315             
17316         }
17317         
17318         
17319         for (var i = node.attributes.length-1; i > -1 ; i--) {
17320             var a = node.attributes[i];
17321             //console.log(a);
17322             
17323             if (a.name.toLowerCase().substr(0,2)=='on')  {
17324                 node.removeAttribute(a.name);
17325                 continue;
17326             }
17327             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
17328                 node.removeAttribute(a.name);
17329                 continue;
17330             }
17331             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
17332                 cleanAttr(a.name,a.value); // fixme..
17333                 continue;
17334             }
17335             if (a.name == 'style') {
17336                 cleanStyle(a.name,a.value);
17337                 continue;
17338             }
17339             /// clean up MS crap..
17340             // tecnically this should be a list of valid class'es..
17341             
17342             
17343             if (a.name == 'class') {
17344                 if (a.value.match(/^Mso/)) {
17345                     node.className = '';
17346                 }
17347                 
17348                 if (a.value.match(/body/)) {
17349                     node.className = '';
17350                 }
17351                 continue;
17352             }
17353             
17354             // style cleanup!?
17355             // class cleanup?
17356             
17357         }
17358         
17359         
17360         this.cleanUpChildren(node);
17361         
17362         
17363     },
17364     /**
17365      * Clean up MS wordisms...
17366      */
17367     cleanWord : function(node)
17368     {
17369         var _t = this;
17370         var cleanWordChildren = function()
17371         {
17372             if (!node.childNodes.length) {
17373                 return;
17374             }
17375             for (var i = node.childNodes.length-1; i > -1 ; i--) {
17376                _t.cleanWord(node.childNodes[i]);
17377             }
17378         }
17379         
17380         
17381         if (!node) {
17382             this.cleanWord(this.doc.body);
17383             return;
17384         }
17385         if (node.nodeName == "#text") {
17386             // clean up silly Windows -- stuff?
17387             return; 
17388         }
17389         if (node.nodeName == "#comment") {
17390             node.parentNode.removeChild(node);
17391             // clean up silly Windows -- stuff?
17392             return; 
17393         }
17394         
17395         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
17396             node.parentNode.removeChild(node);
17397             return;
17398         }
17399         
17400         // remove - but keep children..
17401         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
17402             while (node.childNodes.length) {
17403                 var cn = node.childNodes[0];
17404                 node.removeChild(cn);
17405                 node.parentNode.insertBefore(cn, node);
17406             }
17407             node.parentNode.removeChild(node);
17408             cleanWordChildren();
17409             return;
17410         }
17411         // clean styles
17412         if (node.className.length) {
17413             
17414             var cn = node.className.split(/\W+/);
17415             var cna = [];
17416             Roo.each(cn, function(cls) {
17417                 if (cls.match(/Mso[a-zA-Z]+/)) {
17418                     return;
17419                 }
17420                 cna.push(cls);
17421             });
17422             node.className = cna.length ? cna.join(' ') : '';
17423             if (!cna.length) {
17424                 node.removeAttribute("class");
17425             }
17426         }
17427         
17428         if (node.hasAttribute("lang")) {
17429             node.removeAttribute("lang");
17430         }
17431         
17432         if (node.hasAttribute("style")) {
17433             
17434             var styles = node.getAttribute("style").split(";");
17435             var nstyle = [];
17436             Roo.each(styles, function(s) {
17437                 if (!s.match(/:/)) {
17438                     return;
17439                 }
17440                 var kv = s.split(":");
17441                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
17442                     return;
17443                 }
17444                 // what ever is left... we allow.
17445                 nstyle.push(s);
17446             });
17447             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
17448             if (!nstyle.length) {
17449                 node.removeAttribute('style');
17450             }
17451         }
17452         
17453         cleanWordChildren();
17454         
17455         
17456     },
17457     domToHTML : function(currentElement, depth, nopadtext) {
17458         
17459             depth = depth || 0;
17460             nopadtext = nopadtext || false;
17461         
17462             if (!currentElement) {
17463                 return this.domToHTML(this.doc.body);
17464             }
17465             
17466             //Roo.log(currentElement);
17467             var j;
17468             var allText = false;
17469             var nodeName = currentElement.nodeName;
17470             var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
17471             
17472             if  (nodeName == '#text') {
17473                 return currentElement.nodeValue;
17474             }
17475             
17476             
17477             var ret = '';
17478             if (nodeName != 'BODY') {
17479                  
17480                 var i = 0;
17481                 // Prints the node tagName, such as <A>, <IMG>, etc
17482                 if (tagName) {
17483                     var attr = [];
17484                     for(i = 0; i < currentElement.attributes.length;i++) {
17485                         // quoting?
17486                         var aname = currentElement.attributes.item(i).name;
17487                         if (!currentElement.attributes.item(i).value.length) {
17488                             continue;
17489                         }
17490                         attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
17491                     }
17492                     
17493                     ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
17494                 } 
17495                 else {
17496                     
17497                     // eack
17498                 }
17499             } else {
17500                 tagName = false;
17501             }
17502             if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
17503                 return ret;
17504             }
17505             if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
17506                 nopadtext = true;
17507             }
17508             
17509             
17510             // Traverse the tree
17511             i = 0;
17512             var currentElementChild = currentElement.childNodes.item(i);
17513             var allText = true;
17514             var innerHTML  = '';
17515             lastnode = '';
17516             while (currentElementChild) {
17517                 // Formatting code (indent the tree so it looks nice on the screen)
17518                 var nopad = nopadtext;
17519                 if (lastnode == 'SPAN') {
17520                     nopad  = true;
17521                 }
17522                 // text
17523                 if  (currentElementChild.nodeName == '#text') {
17524                     var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
17525                     if (!nopad && toadd.length > 80) {
17526                         innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
17527                     }
17528                     innerHTML  += toadd;
17529                     
17530                     i++;
17531                     currentElementChild = currentElement.childNodes.item(i);
17532                     lastNode = '';
17533                     continue;
17534                 }
17535                 allText = false;
17536                 
17537                 innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
17538                     
17539                 // Recursively traverse the tree structure of the child node
17540                 innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
17541                 lastnode = currentElementChild.nodeName;
17542                 i++;
17543                 currentElementChild=currentElement.childNodes.item(i);
17544             }
17545             
17546             ret += innerHTML;
17547             
17548             if (!allText) {
17549                     // The remaining code is mostly for formatting the tree
17550                 ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
17551             }
17552             
17553             
17554             if (tagName) {
17555                 ret+= "</"+tagName+">";
17556             }
17557             return ret;
17558             
17559         }
17560     
17561     // hide stuff that is not compatible
17562     /**
17563      * @event blur
17564      * @hide
17565      */
17566     /**
17567      * @event change
17568      * @hide
17569      */
17570     /**
17571      * @event focus
17572      * @hide
17573      */
17574     /**
17575      * @event specialkey
17576      * @hide
17577      */
17578     /**
17579      * @cfg {String} fieldClass @hide
17580      */
17581     /**
17582      * @cfg {String} focusClass @hide
17583      */
17584     /**
17585      * @cfg {String} autoCreate @hide
17586      */
17587     /**
17588      * @cfg {String} inputType @hide
17589      */
17590     /**
17591      * @cfg {String} invalidClass @hide
17592      */
17593     /**
17594      * @cfg {String} invalidText @hide
17595      */
17596     /**
17597      * @cfg {String} msgFx @hide
17598      */
17599     /**
17600      * @cfg {String} validateOnBlur @hide
17601      */
17602 });
17603
17604 Roo.HtmlEditorCore.white = [
17605         'area', 'br', 'img', 'input', 'hr', 'wbr',
17606         
17607        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
17608        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
17609        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
17610        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
17611        'table',   'ul',         'xmp', 
17612        
17613        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
17614       'thead',   'tr', 
17615      
17616       'dir', 'menu', 'ol', 'ul', 'dl',
17617        
17618       'embed',  'object'
17619 ];
17620
17621
17622 Roo.HtmlEditorCore.black = [
17623     //    'embed',  'object', // enable - backend responsiblity to clean thiese
17624         'applet', // 
17625         'base',   'basefont', 'bgsound', 'blink',  'body', 
17626         'frame',  'frameset', 'head',    'html',   'ilayer', 
17627         'iframe', 'layer',  'link',     'meta',    'object',   
17628         'script', 'style' ,'title',  'xml' // clean later..
17629 ];
17630 Roo.HtmlEditorCore.clean = [
17631     'script', 'style', 'title', 'xml'
17632 ];
17633 Roo.HtmlEditorCore.remove = [
17634     'font'
17635 ];
17636 // attributes..
17637
17638 Roo.HtmlEditorCore.ablack = [
17639     'on'
17640 ];
17641     
17642 Roo.HtmlEditorCore.aclean = [ 
17643     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
17644 ];
17645
17646 // protocols..
17647 Roo.HtmlEditorCore.pwhite= [
17648         'http',  'https',  'mailto'
17649 ];
17650
17651 // white listed style attributes.
17652 Roo.HtmlEditorCore.cwhite= [
17653       //  'text-align', /// default is to allow most things..
17654       
17655          
17656 //        'font-size'//??
17657 ];
17658
17659 // black listed style attributes.
17660 Roo.HtmlEditorCore.cblack= [
17661       //  'font-size' -- this can be set by the project 
17662 ];
17663
17664
17665 Roo.HtmlEditorCore.swapCodes   =[ 
17666     [    8211, "--" ], 
17667     [    8212, "--" ], 
17668     [    8216,  "'" ],  
17669     [    8217, "'" ],  
17670     [    8220, '"' ],  
17671     [    8221, '"' ],  
17672     [    8226, "*" ],  
17673     [    8230, "..." ]
17674 ]; 
17675
17676     /*
17677  * - LGPL
17678  *
17679  * HtmlEditor
17680  * 
17681  */
17682
17683 /**
17684  * @class Roo.bootstrap.HtmlEditor
17685  * @extends Roo.bootstrap.TextArea
17686  * Bootstrap HtmlEditor class
17687
17688  * @constructor
17689  * Create a new HtmlEditor
17690  * @param {Object} config The config object
17691  */
17692
17693 Roo.bootstrap.HtmlEditor = function(config){
17694     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
17695     if (!this.toolbars) {
17696         this.toolbars = [];
17697     }
17698     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
17699     this.addEvents({
17700             /**
17701              * @event initialize
17702              * Fires when the editor is fully initialized (including the iframe)
17703              * @param {HtmlEditor} this
17704              */
17705             initialize: true,
17706             /**
17707              * @event activate
17708              * Fires when the editor is first receives the focus. Any insertion must wait
17709              * until after this event.
17710              * @param {HtmlEditor} this
17711              */
17712             activate: true,
17713              /**
17714              * @event beforesync
17715              * Fires before the textarea is updated with content from the editor iframe. Return false
17716              * to cancel the sync.
17717              * @param {HtmlEditor} this
17718              * @param {String} html
17719              */
17720             beforesync: true,
17721              /**
17722              * @event beforepush
17723              * Fires before the iframe editor is updated with content from the textarea. Return false
17724              * to cancel the push.
17725              * @param {HtmlEditor} this
17726              * @param {String} html
17727              */
17728             beforepush: true,
17729              /**
17730              * @event sync
17731              * Fires when the textarea is updated with content from the editor iframe.
17732              * @param {HtmlEditor} this
17733              * @param {String} html
17734              */
17735             sync: true,
17736              /**
17737              * @event push
17738              * Fires when the iframe editor is updated with content from the textarea.
17739              * @param {HtmlEditor} this
17740              * @param {String} html
17741              */
17742             push: true,
17743              /**
17744              * @event editmodechange
17745              * Fires when the editor switches edit modes
17746              * @param {HtmlEditor} this
17747              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
17748              */
17749             editmodechange: true,
17750             /**
17751              * @event editorevent
17752              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
17753              * @param {HtmlEditor} this
17754              */
17755             editorevent: true,
17756             /**
17757              * @event firstfocus
17758              * Fires when on first focus - needed by toolbars..
17759              * @param {HtmlEditor} this
17760              */
17761             firstfocus: true,
17762             /**
17763              * @event autosave
17764              * Auto save the htmlEditor value as a file into Events
17765              * @param {HtmlEditor} this
17766              */
17767             autosave: true,
17768             /**
17769              * @event savedpreview
17770              * preview the saved version of htmlEditor
17771              * @param {HtmlEditor} this
17772              */
17773             savedpreview: true
17774         });
17775 };
17776
17777
17778 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
17779     
17780     
17781       /**
17782      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
17783      */
17784     toolbars : false,
17785    
17786      /**
17787      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
17788      *                        Roo.resizable.
17789      */
17790     resizable : false,
17791      /**
17792      * @cfg {Number} height (in pixels)
17793      */   
17794     height: 300,
17795    /**
17796      * @cfg {Number} width (in pixels)
17797      */   
17798     width: false,
17799     
17800     /**
17801      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
17802      * 
17803      */
17804     stylesheets: false,
17805     
17806     // id of frame..
17807     frameId: false,
17808     
17809     // private properties
17810     validationEvent : false,
17811     deferHeight: true,
17812     initialized : false,
17813     activated : false,
17814     
17815     onFocus : Roo.emptyFn,
17816     iframePad:3,
17817     hideMode:'offsets',
17818     
17819     
17820     tbContainer : false,
17821     
17822     toolbarContainer :function() {
17823         return this.wrap.select('.x-html-editor-tb',true).first();
17824     },
17825
17826     /**
17827      * Protected method that will not generally be called directly. It
17828      * is called when the editor creates its toolbar. Override this method if you need to
17829      * add custom toolbar buttons.
17830      * @param {HtmlEditor} editor
17831      */
17832     createToolbar : function(){
17833         
17834         Roo.log("create toolbars");
17835         
17836         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
17837         this.toolbars[0].render(this.toolbarContainer());
17838         
17839         return;
17840         
17841 //        if (!editor.toolbars || !editor.toolbars.length) {
17842 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
17843 //        }
17844 //        
17845 //        for (var i =0 ; i < editor.toolbars.length;i++) {
17846 //            editor.toolbars[i] = Roo.factory(
17847 //                    typeof(editor.toolbars[i]) == 'string' ?
17848 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
17849 //                Roo.bootstrap.HtmlEditor);
17850 //            editor.toolbars[i].init(editor);
17851 //        }
17852     },
17853
17854      
17855     // private
17856     onRender : function(ct, position)
17857     {
17858        // Roo.log("Call onRender: " + this.xtype);
17859         var _t = this;
17860         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
17861       
17862         this.wrap = this.inputEl().wrap({
17863             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
17864         });
17865         
17866         this.editorcore.onRender(ct, position);
17867          
17868         if (this.resizable) {
17869             this.resizeEl = new Roo.Resizable(this.wrap, {
17870                 pinned : true,
17871                 wrap: true,
17872                 dynamic : true,
17873                 minHeight : this.height,
17874                 height: this.height,
17875                 handles : this.resizable,
17876                 width: this.width,
17877                 listeners : {
17878                     resize : function(r, w, h) {
17879                         _t.onResize(w,h); // -something
17880                     }
17881                 }
17882             });
17883             
17884         }
17885         this.createToolbar(this);
17886        
17887         
17888         if(!this.width && this.resizable){
17889             this.setSize(this.wrap.getSize());
17890         }
17891         if (this.resizeEl) {
17892             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
17893             // should trigger onReize..
17894         }
17895         
17896     },
17897
17898     // private
17899     onResize : function(w, h)
17900     {
17901         Roo.log('resize: ' +w + ',' + h );
17902         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
17903         var ew = false;
17904         var eh = false;
17905         
17906         if(this.inputEl() ){
17907             if(typeof w == 'number'){
17908                 var aw = w - this.wrap.getFrameWidth('lr');
17909                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
17910                 ew = aw;
17911             }
17912             if(typeof h == 'number'){
17913                  var tbh = -11;  // fixme it needs to tool bar size!
17914                 for (var i =0; i < this.toolbars.length;i++) {
17915                     // fixme - ask toolbars for heights?
17916                     tbh += this.toolbars[i].el.getHeight();
17917                     //if (this.toolbars[i].footer) {
17918                     //    tbh += this.toolbars[i].footer.el.getHeight();
17919                     //}
17920                 }
17921               
17922                 
17923                 
17924                 
17925                 
17926                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
17927                 ah -= 5; // knock a few pixes off for look..
17928                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
17929                 var eh = ah;
17930             }
17931         }
17932         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
17933         this.editorcore.onResize(ew,eh);
17934         
17935     },
17936
17937     /**
17938      * Toggles the editor between standard and source edit mode.
17939      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
17940      */
17941     toggleSourceEdit : function(sourceEditMode)
17942     {
17943         this.editorcore.toggleSourceEdit(sourceEditMode);
17944         
17945         if(this.editorcore.sourceEditMode){
17946             Roo.log('editor - showing textarea');
17947             
17948 //            Roo.log('in');
17949 //            Roo.log(this.syncValue());
17950             this.syncValue();
17951             this.inputEl().removeClass(['hide', 'x-hidden']);
17952             this.inputEl().dom.removeAttribute('tabIndex');
17953             this.inputEl().focus();
17954         }else{
17955             Roo.log('editor - hiding textarea');
17956 //            Roo.log('out')
17957 //            Roo.log(this.pushValue()); 
17958             this.pushValue();
17959             
17960             this.inputEl().addClass(['hide', 'x-hidden']);
17961             this.inputEl().dom.setAttribute('tabIndex', -1);
17962             //this.deferFocus();
17963         }
17964          
17965         if(this.resizable){
17966             this.setSize(this.wrap.getSize());
17967         }
17968         
17969         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
17970     },
17971  
17972     // private (for BoxComponent)
17973     adjustSize : Roo.BoxComponent.prototype.adjustSize,
17974
17975     // private (for BoxComponent)
17976     getResizeEl : function(){
17977         return this.wrap;
17978     },
17979
17980     // private (for BoxComponent)
17981     getPositionEl : function(){
17982         return this.wrap;
17983     },
17984
17985     // private
17986     initEvents : function(){
17987         this.originalValue = this.getValue();
17988     },
17989
17990 //    /**
17991 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
17992 //     * @method
17993 //     */
17994 //    markInvalid : Roo.emptyFn,
17995 //    /**
17996 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
17997 //     * @method
17998 //     */
17999 //    clearInvalid : Roo.emptyFn,
18000
18001     setValue : function(v){
18002         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
18003         this.editorcore.pushValue();
18004     },
18005
18006      
18007     // private
18008     deferFocus : function(){
18009         this.focus.defer(10, this);
18010     },
18011
18012     // doc'ed in Field
18013     focus : function(){
18014         this.editorcore.focus();
18015         
18016     },
18017       
18018
18019     // private
18020     onDestroy : function(){
18021         
18022         
18023         
18024         if(this.rendered){
18025             
18026             for (var i =0; i < this.toolbars.length;i++) {
18027                 // fixme - ask toolbars for heights?
18028                 this.toolbars[i].onDestroy();
18029             }
18030             
18031             this.wrap.dom.innerHTML = '';
18032             this.wrap.remove();
18033         }
18034     },
18035
18036     // private
18037     onFirstFocus : function(){
18038         //Roo.log("onFirstFocus");
18039         this.editorcore.onFirstFocus();
18040          for (var i =0; i < this.toolbars.length;i++) {
18041             this.toolbars[i].onFirstFocus();
18042         }
18043         
18044     },
18045     
18046     // private
18047     syncValue : function()
18048     {   
18049         this.editorcore.syncValue();
18050     },
18051     
18052     pushValue : function()
18053     {   
18054         this.editorcore.pushValue();
18055     }
18056      
18057     
18058     // hide stuff that is not compatible
18059     /**
18060      * @event blur
18061      * @hide
18062      */
18063     /**
18064      * @event change
18065      * @hide
18066      */
18067     /**
18068      * @event focus
18069      * @hide
18070      */
18071     /**
18072      * @event specialkey
18073      * @hide
18074      */
18075     /**
18076      * @cfg {String} fieldClass @hide
18077      */
18078     /**
18079      * @cfg {String} focusClass @hide
18080      */
18081     /**
18082      * @cfg {String} autoCreate @hide
18083      */
18084     /**
18085      * @cfg {String} inputType @hide
18086      */
18087     /**
18088      * @cfg {String} invalidClass @hide
18089      */
18090     /**
18091      * @cfg {String} invalidText @hide
18092      */
18093     /**
18094      * @cfg {String} msgFx @hide
18095      */
18096     /**
18097      * @cfg {String} validateOnBlur @hide
18098      */
18099 });
18100  
18101     
18102    
18103    
18104    
18105       
18106 Roo.namespace('Roo.bootstrap.htmleditor');
18107 /**
18108  * @class Roo.bootstrap.HtmlEditorToolbar1
18109  * Basic Toolbar
18110  * 
18111  * Usage:
18112  *
18113  new Roo.bootstrap.HtmlEditor({
18114     ....
18115     toolbars : [
18116         new Roo.bootstrap.HtmlEditorToolbar1({
18117             disable : { fonts: 1 , format: 1, ..., ... , ...],
18118             btns : [ .... ]
18119         })
18120     }
18121      
18122  * 
18123  * @cfg {Object} disable List of elements to disable..
18124  * @cfg {Array} btns List of additional buttons.
18125  * 
18126  * 
18127  * NEEDS Extra CSS? 
18128  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
18129  */
18130  
18131 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
18132 {
18133     
18134     Roo.apply(this, config);
18135     
18136     // default disabled, based on 'good practice'..
18137     this.disable = this.disable || {};
18138     Roo.applyIf(this.disable, {
18139         fontSize : true,
18140         colors : true,
18141         specialElements : true
18142     });
18143     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
18144     
18145     this.editor = config.editor;
18146     this.editorcore = config.editor.editorcore;
18147     
18148     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
18149     
18150     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
18151     // dont call parent... till later.
18152 }
18153 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
18154      
18155     bar : true,
18156     
18157     editor : false,
18158     editorcore : false,
18159     
18160     
18161     formats : [
18162         "p" ,  
18163         "h1","h2","h3","h4","h5","h6", 
18164         "pre", "code", 
18165         "abbr", "acronym", "address", "cite", "samp", "var",
18166         'div','span'
18167     ],
18168     
18169     onRender : function(ct, position)
18170     {
18171        // Roo.log("Call onRender: " + this.xtype);
18172         
18173        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
18174        Roo.log(this.el);
18175        this.el.dom.style.marginBottom = '0';
18176        var _this = this;
18177        var editorcore = this.editorcore;
18178        var editor= this.editor;
18179        
18180        var children = [];
18181        var btn = function(id,cmd , toggle, handler){
18182        
18183             var  event = toggle ? 'toggle' : 'click';
18184        
18185             var a = {
18186                 size : 'sm',
18187                 xtype: 'Button',
18188                 xns: Roo.bootstrap,
18189                 glyphicon : id,
18190                 cmd : id || cmd,
18191                 enableToggle:toggle !== false,
18192                 //html : 'submit'
18193                 pressed : toggle ? false : null,
18194                 listeners : {}
18195             }
18196             a.listeners[toggle ? 'toggle' : 'click'] = function() {
18197                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
18198             }
18199             children.push(a);
18200             return a;
18201        }
18202         
18203         var style = {
18204                 xtype: 'Button',
18205                 size : 'sm',
18206                 xns: Roo.bootstrap,
18207                 glyphicon : 'font',
18208                 //html : 'submit'
18209                 menu : {
18210                     xtype: 'Menu',
18211                     xns: Roo.bootstrap,
18212                     items:  []
18213                 }
18214         };
18215         Roo.each(this.formats, function(f) {
18216             style.menu.items.push({
18217                 xtype :'MenuItem',
18218                 xns: Roo.bootstrap,
18219                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
18220                 tagname : f,
18221                 listeners : {
18222                     click : function()
18223                     {
18224                         editorcore.insertTag(this.tagname);
18225                         editor.focus();
18226                     }
18227                 }
18228                 
18229             });
18230         });
18231          children.push(style);   
18232             
18233             
18234         btn('bold',false,true);
18235         btn('italic',false,true);
18236         btn('align-left', 'justifyleft',true);
18237         btn('align-center', 'justifycenter',true);
18238         btn('align-right' , 'justifyright',true);
18239         btn('link', false, false, function(btn) {
18240             //Roo.log("create link?");
18241             var url = prompt(this.createLinkText, this.defaultLinkValue);
18242             if(url && url != 'http:/'+'/'){
18243                 this.editorcore.relayCmd('createlink', url);
18244             }
18245         }),
18246         btn('list','insertunorderedlist',true);
18247         btn('pencil', false,true, function(btn){
18248                 Roo.log(this);
18249                 
18250                 this.toggleSourceEdit(btn.pressed);
18251         });
18252         /*
18253         var cog = {
18254                 xtype: 'Button',
18255                 size : 'sm',
18256                 xns: Roo.bootstrap,
18257                 glyphicon : 'cog',
18258                 //html : 'submit'
18259                 menu : {
18260                     xtype: 'Menu',
18261                     xns: Roo.bootstrap,
18262                     items:  []
18263                 }
18264         };
18265         
18266         cog.menu.items.push({
18267             xtype :'MenuItem',
18268             xns: Roo.bootstrap,
18269             html : Clean styles,
18270             tagname : f,
18271             listeners : {
18272                 click : function()
18273                 {
18274                     editorcore.insertTag(this.tagname);
18275                     editor.focus();
18276                 }
18277             }
18278             
18279         });
18280        */
18281         
18282          
18283        this.xtype = 'NavSimplebar';
18284         
18285         for(var i=0;i< children.length;i++) {
18286             
18287             this.buttons.add(this.addxtypeChild(children[i]));
18288             
18289         }
18290         
18291         editor.on('editorevent', this.updateToolbar, this);
18292     },
18293     onBtnClick : function(id)
18294     {
18295        this.editorcore.relayCmd(id);
18296        this.editorcore.focus();
18297     },
18298     
18299     /**
18300      * Protected method that will not generally be called directly. It triggers
18301      * a toolbar update by reading the markup state of the current selection in the editor.
18302      */
18303     updateToolbar: function(){
18304
18305         if(!this.editorcore.activated){
18306             this.editor.onFirstFocus(); // is this neeed?
18307             return;
18308         }
18309
18310         var btns = this.buttons; 
18311         var doc = this.editorcore.doc;
18312         btns.get('bold').setActive(doc.queryCommandState('bold'));
18313         btns.get('italic').setActive(doc.queryCommandState('italic'));
18314         //btns.get('underline').setActive(doc.queryCommandState('underline'));
18315         
18316         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
18317         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
18318         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
18319         
18320         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
18321         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
18322          /*
18323         
18324         var ans = this.editorcore.getAllAncestors();
18325         if (this.formatCombo) {
18326             
18327             
18328             var store = this.formatCombo.store;
18329             this.formatCombo.setValue("");
18330             for (var i =0; i < ans.length;i++) {
18331                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
18332                     // select it..
18333                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
18334                     break;
18335                 }
18336             }
18337         }
18338         
18339         
18340         
18341         // hides menus... - so this cant be on a menu...
18342         Roo.bootstrap.MenuMgr.hideAll();
18343         */
18344         Roo.bootstrap.MenuMgr.hideAll();
18345         //this.editorsyncValue();
18346     },
18347     onFirstFocus: function() {
18348         this.buttons.each(function(item){
18349            item.enable();
18350         });
18351     },
18352     toggleSourceEdit : function(sourceEditMode){
18353         
18354           
18355         if(sourceEditMode){
18356             Roo.log("disabling buttons");
18357            this.buttons.each( function(item){
18358                 if(item.cmd != 'pencil'){
18359                     item.disable();
18360                 }
18361             });
18362           
18363         }else{
18364             Roo.log("enabling buttons");
18365             if(this.editorcore.initialized){
18366                 this.buttons.each( function(item){
18367                     item.enable();
18368                 });
18369             }
18370             
18371         }
18372         Roo.log("calling toggole on editor");
18373         // tell the editor that it's been pressed..
18374         this.editor.toggleSourceEdit(sourceEditMode);
18375        
18376     }
18377 });
18378
18379
18380
18381
18382
18383 /**
18384  * @class Roo.bootstrap.Table.AbstractSelectionModel
18385  * @extends Roo.util.Observable
18386  * Abstract base class for grid SelectionModels.  It provides the interface that should be
18387  * implemented by descendant classes.  This class should not be directly instantiated.
18388  * @constructor
18389  */
18390 Roo.bootstrap.Table.AbstractSelectionModel = function(){
18391     this.locked = false;
18392     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
18393 };
18394
18395
18396 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
18397     /** @ignore Called by the grid automatically. Do not call directly. */
18398     init : function(grid){
18399         this.grid = grid;
18400         this.initEvents();
18401     },
18402
18403     /**
18404      * Locks the selections.
18405      */
18406     lock : function(){
18407         this.locked = true;
18408     },
18409
18410     /**
18411      * Unlocks the selections.
18412      */
18413     unlock : function(){
18414         this.locked = false;
18415     },
18416
18417     /**
18418      * Returns true if the selections are locked.
18419      * @return {Boolean}
18420      */
18421     isLocked : function(){
18422         return this.locked;
18423     }
18424 });
18425 /**
18426  * @extends Roo.bootstrap.Table.AbstractSelectionModel
18427  * @class Roo.bootstrap.Table.RowSelectionModel
18428  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
18429  * It supports multiple selections and keyboard selection/navigation. 
18430  * @constructor
18431  * @param {Object} config
18432  */
18433
18434 Roo.bootstrap.Table.RowSelectionModel = function(config){
18435     Roo.apply(this, config);
18436     this.selections = new Roo.util.MixedCollection(false, function(o){
18437         return o.id;
18438     });
18439
18440     this.last = false;
18441     this.lastActive = false;
18442
18443     this.addEvents({
18444         /**
18445              * @event selectionchange
18446              * Fires when the selection changes
18447              * @param {SelectionModel} this
18448              */
18449             "selectionchange" : true,
18450         /**
18451              * @event afterselectionchange
18452              * Fires after the selection changes (eg. by key press or clicking)
18453              * @param {SelectionModel} this
18454              */
18455             "afterselectionchange" : true,
18456         /**
18457              * @event beforerowselect
18458              * Fires when a row is selected being selected, return false to cancel.
18459              * @param {SelectionModel} this
18460              * @param {Number} rowIndex The selected index
18461              * @param {Boolean} keepExisting False if other selections will be cleared
18462              */
18463             "beforerowselect" : true,
18464         /**
18465              * @event rowselect
18466              * Fires when a row is selected.
18467              * @param {SelectionModel} this
18468              * @param {Number} rowIndex The selected index
18469              * @param {Roo.data.Record} r The record
18470              */
18471             "rowselect" : true,
18472         /**
18473              * @event rowdeselect
18474              * Fires when a row is deselected.
18475              * @param {SelectionModel} this
18476              * @param {Number} rowIndex The selected index
18477              */
18478         "rowdeselect" : true
18479     });
18480     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
18481     this.locked = false;
18482 };
18483
18484 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
18485     /**
18486      * @cfg {Boolean} singleSelect
18487      * True to allow selection of only one row at a time (defaults to false)
18488      */
18489     singleSelect : false,
18490
18491     // private
18492     initEvents : function(){
18493
18494         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
18495             this.grid.on("mousedown", this.handleMouseDown, this);
18496         }else{ // allow click to work like normal
18497             this.grid.on("rowclick", this.handleDragableRowClick, this);
18498         }
18499
18500         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
18501             "up" : function(e){
18502                 if(!e.shiftKey){
18503                     this.selectPrevious(e.shiftKey);
18504                 }else if(this.last !== false && this.lastActive !== false){
18505                     var last = this.last;
18506                     this.selectRange(this.last,  this.lastActive-1);
18507                     this.grid.getView().focusRow(this.lastActive);
18508                     if(last !== false){
18509                         this.last = last;
18510                     }
18511                 }else{
18512                     this.selectFirstRow();
18513                 }
18514                 this.fireEvent("afterselectionchange", this);
18515             },
18516             "down" : function(e){
18517                 if(!e.shiftKey){
18518                     this.selectNext(e.shiftKey);
18519                 }else if(this.last !== false && this.lastActive !== false){
18520                     var last = this.last;
18521                     this.selectRange(this.last,  this.lastActive+1);
18522                     this.grid.getView().focusRow(this.lastActive);
18523                     if(last !== false){
18524                         this.last = last;
18525                     }
18526                 }else{
18527                     this.selectFirstRow();
18528                 }
18529                 this.fireEvent("afterselectionchange", this);
18530             },
18531             scope: this
18532         });
18533
18534         var view = this.grid.view;
18535         view.on("refresh", this.onRefresh, this);
18536         view.on("rowupdated", this.onRowUpdated, this);
18537         view.on("rowremoved", this.onRemove, this);
18538     },
18539
18540     // private
18541     onRefresh : function(){
18542         var ds = this.grid.dataSource, i, v = this.grid.view;
18543         var s = this.selections;
18544         s.each(function(r){
18545             if((i = ds.indexOfId(r.id)) != -1){
18546                 v.onRowSelect(i);
18547             }else{
18548                 s.remove(r);
18549             }
18550         });
18551     },
18552
18553     // private
18554     onRemove : function(v, index, r){
18555         this.selections.remove(r);
18556     },
18557
18558     // private
18559     onRowUpdated : function(v, index, r){
18560         if(this.isSelected(r)){
18561             v.onRowSelect(index);
18562         }
18563     },
18564
18565     /**
18566      * Select records.
18567      * @param {Array} records The records to select
18568      * @param {Boolean} keepExisting (optional) True to keep existing selections
18569      */
18570     selectRecords : function(records, keepExisting){
18571         if(!keepExisting){
18572             this.clearSelections();
18573         }
18574         var ds = this.grid.dataSource;
18575         for(var i = 0, len = records.length; i < len; i++){
18576             this.selectRow(ds.indexOf(records[i]), true);
18577         }
18578     },
18579
18580     /**
18581      * Gets the number of selected rows.
18582      * @return {Number}
18583      */
18584     getCount : function(){
18585         return this.selections.length;
18586     },
18587
18588     /**
18589      * Selects the first row in the grid.
18590      */
18591     selectFirstRow : function(){
18592         this.selectRow(0);
18593     },
18594
18595     /**
18596      * Select the last row.
18597      * @param {Boolean} keepExisting (optional) True to keep existing selections
18598      */
18599     selectLastRow : function(keepExisting){
18600         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
18601     },
18602
18603     /**
18604      * Selects the row immediately following the last selected row.
18605      * @param {Boolean} keepExisting (optional) True to keep existing selections
18606      */
18607     selectNext : function(keepExisting){
18608         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
18609             this.selectRow(this.last+1, keepExisting);
18610             this.grid.getView().focusRow(this.last);
18611         }
18612     },
18613
18614     /**
18615      * Selects the row that precedes the last selected row.
18616      * @param {Boolean} keepExisting (optional) True to keep existing selections
18617      */
18618     selectPrevious : function(keepExisting){
18619         if(this.last){
18620             this.selectRow(this.last-1, keepExisting);
18621             this.grid.getView().focusRow(this.last);
18622         }
18623     },
18624
18625     /**
18626      * Returns the selected records
18627      * @return {Array} Array of selected records
18628      */
18629     getSelections : function(){
18630         return [].concat(this.selections.items);
18631     },
18632
18633     /**
18634      * Returns the first selected record.
18635      * @return {Record}
18636      */
18637     getSelected : function(){
18638         return this.selections.itemAt(0);
18639     },
18640
18641
18642     /**
18643      * Clears all selections.
18644      */
18645     clearSelections : function(fast){
18646         if(this.locked) return;
18647         if(fast !== true){
18648             var ds = this.grid.dataSource;
18649             var s = this.selections;
18650             s.each(function(r){
18651                 this.deselectRow(ds.indexOfId(r.id));
18652             }, this);
18653             s.clear();
18654         }else{
18655             this.selections.clear();
18656         }
18657         this.last = false;
18658     },
18659
18660
18661     /**
18662      * Selects all rows.
18663      */
18664     selectAll : function(){
18665         if(this.locked) return;
18666         this.selections.clear();
18667         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
18668             this.selectRow(i, true);
18669         }
18670     },
18671
18672     /**
18673      * Returns True if there is a selection.
18674      * @return {Boolean}
18675      */
18676     hasSelection : function(){
18677         return this.selections.length > 0;
18678     },
18679
18680     /**
18681      * Returns True if the specified row is selected.
18682      * @param {Number/Record} record The record or index of the record to check
18683      * @return {Boolean}
18684      */
18685     isSelected : function(index){
18686         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
18687         return (r && this.selections.key(r.id) ? true : false);
18688     },
18689
18690     /**
18691      * Returns True if the specified record id is selected.
18692      * @param {String} id The id of record to check
18693      * @return {Boolean}
18694      */
18695     isIdSelected : function(id){
18696         return (this.selections.key(id) ? true : false);
18697     },
18698
18699     // private
18700     handleMouseDown : function(e, t){
18701         var view = this.grid.getView(), rowIndex;
18702         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
18703             return;
18704         };
18705         if(e.shiftKey && this.last !== false){
18706             var last = this.last;
18707             this.selectRange(last, rowIndex, e.ctrlKey);
18708             this.last = last; // reset the last
18709             view.focusRow(rowIndex);
18710         }else{
18711             var isSelected = this.isSelected(rowIndex);
18712             if(e.button !== 0 && isSelected){
18713                 view.focusRow(rowIndex);
18714             }else if(e.ctrlKey && isSelected){
18715                 this.deselectRow(rowIndex);
18716             }else if(!isSelected){
18717                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
18718                 view.focusRow(rowIndex);
18719             }
18720         }
18721         this.fireEvent("afterselectionchange", this);
18722     },
18723     // private
18724     handleDragableRowClick :  function(grid, rowIndex, e) 
18725     {
18726         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
18727             this.selectRow(rowIndex, false);
18728             grid.view.focusRow(rowIndex);
18729              this.fireEvent("afterselectionchange", this);
18730         }
18731     },
18732     
18733     /**
18734      * Selects multiple rows.
18735      * @param {Array} rows Array of the indexes of the row to select
18736      * @param {Boolean} keepExisting (optional) True to keep existing selections
18737      */
18738     selectRows : function(rows, keepExisting){
18739         if(!keepExisting){
18740             this.clearSelections();
18741         }
18742         for(var i = 0, len = rows.length; i < len; i++){
18743             this.selectRow(rows[i], true);
18744         }
18745     },
18746
18747     /**
18748      * Selects a range of rows. All rows in between startRow and endRow are also selected.
18749      * @param {Number} startRow The index of the first row in the range
18750      * @param {Number} endRow The index of the last row in the range
18751      * @param {Boolean} keepExisting (optional) True to retain existing selections
18752      */
18753     selectRange : function(startRow, endRow, keepExisting){
18754         if(this.locked) return;
18755         if(!keepExisting){
18756             this.clearSelections();
18757         }
18758         if(startRow <= endRow){
18759             for(var i = startRow; i <= endRow; i++){
18760                 this.selectRow(i, true);
18761             }
18762         }else{
18763             for(var i = startRow; i >= endRow; i--){
18764                 this.selectRow(i, true);
18765             }
18766         }
18767     },
18768
18769     /**
18770      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
18771      * @param {Number} startRow The index of the first row in the range
18772      * @param {Number} endRow The index of the last row in the range
18773      */
18774     deselectRange : function(startRow, endRow, preventViewNotify){
18775         if(this.locked) return;
18776         for(var i = startRow; i <= endRow; i++){
18777             this.deselectRow(i, preventViewNotify);
18778         }
18779     },
18780
18781     /**
18782      * Selects a row.
18783      * @param {Number} row The index of the row to select
18784      * @param {Boolean} keepExisting (optional) True to keep existing selections
18785      */
18786     selectRow : function(index, keepExisting, preventViewNotify){
18787         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
18788         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
18789             if(!keepExisting || this.singleSelect){
18790                 this.clearSelections();
18791             }
18792             var r = this.grid.dataSource.getAt(index);
18793             this.selections.add(r);
18794             this.last = this.lastActive = index;
18795             if(!preventViewNotify){
18796                 this.grid.getView().onRowSelect(index);
18797             }
18798             this.fireEvent("rowselect", this, index, r);
18799             this.fireEvent("selectionchange", this);
18800         }
18801     },
18802
18803     /**
18804      * Deselects a row.
18805      * @param {Number} row The index of the row to deselect
18806      */
18807     deselectRow : function(index, preventViewNotify){
18808         if(this.locked) return;
18809         if(this.last == index){
18810             this.last = false;
18811         }
18812         if(this.lastActive == index){
18813             this.lastActive = false;
18814         }
18815         var r = this.grid.dataSource.getAt(index);
18816         this.selections.remove(r);
18817         if(!preventViewNotify){
18818             this.grid.getView().onRowDeselect(index);
18819         }
18820         this.fireEvent("rowdeselect", this, index);
18821         this.fireEvent("selectionchange", this);
18822     },
18823
18824     // private
18825     restoreLast : function(){
18826         if(this._last){
18827             this.last = this._last;
18828         }
18829     },
18830
18831     // private
18832     acceptsNav : function(row, col, cm){
18833         return !cm.isHidden(col) && cm.isCellEditable(col, row);
18834     },
18835
18836     // private
18837     onEditorKey : function(field, e){
18838         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
18839         if(k == e.TAB){
18840             e.stopEvent();
18841             ed.completeEdit();
18842             if(e.shiftKey){
18843                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
18844             }else{
18845                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
18846             }
18847         }else if(k == e.ENTER && !e.ctrlKey){
18848             e.stopEvent();
18849             ed.completeEdit();
18850             if(e.shiftKey){
18851                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
18852             }else{
18853                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
18854             }
18855         }else if(k == e.ESC){
18856             ed.cancelEdit();
18857         }
18858         if(newCell){
18859             g.startEditing(newCell[0], newCell[1]);
18860         }
18861     }
18862 });/*
18863  * Based on:
18864  * Ext JS Library 1.1.1
18865  * Copyright(c) 2006-2007, Ext JS, LLC.
18866  *
18867  * Originally Released Under LGPL - original licence link has changed is not relivant.
18868  *
18869  * Fork - LGPL
18870  * <script type="text/javascript">
18871  */
18872  
18873 /**
18874  * @class Roo.bootstrap.PagingToolbar
18875  * @extends Roo.Row
18876  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
18877  * @constructor
18878  * Create a new PagingToolbar
18879  * @param {Object} config The config object
18880  */
18881 Roo.bootstrap.PagingToolbar = function(config)
18882 {
18883     // old args format still supported... - xtype is prefered..
18884         // created from xtype...
18885     var ds = config.dataSource;
18886     this.toolbarItems = [];
18887     if (config.items) {
18888         this.toolbarItems = config.items;
18889 //        config.items = [];
18890     }
18891     
18892     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
18893     this.ds = ds;
18894     this.cursor = 0;
18895     if (ds) { 
18896         this.bind(ds);
18897     }
18898     
18899     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
18900     
18901 };
18902
18903 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
18904     /**
18905      * @cfg {Roo.data.Store} dataSource
18906      * The underlying data store providing the paged data
18907      */
18908     /**
18909      * @cfg {String/HTMLElement/Element} container
18910      * container The id or element that will contain the toolbar
18911      */
18912     /**
18913      * @cfg {Boolean} displayInfo
18914      * True to display the displayMsg (defaults to false)
18915      */
18916     /**
18917      * @cfg {Number} pageSize
18918      * The number of records to display per page (defaults to 20)
18919      */
18920     pageSize: 20,
18921     /**
18922      * @cfg {String} displayMsg
18923      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
18924      */
18925     displayMsg : 'Displaying {0} - {1} of {2}',
18926     /**
18927      * @cfg {String} emptyMsg
18928      * The message to display when no records are found (defaults to "No data to display")
18929      */
18930     emptyMsg : 'No data to display',
18931     /**
18932      * Customizable piece of the default paging text (defaults to "Page")
18933      * @type String
18934      */
18935     beforePageText : "Page",
18936     /**
18937      * Customizable piece of the default paging text (defaults to "of %0")
18938      * @type String
18939      */
18940     afterPageText : "of {0}",
18941     /**
18942      * Customizable piece of the default paging text (defaults to "First Page")
18943      * @type String
18944      */
18945     firstText : "First Page",
18946     /**
18947      * Customizable piece of the default paging text (defaults to "Previous Page")
18948      * @type String
18949      */
18950     prevText : "Previous Page",
18951     /**
18952      * Customizable piece of the default paging text (defaults to "Next Page")
18953      * @type String
18954      */
18955     nextText : "Next Page",
18956     /**
18957      * Customizable piece of the default paging text (defaults to "Last Page")
18958      * @type String
18959      */
18960     lastText : "Last Page",
18961     /**
18962      * Customizable piece of the default paging text (defaults to "Refresh")
18963      * @type String
18964      */
18965     refreshText : "Refresh",
18966
18967     buttons : false,
18968     // private
18969     onRender : function(ct, position) 
18970     {
18971         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
18972         this.navgroup.parentId = this.id;
18973         this.navgroup.onRender(this.el, null);
18974         // add the buttons to the navgroup
18975         
18976         if(this.displayInfo){
18977             Roo.log(this.el.select('ul.navbar-nav',true).first());
18978             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
18979             this.displayEl = this.el.select('.x-paging-info', true).first();
18980 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
18981 //            this.displayEl = navel.el.select('span',true).first();
18982         }
18983         
18984         var _this = this;
18985         
18986         if(this.buttons){
18987             Roo.each(_this.buttons, function(e){
18988                Roo.factory(e).onRender(_this.el, null);
18989             });
18990         }
18991             
18992         Roo.each(_this.toolbarItems, function(e) {
18993             _this.navgroup.addItem(e);
18994         });
18995         
18996         this.first = this.navgroup.addItem({
18997             tooltip: this.firstText,
18998             cls: "prev",
18999             icon : 'fa fa-backward',
19000             disabled: true,
19001             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
19002         });
19003         
19004         this.prev =  this.navgroup.addItem({
19005             tooltip: this.prevText,
19006             cls: "prev",
19007             icon : 'fa fa-step-backward',
19008             disabled: true,
19009             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
19010         });
19011     //this.addSeparator();
19012         
19013         
19014         var field = this.navgroup.addItem( {
19015             tagtype : 'span',
19016             cls : 'x-paging-position',
19017             
19018             html : this.beforePageText  +
19019                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
19020                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
19021          } ); //?? escaped?
19022         
19023         this.field = field.el.select('input', true).first();
19024         this.field.on("keydown", this.onPagingKeydown, this);
19025         this.field.on("focus", function(){this.dom.select();});
19026     
19027     
19028         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
19029         //this.field.setHeight(18);
19030         //this.addSeparator();
19031         this.next = this.navgroup.addItem({
19032             tooltip: this.nextText,
19033             cls: "next",
19034             html : ' <i class="fa fa-step-forward">',
19035             disabled: true,
19036             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
19037         });
19038         this.last = this.navgroup.addItem({
19039             tooltip: this.lastText,
19040             icon : 'fa fa-forward',
19041             cls: "next",
19042             disabled: true,
19043             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
19044         });
19045     //this.addSeparator();
19046         this.loading = this.navgroup.addItem({
19047             tooltip: this.refreshText,
19048             icon: 'fa fa-refresh',
19049             
19050             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
19051         });
19052
19053     },
19054
19055     // private
19056     updateInfo : function(){
19057         if(this.displayEl){
19058             var count = this.ds.getCount();
19059             var msg = count == 0 ?
19060                 this.emptyMsg :
19061                 String.format(
19062                     this.displayMsg,
19063                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
19064                 );
19065             this.displayEl.update(msg);
19066         }
19067     },
19068
19069     // private
19070     onLoad : function(ds, r, o){
19071        this.cursor = o.params ? o.params.start : 0;
19072        var d = this.getPageData(),
19073             ap = d.activePage,
19074             ps = d.pages;
19075         
19076        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
19077        this.field.dom.value = ap;
19078        this.first.setDisabled(ap == 1);
19079        this.prev.setDisabled(ap == 1);
19080        this.next.setDisabled(ap == ps);
19081        this.last.setDisabled(ap == ps);
19082        this.loading.enable();
19083        this.updateInfo();
19084     },
19085
19086     // private
19087     getPageData : function(){
19088         var total = this.ds.getTotalCount();
19089         return {
19090             total : total,
19091             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
19092             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
19093         };
19094     },
19095
19096     // private
19097     onLoadError : function(){
19098         this.loading.enable();
19099     },
19100
19101     // private
19102     onPagingKeydown : function(e){
19103         var k = e.getKey();
19104         var d = this.getPageData();
19105         if(k == e.RETURN){
19106             var v = this.field.dom.value, pageNum;
19107             if(!v || isNaN(pageNum = parseInt(v, 10))){
19108                 this.field.dom.value = d.activePage;
19109                 return;
19110             }
19111             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
19112             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
19113             e.stopEvent();
19114         }
19115         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))
19116         {
19117           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
19118           this.field.dom.value = pageNum;
19119           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
19120           e.stopEvent();
19121         }
19122         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
19123         {
19124           var v = this.field.dom.value, pageNum; 
19125           var increment = (e.shiftKey) ? 10 : 1;
19126           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
19127             increment *= -1;
19128           if(!v || isNaN(pageNum = parseInt(v, 10))) {
19129             this.field.dom.value = d.activePage;
19130             return;
19131           }
19132           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
19133           {
19134             this.field.dom.value = parseInt(v, 10) + increment;
19135             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
19136             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
19137           }
19138           e.stopEvent();
19139         }
19140     },
19141
19142     // private
19143     beforeLoad : function(){
19144         if(this.loading){
19145             this.loading.disable();
19146         }
19147     },
19148
19149     // private
19150     onClick : function(which){
19151         var ds = this.ds;
19152         if (!ds) {
19153             return;
19154         }
19155         switch(which){
19156             case "first":
19157                 ds.load({params:{start: 0, limit: this.pageSize}});
19158             break;
19159             case "prev":
19160                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
19161             break;
19162             case "next":
19163                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
19164             break;
19165             case "last":
19166                 var total = ds.getTotalCount();
19167                 var extra = total % this.pageSize;
19168                 var lastStart = extra ? (total - extra) : total-this.pageSize;
19169                 ds.load({params:{start: lastStart, limit: this.pageSize}});
19170             break;
19171             case "refresh":
19172                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
19173             break;
19174         }
19175     },
19176
19177     /**
19178      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
19179      * @param {Roo.data.Store} store The data store to unbind
19180      */
19181     unbind : function(ds){
19182         ds.un("beforeload", this.beforeLoad, this);
19183         ds.un("load", this.onLoad, this);
19184         ds.un("loadexception", this.onLoadError, this);
19185         ds.un("remove", this.updateInfo, this);
19186         ds.un("add", this.updateInfo, this);
19187         this.ds = undefined;
19188     },
19189
19190     /**
19191      * Binds the paging toolbar to the specified {@link Roo.data.Store}
19192      * @param {Roo.data.Store} store The data store to bind
19193      */
19194     bind : function(ds){
19195         ds.on("beforeload", this.beforeLoad, this);
19196         ds.on("load", this.onLoad, this);
19197         ds.on("loadexception", this.onLoadError, this);
19198         ds.on("remove", this.updateInfo, this);
19199         ds.on("add", this.updateInfo, this);
19200         this.ds = ds;
19201     }
19202 });/*
19203  * - LGPL
19204  *
19205  * element
19206  * 
19207  */
19208
19209 /**
19210  * @class Roo.bootstrap.MessageBar
19211  * @extends Roo.bootstrap.Component
19212  * Bootstrap MessageBar class
19213  * @cfg {String} html contents of the MessageBar
19214  * @cfg {String} weight (info | success | warning | danger) default info
19215  * @cfg {String} beforeClass insert the bar before the given class
19216  * @cfg {Boolean} closable (true | false) default false
19217  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
19218  * 
19219  * @constructor
19220  * Create a new Element
19221  * @param {Object} config The config object
19222  */
19223
19224 Roo.bootstrap.MessageBar = function(config){
19225     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
19226 };
19227
19228 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
19229     
19230     html: '',
19231     weight: 'info',
19232     closable: false,
19233     fixed: false,
19234     beforeClass: 'bootstrap-sticky-wrap',
19235     
19236     getAutoCreate : function(){
19237         
19238         var cfg = {
19239             tag: 'div',
19240             cls: 'alert alert-dismissable alert-' + this.weight,
19241             cn: [
19242                 {
19243                     tag: 'span',
19244                     cls: 'message',
19245                     html: this.html || ''
19246                 }
19247             ]
19248         }
19249         
19250         if(this.fixed){
19251             cfg.cls += ' alert-messages-fixed';
19252         }
19253         
19254         if(this.closable){
19255             cfg.cn.push({
19256                 tag: 'button',
19257                 cls: 'close',
19258                 html: 'x'
19259             });
19260         }
19261         
19262         return cfg;
19263     },
19264     
19265     onRender : function(ct, position)
19266     {
19267         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19268         
19269         if(!this.el){
19270             var cfg = Roo.apply({},  this.getAutoCreate());
19271             cfg.id = Roo.id();
19272             
19273             if (this.cls) {
19274                 cfg.cls += ' ' + this.cls;
19275             }
19276             if (this.style) {
19277                 cfg.style = this.style;
19278             }
19279             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
19280             
19281             this.el.setVisibilityMode(Roo.Element.DISPLAY);
19282         }
19283         
19284         this.el.select('>button.close').on('click', this.hide, this);
19285         
19286     },
19287     
19288     show : function()
19289     {
19290         if (!this.rendered) {
19291             this.render();
19292         }
19293         
19294         this.el.show();
19295         
19296         this.fireEvent('show', this);
19297         
19298     },
19299     
19300     hide : function()
19301     {
19302         if (!this.rendered) {
19303             this.render();
19304         }
19305         
19306         this.el.hide();
19307         
19308         this.fireEvent('hide', this);
19309     },
19310     
19311     update : function()
19312     {
19313 //        var e = this.el.dom.firstChild;
19314 //        
19315 //        if(this.closable){
19316 //            e = e.nextSibling;
19317 //        }
19318 //        
19319 //        e.data = this.html || '';
19320
19321         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
19322     }
19323    
19324 });
19325
19326  
19327
19328      /*
19329  * - LGPL
19330  *
19331  * Graph
19332  * 
19333  */
19334
19335
19336 /**
19337  * @class Roo.bootstrap.Graph
19338  * @extends Roo.bootstrap.Component
19339  * Bootstrap Graph class
19340 > Prameters
19341  -sm {number} sm 4
19342  -md {number} md 5
19343  @cfg {String} graphtype  bar | vbar | pie
19344  @cfg {number} g_x coodinator | centre x (pie)
19345  @cfg {number} g_y coodinator | centre y (pie)
19346  @cfg {number} g_r radius (pie)
19347  @cfg {number} g_height height of the chart (respected by all elements in the set)
19348  @cfg {number} g_width width of the chart (respected by all elements in the set)
19349  @cfg {Object} title The title of the chart
19350     
19351  -{Array}  values
19352  -opts (object) options for the chart 
19353      o {
19354      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
19355      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
19356      o vgutter (number)
19357      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.
19358      o stacked (boolean) whether or not to tread values as in a stacked bar chart
19359      o to
19360      o stretch (boolean)
19361      o }
19362  -opts (object) options for the pie
19363      o{
19364      o cut
19365      o startAngle (number)
19366      o endAngle (number)
19367      } 
19368  *
19369  * @constructor
19370  * Create a new Input
19371  * @param {Object} config The config object
19372  */
19373
19374 Roo.bootstrap.Graph = function(config){
19375     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
19376     
19377     this.addEvents({
19378         // img events
19379         /**
19380          * @event click
19381          * The img click event for the img.
19382          * @param {Roo.EventObject} e
19383          */
19384         "click" : true
19385     });
19386 };
19387
19388 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
19389     
19390     sm: 4,
19391     md: 5,
19392     graphtype: 'bar',
19393     g_height: 250,
19394     g_width: 400,
19395     g_x: 50,
19396     g_y: 50,
19397     g_r: 30,
19398     opts:{
19399         //g_colors: this.colors,
19400         g_type: 'soft',
19401         g_gutter: '20%'
19402
19403     },
19404     title : false,
19405
19406     getAutoCreate : function(){
19407         
19408         var cfg = {
19409             tag: 'div',
19410             html : null
19411         }
19412         
19413         
19414         return  cfg;
19415     },
19416
19417     onRender : function(ct,position){
19418         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
19419         this.raphael = Raphael(this.el.dom);
19420         
19421                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19422                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19423                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19424                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
19425                 /*
19426                 r.text(160, 10, "Single Series Chart").attr(txtattr);
19427                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
19428                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
19429                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
19430                 
19431                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
19432                 r.barchart(330, 10, 300, 220, data1);
19433                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
19434                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
19435                 */
19436                 
19437                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19438                 // r.barchart(30, 30, 560, 250,  xdata, {
19439                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
19440                 //     axis : "0 0 1 1",
19441                 //     axisxlabels :  xdata
19442                 //     //yvalues : cols,
19443                    
19444                 // });
19445 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19446 //        
19447 //        this.load(null,xdata,{
19448 //                axis : "0 0 1 1",
19449 //                axisxlabels :  xdata
19450 //                });
19451
19452     },
19453
19454     load : function(graphtype,xdata,opts){
19455         this.raphael.clear();
19456         if(!graphtype) {
19457             graphtype = this.graphtype;
19458         }
19459         if(!opts){
19460             opts = this.opts;
19461         }
19462         var r = this.raphael,
19463             fin = function () {
19464                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
19465             },
19466             fout = function () {
19467                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
19468             },
19469             pfin = function() {
19470                 this.sector.stop();
19471                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
19472
19473                 if (this.label) {
19474                     this.label[0].stop();
19475                     this.label[0].attr({ r: 7.5 });
19476                     this.label[1].attr({ "font-weight": 800 });
19477                 }
19478             },
19479             pfout = function() {
19480                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
19481
19482                 if (this.label) {
19483                     this.label[0].animate({ r: 5 }, 500, "bounce");
19484                     this.label[1].attr({ "font-weight": 400 });
19485                 }
19486             };
19487
19488         switch(graphtype){
19489             case 'bar':
19490                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
19491                 break;
19492             case 'hbar':
19493                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
19494                 break;
19495             case 'pie':
19496 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
19497 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
19498 //            
19499                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
19500                 
19501                 break;
19502
19503         }
19504         
19505         if(this.title){
19506             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
19507         }
19508         
19509     },
19510     
19511     setTitle: function(o)
19512     {
19513         this.title = o;
19514     },
19515     
19516     initEvents: function() {
19517         
19518         if(!this.href){
19519             this.el.on('click', this.onClick, this);
19520         }
19521     },
19522     
19523     onClick : function(e)
19524     {
19525         Roo.log('img onclick');
19526         this.fireEvent('click', this, e);
19527     }
19528    
19529 });
19530
19531  
19532 /*
19533  * - LGPL
19534  *
19535  * numberBox
19536  * 
19537  */
19538 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19539
19540 /**
19541  * @class Roo.bootstrap.dash.NumberBox
19542  * @extends Roo.bootstrap.Component
19543  * Bootstrap NumberBox class
19544  * @cfg {String} bgcolor Box background color, such as (aqua | green | yellow | red etc..) Default aqua
19545  * @cfg {String} headline Box headline
19546  * @cfg {String} content Box content
19547  * @cfg {String} icon Box icon
19548  * @cfg {String} footer Footer text
19549  * @cfg {String} fhref Footer href
19550  * 
19551  * @constructor
19552  * Create a new NumberBox
19553  * @param {Object} config The config object
19554  */
19555
19556
19557 Roo.bootstrap.dash.NumberBox = function(config){
19558     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
19559     
19560 };
19561
19562 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
19563     
19564     bgcolor : 'aqua',
19565     headline : '',
19566     content : '',
19567     icon : '',
19568     footer : '',
19569     fhref : '',
19570     ficon : '',
19571     
19572     getAutoCreate : function(){
19573         
19574         var cfg = {
19575             tag : 'div',
19576             cls : 'small-box bg-' + this.bgcolor,
19577             cn : [
19578                 {
19579                     tag : 'div',
19580                     cls : 'inner',
19581                     cn :[
19582                         {
19583                             tag : 'h3',
19584                             cls : 'roo-headline',
19585                             html : this.headline
19586                         },
19587                         {
19588                             tag : 'p',
19589                             cls : 'roo-content',
19590                             html : this.content
19591                         }
19592                     ]
19593                 }
19594             ]
19595         }
19596         
19597         if(this.icon){
19598             cfg.cn.push({
19599                 tag : 'div',
19600                 cls : 'icon',
19601                 cn :[
19602                     {
19603                         tag : 'i',
19604                         cls : 'ion ' + this.icon
19605                     }
19606                 ]
19607             });
19608         }
19609         
19610         if(this.footer){
19611             var footer = {
19612                 tag : 'a',
19613                 cls : 'small-box-footer',
19614                 href : this.fhref || '#',
19615                 html : this.footer
19616             };
19617             
19618             cfg.cn.push(footer);
19619             
19620         }
19621         
19622         return  cfg;
19623     },
19624
19625     onRender : function(ct,position){
19626         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
19627
19628
19629        
19630                 
19631     },
19632
19633     setHeadline: function (value)
19634     {
19635         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
19636     },
19637     
19638     setFooter: function (value, href)
19639     {
19640         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
19641         
19642         if(href){
19643             this.el.select('a.small-box-footer',true).first().attr('href', href);
19644         }
19645         
19646     },
19647
19648     setContent: function (value)
19649     {
19650         this.el.select('.roo-content',true).first().dom.innerHTML = value;
19651     },
19652
19653     initEvents: function() 
19654     {   
19655         
19656     }
19657     
19658 });
19659
19660  
19661 /*
19662  * - LGPL
19663  *
19664  * TabBox
19665  * 
19666  */
19667 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19668
19669 /**
19670  * @class Roo.bootstrap.dash.TabBox
19671  * @extends Roo.bootstrap.Component
19672  * Bootstrap TabBox class
19673  * @cfg {String} title Title of the TabBox
19674  * @cfg {String} icon Icon of the TabBox
19675  * @cfg {Boolean} showtabs (true|false) show the tabs default true
19676  * 
19677  * @constructor
19678  * Create a new TabBox
19679  * @param {Object} config The config object
19680  */
19681
19682
19683 Roo.bootstrap.dash.TabBox = function(config){
19684     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
19685     this.addEvents({
19686         // raw events
19687         /**
19688          * @event addpane
19689          * When a pane is added
19690          * @param {Roo.bootstrap.dash.TabPane} pane
19691          */
19692         "addpane" : true
19693          
19694     });
19695 };
19696
19697 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
19698
19699     title : '',
19700     icon : false,
19701     showtabs : true,
19702     
19703     getChildContainer : function()
19704     {
19705         return this.el.select('.tab-content', true).first();
19706     },
19707     
19708     getAutoCreate : function(){
19709         
19710         var header = {
19711             tag: 'li',
19712             cls: 'pull-left header',
19713             html: this.title,
19714             cn : []
19715         };
19716         
19717         if(this.icon){
19718             header.cn.push({
19719                 tag: 'i',
19720                 cls: 'fa ' + this.icon
19721             });
19722         }
19723         
19724         
19725         var cfg = {
19726             tag: 'div',
19727             cls: 'nav-tabs-custom',
19728             cn: [
19729                 {
19730                     tag: 'ul',
19731                     cls: 'nav nav-tabs pull-right',
19732                     cn: [
19733                         header
19734                     ]
19735                 },
19736                 {
19737                     tag: 'div',
19738                     cls: 'tab-content no-padding',
19739                     cn: []
19740                 }
19741             ]
19742         }
19743
19744         return  cfg;
19745     },
19746     initEvents : function()
19747     {
19748         //Roo.log('add add pane handler');
19749         this.on('addpane', this.onAddPane, this);
19750     },
19751      /**
19752      * Updates the box title
19753      * @param {String} html to set the title to.
19754      */
19755     setTitle : function(value)
19756     {
19757         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
19758     },
19759     onAddPane : function(pane)
19760     {
19761         //Roo.log('addpane');
19762         //Roo.log(pane);
19763         // tabs are rendere left to right..
19764         if(!this.showtabs){
19765             return;
19766         }
19767         
19768         var ctr = this.el.select('.nav-tabs', true).first();
19769          
19770          
19771         var existing = ctr.select('.nav-tab',true);
19772         var qty = existing.getCount();;
19773         
19774         
19775         var tab = ctr.createChild({
19776             tag : 'li',
19777             cls : 'nav-tab' + (qty ? '' : ' active'),
19778             cn : [
19779                 {
19780                     tag : 'a',
19781                     href:'#',
19782                     html : pane.title
19783                 }
19784             ]
19785         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
19786         pane.tab = tab;
19787         
19788         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
19789         if (!qty) {
19790             pane.el.addClass('active');
19791         }
19792         
19793                 
19794     },
19795     onTabClick : function(ev,un,ob,pane)
19796     {
19797         //Roo.log('tab - prev default');
19798         ev.preventDefault();
19799         
19800         
19801         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
19802         pane.tab.addClass('active');
19803         //Roo.log(pane.title);
19804         this.getChildContainer().select('.tab-pane',true).removeClass('active');
19805         // technically we should have a deactivate event.. but maybe add later.
19806         // and it should not de-activate the selected tab...
19807         
19808         pane.el.addClass('active');
19809         pane.fireEvent('activate');
19810         
19811         
19812     }
19813     
19814     
19815 });
19816
19817  
19818 /*
19819  * - LGPL
19820  *
19821  * Tab pane
19822  * 
19823  */
19824 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19825 /**
19826  * @class Roo.bootstrap.TabPane
19827  * @extends Roo.bootstrap.Component
19828  * Bootstrap TabPane class
19829  * @cfg {Boolean} active (false | true) Default false
19830  * @cfg {String} title title of panel
19831
19832  * 
19833  * @constructor
19834  * Create a new TabPane
19835  * @param {Object} config The config object
19836  */
19837
19838 Roo.bootstrap.dash.TabPane = function(config){
19839     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
19840     
19841     this.addEvents({
19842         // raw events
19843         /**
19844          * @event activate
19845          * When a pane is activated
19846          * @param {Roo.bootstrap.dash.TabPane} pane
19847          */
19848         "activate" : true
19849          
19850     });
19851 };
19852
19853 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
19854     
19855     active : false,
19856     title : '',
19857     
19858     // the tabBox that this is attached to.
19859     tab : false,
19860      
19861     getAutoCreate : function() 
19862     {
19863         var cfg = {
19864             tag: 'div',
19865             cls: 'tab-pane'
19866         }
19867         
19868         if(this.active){
19869             cfg.cls += ' active';
19870         }
19871         
19872         return cfg;
19873     },
19874     initEvents  : function()
19875     {
19876         //Roo.log('trigger add pane handler');
19877         this.parent().fireEvent('addpane', this)
19878     },
19879     
19880      /**
19881      * Updates the tab title 
19882      * @param {String} html to set the title to.
19883      */
19884     setTitle: function(str)
19885     {
19886         if (!this.tab) {
19887             return;
19888         }
19889         this.title = str;
19890         this.tab.select('a', true).first().dom.innerHTML = str;
19891         
19892     }
19893     
19894     
19895     
19896 });
19897
19898  
19899
19900
19901  /*
19902  * - LGPL
19903  *
19904  * menu
19905  * 
19906  */
19907 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
19908
19909 /**
19910  * @class Roo.bootstrap.menu.Menu
19911  * @extends Roo.bootstrap.Component
19912  * Bootstrap Menu class - container for Menu
19913  * @cfg {String} html Text of the menu
19914  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
19915  * @cfg {String} icon Font awesome icon
19916  * @cfg {String} pos Menu align to (top | bottom) default bottom
19917  * 
19918  * 
19919  * @constructor
19920  * Create a new Menu
19921  * @param {Object} config The config object
19922  */
19923
19924
19925 Roo.bootstrap.menu.Menu = function(config){
19926     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
19927     
19928     this.addEvents({
19929         /**
19930          * @event beforeshow
19931          * Fires before this menu is displayed
19932          * @param {Roo.bootstrap.menu.Menu} this
19933          */
19934         beforeshow : true,
19935         /**
19936          * @event beforehide
19937          * Fires before this menu is hidden
19938          * @param {Roo.bootstrap.menu.Menu} this
19939          */
19940         beforehide : true,
19941         /**
19942          * @event show
19943          * Fires after this menu is displayed
19944          * @param {Roo.bootstrap.menu.Menu} this
19945          */
19946         show : true,
19947         /**
19948          * @event hide
19949          * Fires after this menu is hidden
19950          * @param {Roo.bootstrap.menu.Menu} this
19951          */
19952         hide : true,
19953         /**
19954          * @event click
19955          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19956          * @param {Roo.bootstrap.menu.Menu} this
19957          * @param {Roo.EventObject} e
19958          */
19959         click : true
19960     });
19961     
19962 };
19963
19964 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
19965     
19966     submenu : false,
19967     html : '',
19968     weight : 'default',
19969     icon : false,
19970     pos : 'bottom',
19971     
19972     
19973     getChildContainer : function() {
19974         if(this.isSubMenu){
19975             return this.el;
19976         }
19977         
19978         return this.el.select('ul.dropdown-menu', true).first();  
19979     },
19980     
19981     getAutoCreate : function()
19982     {
19983         var text = [
19984             {
19985                 tag : 'span',
19986                 cls : 'roo-menu-text',
19987                 html : this.html
19988             }
19989         ];
19990         
19991         if(this.icon){
19992             text.unshift({
19993                 tag : 'i',
19994                 cls : 'fa ' + this.icon
19995             })
19996         }
19997         
19998         
19999         var cfg = {
20000             tag : 'div',
20001             cls : 'btn-group',
20002             cn : [
20003                 {
20004                     tag : 'button',
20005                     cls : 'dropdown-button btn btn-' + this.weight,
20006                     cn : text
20007                 },
20008                 {
20009                     tag : 'button',
20010                     cls : 'dropdown-toggle btn btn-' + this.weight,
20011                     cn : [
20012                         {
20013                             tag : 'span',
20014                             cls : 'caret'
20015                         }
20016                     ]
20017                 },
20018                 {
20019                     tag : 'ul',
20020                     cls : 'dropdown-menu'
20021                 }
20022             ]
20023             
20024         };
20025         
20026         if(this.pos == 'top'){
20027             cfg.cls += ' dropup';
20028         }
20029         
20030         if(this.isSubMenu){
20031             cfg = {
20032                 tag : 'ul',
20033                 cls : 'dropdown-menu'
20034             }
20035         }
20036         
20037         return cfg;
20038     },
20039     
20040     onRender : function(ct, position)
20041     {
20042         this.isSubMenu = ct.hasClass('dropdown-submenu');
20043         
20044         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
20045     },
20046     
20047     initEvents : function() 
20048     {
20049         if(this.isSubMenu){
20050             return;
20051         }
20052         
20053         this.hidden = true;
20054         
20055         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
20056         this.triggerEl.on('click', this.onTriggerPress, this);
20057         
20058         this.buttonEl = this.el.select('button.dropdown-button', true).first();
20059         this.buttonEl.on('click', this.onClick, this);
20060         
20061     },
20062     
20063     list : function()
20064     {
20065         if(this.isSubMenu){
20066             return this.el;
20067         }
20068         
20069         return this.el.select('ul.dropdown-menu', true).first();
20070     },
20071     
20072     onClick : function(e)
20073     {
20074         this.fireEvent("click", this, e);
20075     },
20076     
20077     onTriggerPress  : function(e)
20078     {   
20079         if (this.isVisible()) {
20080             this.hide();
20081         } else {
20082             this.show();
20083         }
20084     },
20085     
20086     isVisible : function(){
20087         return !this.hidden;
20088     },
20089     
20090     show : function()
20091     {
20092         this.fireEvent("beforeshow", this);
20093         
20094         this.hidden = false;
20095         this.el.addClass('open');
20096         
20097         Roo.get(document).on("mouseup", this.onMouseUp, this);
20098         
20099         this.fireEvent("show", this);
20100         
20101         
20102     },
20103     
20104     hide : function()
20105     {
20106         this.fireEvent("beforehide", this);
20107         
20108         this.hidden = true;
20109         this.el.removeClass('open');
20110         
20111         Roo.get(document).un("mouseup", this.onMouseUp);
20112         
20113         this.fireEvent("hide", this);
20114     },
20115     
20116     onMouseUp : function()
20117     {
20118         this.hide();
20119     }
20120     
20121 });
20122
20123  
20124  /*
20125  * - LGPL
20126  *
20127  * menu item
20128  * 
20129  */
20130 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20131
20132 /**
20133  * @class Roo.bootstrap.menu.Item
20134  * @extends Roo.bootstrap.Component
20135  * Bootstrap MenuItem class
20136  * @cfg {Boolean} submenu (true | false) default false
20137  * @cfg {String} html text of the item
20138  * @cfg {String} href the link
20139  * @cfg {Boolean} disable (true | false) default false
20140  * @cfg {Boolean} preventDefault (true | false) default true
20141  * @cfg {String} icon Font awesome icon
20142  * @cfg {String} pos Submenu align to (left | right) default right 
20143  * 
20144  * 
20145  * @constructor
20146  * Create a new Item
20147  * @param {Object} config The config object
20148  */
20149
20150
20151 Roo.bootstrap.menu.Item = function(config){
20152     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
20153     this.addEvents({
20154         /**
20155          * @event mouseover
20156          * Fires when the mouse is hovering over this menu
20157          * @param {Roo.bootstrap.menu.Item} this
20158          * @param {Roo.EventObject} e
20159          */
20160         mouseover : true,
20161         /**
20162          * @event mouseout
20163          * Fires when the mouse exits this menu
20164          * @param {Roo.bootstrap.menu.Item} this
20165          * @param {Roo.EventObject} e
20166          */
20167         mouseout : true,
20168         // raw events
20169         /**
20170          * @event click
20171          * The raw click event for the entire grid.
20172          * @param {Roo.EventObject} e
20173          */
20174         click : true
20175     });
20176 };
20177
20178 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
20179     
20180     submenu : false,
20181     href : '',
20182     html : '',
20183     preventDefault: true,
20184     disable : false,
20185     icon : false,
20186     pos : 'right',
20187     
20188     getAutoCreate : function()
20189     {
20190         var text = [
20191             {
20192                 tag : 'span',
20193                 cls : 'roo-menu-item-text',
20194                 html : this.html
20195             }
20196         ];
20197         
20198         if(this.icon){
20199             text.unshift({
20200                 tag : 'i',
20201                 cls : 'fa ' + this.icon
20202             })
20203         }
20204         
20205         var cfg = {
20206             tag : 'li',
20207             cn : [
20208                 {
20209                     tag : 'a',
20210                     href : this.href || '#',
20211                     cn : text
20212                 }
20213             ]
20214         };
20215         
20216         if(this.disable){
20217             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
20218         }
20219         
20220         if(this.submenu){
20221             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
20222             
20223             if(this.pos == 'left'){
20224                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
20225             }
20226         }
20227         
20228         return cfg;
20229     },
20230     
20231     initEvents : function() 
20232     {
20233         this.el.on('mouseover', this.onMouseOver, this);
20234         this.el.on('mouseout', this.onMouseOut, this);
20235         
20236         this.el.select('a', true).first().on('click', this.onClick, this);
20237         
20238     },
20239     
20240     onClick : function(e)
20241     {
20242         if(this.preventDefault){
20243             e.preventDefault();
20244         }
20245         
20246         this.fireEvent("click", this, e);
20247     },
20248     
20249     onMouseOver : function(e)
20250     {
20251         if(this.submenu && this.pos == 'left'){
20252             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
20253         }
20254         
20255         this.fireEvent("mouseover", this, e);
20256     },
20257     
20258     onMouseOut : function(e)
20259     {
20260         this.fireEvent("mouseout", this, e);
20261     }
20262 });
20263
20264  
20265
20266  /*
20267  * - LGPL
20268  *
20269  * menu separator
20270  * 
20271  */
20272 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20273
20274 /**
20275  * @class Roo.bootstrap.menu.Separator
20276  * @extends Roo.bootstrap.Component
20277  * Bootstrap Separator class
20278  * 
20279  * @constructor
20280  * Create a new Separator
20281  * @param {Object} config The config object
20282  */
20283
20284
20285 Roo.bootstrap.menu.Separator = function(config){
20286     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
20287 };
20288
20289 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
20290     
20291     getAutoCreate : function(){
20292         var cfg = {
20293             tag : 'li',
20294             cls: 'divider'
20295         };
20296         
20297         return cfg;
20298     }
20299    
20300 });
20301
20302  
20303
20304