css/xtheme-slate.css
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * 
20  * @constructor
21  * Do not use directly - it does not do anything..
22  * @param {Object} config The config object
23  */
24
25
26
27 Roo.bootstrap.Component = function(config){
28     Roo.bootstrap.Component.superclass.constructor.call(this, config);
29 };
30
31 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
32     
33     
34     allowDomMove : false, // to stop relocations in parent onRender...
35     
36     cls : false,
37     
38     style : false,
39     
40     autoCreate : false,
41     
42     initEvents : function() {  },
43     
44     xattr : false,
45     
46     parentId : false,
47     
48     can_build_overlaid : true,
49     
50     dataId : false,
51     
52     name : false,
53     
54     parent: function() {
55         // returns the parent component..
56         return Roo.ComponentMgr.get(this.parentId)
57         
58         
59     },
60     
61     // private
62     onRender : function(ct, position)
63     {
64        // Roo.log("Call onRender: " + this.xtype);
65         
66         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
67         
68         if(this.el){
69             if (this.el.attr('xtype')) {
70                 this.el.attr('xtypex', this.el.attr('xtype'));
71                 this.el.dom.removeAttribute('xtype');
72                 
73                 this.initEvents();
74             }
75             
76             return;
77         }
78         
79          
80         
81         var cfg = Roo.apply({},  this.getAutoCreate());
82         cfg.id = Roo.id();
83         
84         // fill in the extra attributes 
85         if (this.xattr && typeof(this.xattr) =='object') {
86             for (var i in this.xattr) {
87                 cfg[i] = this.xattr[i];
88             }
89         }
90         
91         if(this.dataId){
92             cfg.dataId = this.dataId;
93         }
94         
95         if (this.cls) {
96             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
97         }
98         
99         if (this.style) { // fixme needs to support more complex style data.
100             cfg.style = this.style;
101         }
102         
103         if(this.name){
104             cfg.name = this.name;
105         }
106         
107         this.el = ct.createChild(cfg, position);
108         
109         if(this.tabIndex !== undefined){
110             this.el.dom.setAttribute('tabIndex', this.tabIndex);
111         }
112         this.initEvents();
113         
114         
115     },
116     
117     getChildContainer : function()
118     {
119         return this.el;
120     },
121     
122     
123     addxtype  : function(tree,cntr)
124     {
125         var cn = this;
126         
127         cn = Roo.factory(tree);
128            
129         cn.parentType = this.xtype; //??
130         cn.parentId = this.id;
131         
132         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
133         
134         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
135         
136         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
137         
138         var build_from_html =  Roo.XComponent.build_from_html;
139           
140         var is_body  = (tree.xtype == 'Body') ;
141           
142         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
143           
144         var self_cntr_el = Roo.get(this[cntr](false));
145         
146         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
147             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
148                 return this.addxtypeChild(tree,cntr);
149             }
150             
151             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
152                 
153             if(echild){
154                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
155             }
156             
157             Roo.log('skipping render');
158             return cn;
159             
160         }
161         
162         var ret = false;
163         
164         while (true) {
165             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
166             
167             if (!echild) {
168                 break;
169             }
170             
171             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
172                 break;
173             }
174             
175             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
176         }
177         return ret;
178     },
179     
180     addxtypeChild : function (tree, cntr)
181     {
182         Roo.log('addxtypeChild:' + cntr);
183         var cn = this;
184         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
185         
186         
187         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
188                     (typeof(tree['flexy:foreach']) != 'undefined');
189           
190         
191         
192         
193         // render the element if it's not BODY.
194         if (tree.xtype != 'Body') {
195            
196             cn = Roo.factory(tree);
197            
198             cn.parentType = this.xtype; //??
199             cn.parentId = this.id;
200             
201             var build_from_html =  Roo.XComponent.build_from_html;
202             
203             
204             // does the container contain child eleemnts with 'xtype' attributes.
205             // that match this xtype..
206             // note - when we render we create these as well..
207             // so we should check to see if body has xtype set.
208             if (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
209                
210                 var self_cntr_el = Roo.get(this[cntr](false));
211                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
212                 
213                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
214                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
215                   
216                   
217                   
218                     cn.el = echild;
219                   //  Roo.log("GOT");
220                     //echild.dom.removeAttribute('xtype');
221                 } else {
222                     Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
223                    
224                 }
225             }
226            
227             
228                
229             // if object has flexy:if - then it may or may not be rendered.
230             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
231                 // skip a flexy if element.
232                 Roo.log('skipping render');
233              } else {
234                  
235                 // actually if flexy:foreach is found, we really want to create 
236                 // multiple copies here...
237                 //Roo.log('render');
238                 //Roo.log(this[cntr]());
239                 cn.render(this[cntr](true));
240              }
241             // then add the element..
242         }
243         
244         
245         // handle the kids..
246         
247         var nitems = [];
248         /*
249         if (typeof (tree.menu) != 'undefined') {
250             tree.menu.parentType = cn.xtype;
251             tree.menu.triggerEl = cn.el;
252             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
253             
254         }
255         */
256         if (!tree.items || !tree.items.length) {
257             cn.items = nitems;
258             return cn;
259         }
260         var items = tree.items;
261         delete tree.items;
262         
263         //Roo.log(items.length);
264             // add the items..
265         for(var i =0;i < items.length;i++) {
266             nitems.push(cn.addxtype(Roo.apply({}, items[i])));
267         }
268         
269         cn.items = nitems;
270         
271         return cn;
272     }
273     
274     
275     
276     
277 });
278
279  /*
280  * - LGPL
281  *
282  * Body
283  * 
284  */
285
286 /**
287  * @class Roo.bootstrap.Body
288  * @extends Roo.bootstrap.Component
289  * Bootstrap Body class
290  * 
291  * @constructor
292  * Create a new body
293  * @param {Object} config The config object
294  */
295
296 Roo.bootstrap.Body = function(config){
297     Roo.bootstrap.Body.superclass.constructor.call(this, config);
298     this.el = Roo.get(document.body);
299     if (this.cls && this.cls.length) {
300         Roo.get(document.body).addClass(this.cls);
301     }
302 };
303
304 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
305       
306         autoCreate : {
307         cls: 'container'
308     },
309     onRender : function(ct, position)
310     {
311        /* Roo.log("Roo.bootstrap.Body - onRender");
312         if (this.cls && this.cls.length) {
313             Roo.get(document.body).addClass(this.cls);
314         }
315         // style??? xttr???
316         */
317     }
318     
319     
320  
321    
322 });
323
324  /*
325  * - LGPL
326  *
327  * button group
328  * 
329  */
330
331
332 /**
333  * @class Roo.bootstrap.ButtonGroup
334  * @extends Roo.bootstrap.Component
335  * Bootstrap ButtonGroup class
336  * @cfg {String} size lg | sm | xs (default empty normal)
337  * @cfg {String} align vertical | justified  (default none)
338  * @cfg {String} direction up | down (default down)
339  * @cfg {Boolean} toolbar false | true
340  * @cfg {Boolean} btn true | false
341  * 
342  * 
343  * @constructor
344  * Create a new Input
345  * @param {Object} config The config object
346  */
347
348 Roo.bootstrap.ButtonGroup = function(config){
349     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
350 };
351
352 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
353     
354     size: '',
355     align: '',
356     direction: '',
357     toolbar: false,
358     btn: true,
359
360     getAutoCreate : function(){
361         var cfg = {
362             cls: 'btn-group',
363             html : null
364         }
365         
366         cfg.html = this.html || cfg.html;
367         
368         if (this.toolbar) {
369             cfg = {
370                 cls: 'btn-toolbar',
371                 html: null
372             }
373             
374             return cfg;
375         }
376         
377         if (['vertical','justified'].indexOf(this.align)!==-1) {
378             cfg.cls = 'btn-group-' + this.align;
379             
380             if (this.align == 'justified') {
381                 console.log(this.items);
382             }
383         }
384         
385         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
386             cfg.cls += ' btn-group-' + this.size;
387         }
388         
389         if (this.direction == 'up') {
390             cfg.cls += ' dropup' ;
391         }
392         
393         return cfg;
394     }
395    
396 });
397
398  /*
399  * - LGPL
400  *
401  * button
402  * 
403  */
404
405 /**
406  * @class Roo.bootstrap.Button
407  * @extends Roo.bootstrap.Component
408  * Bootstrap Button class
409  * @cfg {String} html The button content
410  * @cfg {String} weight default (or empty) | primary | success | info | warning | danger | link
411  * @cfg {String} size empty | lg | sm | xs
412  * @cfg {String} tag empty | a | input | submit
413  * @cfg {String} href empty or href
414  * @cfg {Boolean} disabled false | true
415  * @cfg {Boolean} isClose false | true
416  * @cfg {String} glyphicon empty | adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out
417  * @cfg {String} badge text for badge
418  * @cfg {String} theme default (or empty) | glow
419  * @cfg {Boolean} inverse false | true
420  * @cfg {Boolean} toggle false | true
421  * @cfg {String} ontext text for on toggle state
422  * @cfg {String} offtext text for off toggle state
423  * @cfg {Boolean} defaulton true | false
424  * @cfg {Boolean} preventDefault (true | false) default true
425  * @cfg {Boolean} removeClass true | false remove the standard class..
426  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
427  * 
428  * @constructor
429  * Create a new button
430  * @param {Object} config The config object
431  */
432
433
434 Roo.bootstrap.Button = function(config){
435     Roo.bootstrap.Button.superclass.constructor.call(this, config);
436     this.addEvents({
437         // raw events
438         /**
439          * @event click
440          * When a butotn is pressed
441          * @param {Roo.EventObject} e
442          */
443         "click" : true,
444          /**
445          * @event toggle
446          * After the button has been toggles
447          * @param {Roo.EventObject} e
448          * @param {boolean} pressed (also available as button.pressed)
449          */
450         "toggle" : true
451     });
452 };
453
454 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
455     html: false,
456     active: false,
457     weight: '',
458     size: '',
459     tag: 'button',
460     href: '',
461     disabled: false,
462     isClose: false,
463     glyphicon: '',
464     badge: '',
465     theme: 'default',
466     inverse: false,
467     
468     toggle: false,
469     ontext: 'ON',
470     offtext: 'OFF',
471     defaulton: true,
472     preventDefault: true,
473     removeClass: false,
474     name: false,
475     target: false,
476     
477     
478     pressed : null,
479      
480     
481     getAutoCreate : function(){
482         
483         var cfg = {
484             tag : 'button',
485             cls : 'roo-button',
486             html: ''
487         };
488         
489         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
490             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
491             this.tag = 'button';
492         } else {
493             cfg.tag = this.tag;
494         }
495         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
496         
497         if (this.toggle == true) {
498             cfg={
499                 tag: 'div',
500                 cls: 'slider-frame roo-button',
501                 cn: [
502                     {
503                         tag: 'span',
504                         'data-on-text':'ON',
505                         'data-off-text':'OFF',
506                         cls: 'slider-button',
507                         html: this.offtext
508                     }
509                 ]
510             };
511             
512             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
513                 cfg.cls += ' '+this.weight;
514             }
515             
516             return cfg;
517         }
518         
519         if (this.isClose) {
520             cfg.cls += ' close';
521             
522             cfg["aria-hidden"] = true;
523             
524             cfg.html = "&times;";
525             
526             return cfg;
527         }
528         
529          
530         if (this.theme==='default') {
531             cfg.cls = 'btn roo-button';
532             
533             //if (this.parentType != 'Navbar') {
534             this.weight = this.weight.length ?  this.weight : 'default';
535             //}
536             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
537                 
538                 cfg.cls += ' btn-' + this.weight;
539             }
540         } else if (this.theme==='glow') {
541             
542             cfg.tag = 'a';
543             cfg.cls = 'btn-glow roo-button';
544             
545             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
546                 
547                 cfg.cls += ' ' + this.weight;
548             }
549         }
550    
551         
552         if (this.inverse) {
553             this.cls += ' inverse';
554         }
555         
556         
557         if (this.active) {
558             cfg.cls += ' active';
559         }
560         
561         if (this.disabled) {
562             cfg.disabled = 'disabled';
563         }
564         
565         if (this.items) {
566             Roo.log('changing to ul' );
567             cfg.tag = 'ul';
568             this.glyphicon = 'caret';
569         }
570         
571         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
572          
573         //gsRoo.log(this.parentType);
574         if (this.parentType === 'Navbar' && !this.parent().bar) {
575             Roo.log('changing to li?');
576             
577             cfg.tag = 'li';
578             
579             cfg.cls = '';
580             cfg.cn =  [{
581                 tag : 'a',
582                 cls : 'roo-button',
583                 html : this.html,
584                 href : this.href || '#'
585             }];
586             if (this.menu) {
587                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
588                 cfg.cls += ' dropdown';
589             }   
590             
591             delete cfg.html;
592             
593         }
594         
595        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
596         
597         if (this.glyphicon) {
598             cfg.html = ' ' + cfg.html;
599             
600             cfg.cn = [
601                 {
602                     tag: 'span',
603                     cls: 'glyphicon glyphicon-' + this.glyphicon
604                 }
605             ];
606         }
607         
608         if (this.badge) {
609             cfg.html += ' ';
610             
611             cfg.tag = 'a';
612             
613 //            cfg.cls='btn roo-button';
614             
615             cfg.href=this.href;
616             
617             var value = cfg.html;
618             
619             if(this.glyphicon){
620                 value = {
621                             tag: 'span',
622                             cls: 'glyphicon glyphicon-' + this.glyphicon,
623                             html: this.html
624                         };
625                 
626             }
627             
628             cfg.cn = [
629                 value,
630                 {
631                     tag: 'span',
632                     cls: 'badge',
633                     html: this.badge
634                 }
635             ];
636             
637             cfg.html='';
638         }
639         
640         if (this.menu) {
641             cfg.cls += ' dropdown';
642             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
643         }
644         
645         if (cfg.tag !== 'a' && this.href !== '') {
646             throw "Tag must be a to set href.";
647         } else if (this.href.length > 0) {
648             cfg.href = this.href;
649         }
650         
651         if(this.removeClass){
652             cfg.cls = '';
653         }
654         
655         if(this.target){
656             cfg.target = this.target;
657         }
658         
659         return cfg;
660     },
661     initEvents: function() {
662        // Roo.log('init events?');
663 //        Roo.log(this.el.dom);
664         // add the menu...
665         
666         if (typeof (this.menu) != 'undefined') {
667             this.menu.parentType = this.xtype;
668             this.menu.triggerEl = this.el;
669             this.addxtype(Roo.apply({}, this.menu));
670         }
671
672
673        if (this.el.hasClass('roo-button')) {
674             this.el.on('click', this.onClick, this);
675        } else {
676             this.el.select('.roo-button').on('click', this.onClick, this);
677        }
678        
679        if(this.removeClass){
680            this.el.on('click', this.onClick, this);
681        }
682        
683        this.el.enableDisplayMode();
684         
685     },
686     onClick : function(e)
687     {
688         if (this.disabled) {
689             return;
690         }
691         
692         Roo.log('button on click ');
693         if(this.preventDefault){
694             e.preventDefault();
695         }
696         if (this.pressed === true || this.pressed === false) {
697             this.pressed = !this.pressed;
698             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
699             this.fireEvent('toggle', this, e, this.pressed);
700         }
701         
702         
703         this.fireEvent('click', this, e);
704     },
705     
706     /**
707      * Enables this button
708      */
709     enable : function()
710     {
711         this.disabled = false;
712         this.el.removeClass('disabled');
713     },
714     
715     /**
716      * Disable this button
717      */
718     disable : function()
719     {
720         this.disabled = true;
721         this.el.addClass('disabled');
722     },
723      /**
724      * sets the active state on/off, 
725      * @param {Boolean} state (optional) Force a particular state
726      */
727     setActive : function(v) {
728         
729         this.el[v ? 'addClass' : 'removeClass']('active');
730     },
731      /**
732      * toggles the current active state 
733      */
734     toggleActive : function()
735     {
736        var active = this.el.hasClass('active');
737        this.setActive(!active);
738        
739         
740     },
741     setText : function(str)
742     {
743         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
744     },
745     getText : function()
746     {
747         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
748     },
749     hide: function() {
750        
751      
752         this.el.hide();   
753     },
754     show: function() {
755        
756         this.el.show();   
757     }
758     
759     
760 });
761
762  /*
763  * - LGPL
764  *
765  * column
766  * 
767  */
768
769 /**
770  * @class Roo.bootstrap.Column
771  * @extends Roo.bootstrap.Component
772  * Bootstrap Column class
773  * @cfg {Number} xs colspan out of 12 for mobile-sized screens 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 {String} alert (success|info|warning|danger) type alert (changes background / border...)
778  * @cfg {String} fa (ban|check|...) font awesome icon
779  * @cfg {String} icon (info-sign|check|...) glyphicon name
780
781  * @cfg {String} html content of column.
782  * 
783  * @constructor
784  * Create a new Column
785  * @param {Object} config The config object
786  */
787
788 Roo.bootstrap.Column = function(config){
789     Roo.bootstrap.Column.superclass.constructor.call(this, config);
790 };
791
792 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
793     
794     xs: false,
795     sm: false,
796     md: false,
797     lg: false,
798     html: '',
799     offset: 0,
800     alert: false,
801     fa: false,
802     icon : false,
803     
804     getAutoCreate : function(){
805         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
806         
807         cfg = {
808             tag: 'div',
809             cls: 'column'
810         };
811         
812         var settings=this;
813         ['xs','sm','md','lg'].map(function(size){
814             if (settings[size] !== false) {
815                 if (!settings[size]) { // 0 = hidden
816                     cfg.cls += ' hidden-' + size;
817                     return;
818                 }
819                 cfg.cls += ' col-' + size + '-' + settings[size];
820             }
821         });
822         
823         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
824             cfg.cls +=' alert alert-' + this.alert;
825         }
826         
827         
828         if (this.html.length) {
829             cfg.html = this.html;
830         }
831         if (this.fa) {
832             cfg.html = '<i class="fa fa-'+this.fa + '"></i>' + (cfg.html || '');
833         }
834         if (this.icon) {
835             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + + (cfg.html || '')
836         }
837         
838         return cfg;
839     }
840    
841 });
842
843  
844
845  /*
846  * - LGPL
847  *
848  * page container.
849  * 
850  */
851
852
853 /**
854  * @class Roo.bootstrap.Container
855  * @extends Roo.bootstrap.Component
856  * Bootstrap Container class
857  * @cfg {Boolean} jumbotron is it a jumbotron element
858  * @cfg {String} html content of element
859  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
860  * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
861  * @cfg {String} header content of header (for panel)
862  * @cfg {String} footer content of footer (for panel)
863  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
864  * @cfg {String} tag (header|aside|section) type of HTML tag.
865  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
866  * @cfg {String} fa (ban|check|...) font awesome icon
867  * @cfg {String} icon (info-sign|check|...) glyphicon name
868
869  *     
870  * @constructor
871  * Create a new Container
872  * @param {Object} config The config object
873  */
874
875 Roo.bootstrap.Container = function(config){
876     Roo.bootstrap.Container.superclass.constructor.call(this, config);
877 };
878
879 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
880     
881     jumbotron : false,
882     well: '',
883     panel : '',
884     header: '',
885     footer : '',
886     sticky: '',
887     tag : false,
888     alert : false,
889     fa: false,
890     icon : false,
891   
892      
893     getChildContainer : function() {
894         
895         if(!this.el){
896             return false;
897         }
898         
899         if (this.panel.length) {
900             return this.el.select('.panel-body',true).first();
901         }
902         
903         return this.el;
904     },
905     
906     
907     getAutoCreate : function(){
908         
909         var cfg = {
910             tag : this.tag || 'div',
911             html : '',
912             cls : ''
913         };
914         if (this.jumbotron) {
915             cfg.cls = 'jumbotron';
916         }
917         
918         
919         
920         // - this is applied by the parent..
921         //if (this.cls) {
922         //    cfg.cls = this.cls + '';
923         //}
924         
925         if (this.sticky.length) {
926             
927             var bd = Roo.get(document.body);
928             if (!bd.hasClass('bootstrap-sticky')) {
929                 bd.addClass('bootstrap-sticky');
930                 Roo.select('html',true).setStyle('height', '100%');
931             }
932              
933             cfg.cls += 'bootstrap-sticky-' + this.sticky;
934         }
935         
936         
937         if (this.well.length) {
938             switch (this.well) {
939                 case 'lg':
940                 case 'sm':
941                     cfg.cls +=' well well-' +this.well;
942                     break;
943                 default:
944                     cfg.cls +=' well';
945                     break;
946             }
947         }
948         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
949             cfg.cls +=' alert alert-' + this.alert;
950         }
951         
952         var body = cfg;
953         
954         if (this.panel.length) {
955             cfg.cls += ' panel panel-' + this.panel;
956             cfg.cn = [];
957             if (this.header.length) {
958                 cfg.cn.push({
959                     
960                     cls : 'panel-heading',
961                     cn : [{
962                         tag: 'h3',
963                         cls : 'panel-title',
964                         html : this.header
965                     }]
966                     
967                 });
968             }
969             body = false;
970             cfg.cn.push({
971                 cls : 'panel-body',
972                 html : this.html
973             });
974             
975             
976             if (this.footer.length) {
977                 cfg.cn.push({
978                     cls : 'panel-footer',
979                     html : this.footer
980                     
981                 });
982             }
983             
984         }
985         
986         if (body) {
987             body.html = this.html || cfg.html;
988             // prefix with the icons..
989             if (this.fa) {
990                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
991             }
992             if (this.icon) {
993                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
994             }
995             
996             
997         }
998         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
999             cfg.cls =  'container';
1000         }
1001         
1002         return cfg;
1003     },
1004     
1005     titleEl : function()
1006     {
1007         if(!this.el || !this.panel.length || !this.header.length){
1008             return;
1009         }
1010         
1011         return this.el.select('.panel-title',true).first();
1012     },
1013     
1014     setTitle : function(v)
1015     {
1016         var titleEl = this.titleEl();
1017         
1018         if(!titleEl){
1019             return;
1020         }
1021         
1022         titleEl.dom.innerHTML = v;
1023     },
1024     
1025     getTitle : function()
1026     {
1027         
1028         var titleEl = this.titleEl();
1029         
1030         if(!titleEl){
1031             return '';
1032         }
1033         
1034         return titleEl.dom.innerHTML;
1035     }
1036    
1037 });
1038
1039  /*
1040  * - LGPL
1041  *
1042  * image
1043  * 
1044  */
1045
1046
1047 /**
1048  * @class Roo.bootstrap.Img
1049  * @extends Roo.bootstrap.Component
1050  * Bootstrap Img class
1051  * @cfg {Boolean} imgResponsive false | true
1052  * @cfg {String} border rounded | circle | thumbnail
1053  * @cfg {String} src image source
1054  * @cfg {String} alt image alternative text
1055  * @cfg {String} href a tag href
1056  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1057  * 
1058  * @constructor
1059  * Create a new Input
1060  * @param {Object} config The config object
1061  */
1062
1063 Roo.bootstrap.Img = function(config){
1064     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1065     
1066     this.addEvents({
1067         // img events
1068         /**
1069          * @event click
1070          * The img click event for the img.
1071          * @param {Roo.EventObject} e
1072          */
1073         "click" : true
1074     });
1075 };
1076
1077 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1078     
1079     imgResponsive: true,
1080     border: '',
1081     src: '',
1082     href: false,
1083     target: false,
1084
1085     getAutoCreate : function(){
1086         
1087         var cfg = {
1088             tag: 'img',
1089             cls: (this.imgResponsive) ? 'img-responsive' : '',
1090             html : null
1091         }
1092         
1093         cfg.html = this.html || cfg.html;
1094         
1095         cfg.src = this.src || cfg.src;
1096         
1097         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1098             cfg.cls += ' img-' + this.border;
1099         }
1100         
1101         if(this.alt){
1102             cfg.alt = this.alt;
1103         }
1104         
1105         if(this.href){
1106             var a = {
1107                 tag: 'a',
1108                 href: this.href,
1109                 cn: [
1110                     cfg
1111                 ]
1112             }
1113             
1114             if(this.target){
1115                 a.target = this.target;
1116             }
1117             
1118         }
1119         
1120         
1121         return (this.href) ? a : cfg;
1122     },
1123     
1124     initEvents: function() {
1125         
1126         if(!this.href){
1127             this.el.on('click', this.onClick, this);
1128         }
1129     },
1130     
1131     onClick : function(e)
1132     {
1133         Roo.log('img onclick');
1134         this.fireEvent('click', this, e);
1135     }
1136    
1137 });
1138
1139  /*
1140  * - LGPL
1141  *
1142  * image
1143  * 
1144  */
1145
1146
1147 /**
1148  * @class Roo.bootstrap.Link
1149  * @extends Roo.bootstrap.Component
1150  * Bootstrap Link Class
1151  * @cfg {String} alt image alternative text
1152  * @cfg {String} href a tag href
1153  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1154  * @cfg {String} html the content of the link.
1155  * @cfg {Boolean} preventDefault (true | false) default false
1156
1157  * 
1158  * @constructor
1159  * Create a new Input
1160  * @param {Object} config The config object
1161  */
1162
1163 Roo.bootstrap.Link = function(config){
1164     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1165     
1166     this.addEvents({
1167         // img events
1168         /**
1169          * @event click
1170          * The img click event for the img.
1171          * @param {Roo.EventObject} e
1172          */
1173         "click" : true
1174     });
1175 };
1176
1177 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1178     
1179     href: false,
1180     target: false,
1181     preventDefault: false,
1182
1183     getAutoCreate : function(){
1184         
1185         var cfg = {
1186             tag: 'a',
1187             html : this.html || 'html-missing'
1188         }
1189         
1190         
1191         if(this.alt){
1192             cfg.alt = this.alt;
1193         }
1194         cfg.href = this.href || '#';
1195         if(this.target){
1196             cfg.target = this.target;
1197         }
1198         
1199         return cfg;
1200     },
1201     
1202     initEvents: function() {
1203         
1204         if(!this.href || this.preventDefault){
1205             this.el.on('click', this.onClick, this);
1206         }
1207     },
1208     
1209     onClick : function(e)
1210     {
1211         if(this.preventDefault){
1212             e.preventDefault();
1213         }
1214         //Roo.log('img onclick');
1215         this.fireEvent('click', this, e);
1216     }
1217    
1218 });
1219
1220  /*
1221  * - LGPL
1222  *
1223  * header
1224  * 
1225  */
1226
1227 /**
1228  * @class Roo.bootstrap.Header
1229  * @extends Roo.bootstrap.Component
1230  * Bootstrap Header class
1231  * @cfg {String} html content of header
1232  * @cfg {Number} level (1|2|3|4|5|6) default 1
1233  * 
1234  * @constructor
1235  * Create a new Header
1236  * @param {Object} config The config object
1237  */
1238
1239
1240 Roo.bootstrap.Header  = function(config){
1241     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1242 };
1243
1244 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1245     
1246     //href : false,
1247     html : false,
1248     level : 1,
1249     
1250     
1251     
1252     getAutoCreate : function(){
1253         
1254         var cfg = {
1255             tag: 'h' + (1 *this.level),
1256             html: this.html || 'fill in html'
1257         } ;
1258         
1259         return cfg;
1260     }
1261    
1262 });
1263
1264  
1265
1266  /*
1267  * Based on:
1268  * Ext JS Library 1.1.1
1269  * Copyright(c) 2006-2007, Ext JS, LLC.
1270  *
1271  * Originally Released Under LGPL - original licence link has changed is not relivant.
1272  *
1273  * Fork - LGPL
1274  * <script type="text/javascript">
1275  */
1276  
1277 /**
1278  * @class Roo.bootstrap.MenuMgr
1279  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1280  * @singleton
1281  */
1282 Roo.bootstrap.MenuMgr = function(){
1283    var menus, active, groups = {}, attached = false, lastShow = new Date();
1284
1285    // private - called when first menu is created
1286    function init(){
1287        menus = {};
1288        active = new Roo.util.MixedCollection();
1289        Roo.get(document).addKeyListener(27, function(){
1290            if(active.length > 0){
1291                hideAll();
1292            }
1293        });
1294    }
1295
1296    // private
1297    function hideAll(){
1298        if(active && active.length > 0){
1299            var c = active.clone();
1300            c.each(function(m){
1301                m.hide();
1302            });
1303        }
1304    }
1305
1306    // private
1307    function onHide(m){
1308        active.remove(m);
1309        if(active.length < 1){
1310            Roo.get(document).un("mouseup", onMouseDown);
1311             
1312            attached = false;
1313        }
1314    }
1315
1316    // private
1317    function onShow(m){
1318        var last = active.last();
1319        lastShow = new Date();
1320        active.add(m);
1321        if(!attached){
1322           Roo.get(document).on("mouseup", onMouseDown);
1323            
1324            attached = true;
1325        }
1326        if(m.parentMenu){
1327           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1328           m.parentMenu.activeChild = m;
1329        }else if(last && last.isVisible()){
1330           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1331        }
1332    }
1333
1334    // private
1335    function onBeforeHide(m){
1336        if(m.activeChild){
1337            m.activeChild.hide();
1338        }
1339        if(m.autoHideTimer){
1340            clearTimeout(m.autoHideTimer);
1341            delete m.autoHideTimer;
1342        }
1343    }
1344
1345    // private
1346    function onBeforeShow(m){
1347        var pm = m.parentMenu;
1348        if(!pm && !m.allowOtherMenus){
1349            hideAll();
1350        }else if(pm && pm.activeChild && active != m){
1351            pm.activeChild.hide();
1352        }
1353    }
1354
1355    // private
1356    function onMouseDown(e){
1357         Roo.log("on MouseDown");
1358         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
1359            hideAll();
1360         }
1361         
1362         
1363    }
1364
1365    // private
1366    function onBeforeCheck(mi, state){
1367        if(state){
1368            var g = groups[mi.group];
1369            for(var i = 0, l = g.length; i < l; i++){
1370                if(g[i] != mi){
1371                    g[i].setChecked(false);
1372                }
1373            }
1374        }
1375    }
1376
1377    return {
1378
1379        /**
1380         * Hides all menus that are currently visible
1381         */
1382        hideAll : function(){
1383             hideAll();  
1384        },
1385
1386        // private
1387        register : function(menu){
1388            if(!menus){
1389                init();
1390            }
1391            menus[menu.id] = menu;
1392            menu.on("beforehide", onBeforeHide);
1393            menu.on("hide", onHide);
1394            menu.on("beforeshow", onBeforeShow);
1395            menu.on("show", onShow);
1396            var g = menu.group;
1397            if(g && menu.events["checkchange"]){
1398                if(!groups[g]){
1399                    groups[g] = [];
1400                }
1401                groups[g].push(menu);
1402                menu.on("checkchange", onCheck);
1403            }
1404        },
1405
1406         /**
1407          * Returns a {@link Roo.menu.Menu} object
1408          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1409          * be used to generate and return a new Menu instance.
1410          */
1411        get : function(menu){
1412            if(typeof menu == "string"){ // menu id
1413                return menus[menu];
1414            }else if(menu.events){  // menu instance
1415                return menu;
1416            }
1417            /*else if(typeof menu.length == 'number'){ // array of menu items?
1418                return new Roo.bootstrap.Menu({items:menu});
1419            }else{ // otherwise, must be a config
1420                return new Roo.bootstrap.Menu(menu);
1421            }
1422            */
1423            return false;
1424        },
1425
1426        // private
1427        unregister : function(menu){
1428            delete menus[menu.id];
1429            menu.un("beforehide", onBeforeHide);
1430            menu.un("hide", onHide);
1431            menu.un("beforeshow", onBeforeShow);
1432            menu.un("show", onShow);
1433            var g = menu.group;
1434            if(g && menu.events["checkchange"]){
1435                groups[g].remove(menu);
1436                menu.un("checkchange", onCheck);
1437            }
1438        },
1439
1440        // private
1441        registerCheckable : function(menuItem){
1442            var g = menuItem.group;
1443            if(g){
1444                if(!groups[g]){
1445                    groups[g] = [];
1446                }
1447                groups[g].push(menuItem);
1448                menuItem.on("beforecheckchange", onBeforeCheck);
1449            }
1450        },
1451
1452        // private
1453        unregisterCheckable : function(menuItem){
1454            var g = menuItem.group;
1455            if(g){
1456                groups[g].remove(menuItem);
1457                menuItem.un("beforecheckchange", onBeforeCheck);
1458            }
1459        }
1460    };
1461 }();/*
1462  * - LGPL
1463  *
1464  * menu
1465  * 
1466  */
1467
1468 /**
1469  * @class Roo.bootstrap.Menu
1470  * @extends Roo.bootstrap.Component
1471  * Bootstrap Menu class - container for MenuItems
1472  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1473  * 
1474  * @constructor
1475  * Create a new Menu
1476  * @param {Object} config The config object
1477  */
1478
1479
1480 Roo.bootstrap.Menu = function(config){
1481     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1482     if (this.registerMenu) {
1483         Roo.bootstrap.MenuMgr.register(this);
1484     }
1485     this.addEvents({
1486         /**
1487          * @event beforeshow
1488          * Fires before this menu is displayed
1489          * @param {Roo.menu.Menu} this
1490          */
1491         beforeshow : true,
1492         /**
1493          * @event beforehide
1494          * Fires before this menu is hidden
1495          * @param {Roo.menu.Menu} this
1496          */
1497         beforehide : true,
1498         /**
1499          * @event show
1500          * Fires after this menu is displayed
1501          * @param {Roo.menu.Menu} this
1502          */
1503         show : true,
1504         /**
1505          * @event hide
1506          * Fires after this menu is hidden
1507          * @param {Roo.menu.Menu} this
1508          */
1509         hide : true,
1510         /**
1511          * @event click
1512          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1513          * @param {Roo.menu.Menu} this
1514          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1515          * @param {Roo.EventObject} e
1516          */
1517         click : true,
1518         /**
1519          * @event mouseover
1520          * Fires when the mouse is hovering over this menu
1521          * @param {Roo.menu.Menu} this
1522          * @param {Roo.EventObject} e
1523          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1524          */
1525         mouseover : true,
1526         /**
1527          * @event mouseout
1528          * Fires when the mouse exits this menu
1529          * @param {Roo.menu.Menu} this
1530          * @param {Roo.EventObject} e
1531          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1532          */
1533         mouseout : true,
1534         /**
1535          * @event itemclick
1536          * Fires when a menu item contained in this menu is clicked
1537          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1538          * @param {Roo.EventObject} e
1539          */
1540         itemclick: true
1541     });
1542     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1543 };
1544
1545 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1546     
1547    /// html : false,
1548     //align : '',
1549     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1550     type: false,
1551     /**
1552      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1553      */
1554     registerMenu : true,
1555     
1556     menuItems :false, // stores the menu items..
1557     
1558     hidden:true,
1559     
1560     parentMenu : false,
1561     
1562     getChildContainer : function() {
1563         return this.el;  
1564     },
1565     
1566     getAutoCreate : function(){
1567          
1568         //if (['right'].indexOf(this.align)!==-1) {
1569         //    cfg.cn[1].cls += ' pull-right'
1570         //}
1571         
1572         
1573         var cfg = {
1574             tag : 'ul',
1575             cls : 'dropdown-menu' ,
1576             style : 'z-index:1000'
1577             
1578         }
1579         
1580         if (this.type === 'submenu') {
1581             cfg.cls = 'submenu active';
1582         }
1583         if (this.type === 'treeview') {
1584             cfg.cls = 'treeview-menu';
1585         }
1586         
1587         return cfg;
1588     },
1589     initEvents : function() {
1590         
1591        // Roo.log("ADD event");
1592        // Roo.log(this.triggerEl.dom);
1593         this.triggerEl.on('click', this.onTriggerPress, this);
1594         this.triggerEl.addClass('dropdown-toggle');
1595         this.el.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
1596
1597         this.el.on("mouseover", this.onMouseOver, this);
1598         this.el.on("mouseout", this.onMouseOut, this);
1599         
1600         
1601     },
1602     findTargetItem : function(e){
1603         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
1604         if(!t){
1605             return false;
1606         }
1607         //Roo.log(t);         Roo.log(t.id);
1608         if(t && t.id){
1609             //Roo.log(this.menuitems);
1610             return this.menuitems.get(t.id);
1611             
1612             //return this.items.get(t.menuItemId);
1613         }
1614         
1615         return false;
1616     },
1617     onClick : function(e){
1618         Roo.log("menu.onClick");
1619         var t = this.findTargetItem(e);
1620         if(!t){
1621             return;
1622         }
1623         Roo.log(e);
1624         /*
1625         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
1626             if(t == this.activeItem && t.shouldDeactivate(e)){
1627                 this.activeItem.deactivate();
1628                 delete this.activeItem;
1629                 return;
1630             }
1631             if(t.canActivate){
1632                 this.setActiveItem(t, true);
1633             }
1634             return;
1635             
1636             
1637         }
1638         */
1639         Roo.log('pass click event');
1640         
1641         t.onClick(e);
1642         
1643         this.fireEvent("click", this, t, e);
1644         
1645         this.hide();
1646     },
1647      onMouseOver : function(e){
1648         var t  = this.findTargetItem(e);
1649         //Roo.log(t);
1650         //if(t){
1651         //    if(t.canActivate && !t.disabled){
1652         //        this.setActiveItem(t, true);
1653         //    }
1654         //}
1655         
1656         this.fireEvent("mouseover", this, e, t);
1657     },
1658     isVisible : function(){
1659         return !this.hidden;
1660     },
1661      onMouseOut : function(e){
1662         var t  = this.findTargetItem(e);
1663         
1664         //if(t ){
1665         //    if(t == this.activeItem && t.shouldDeactivate(e)){
1666         //        this.activeItem.deactivate();
1667         //        delete this.activeItem;
1668         //    }
1669         //}
1670         this.fireEvent("mouseout", this, e, t);
1671     },
1672     
1673     
1674     /**
1675      * Displays this menu relative to another element
1676      * @param {String/HTMLElement/Roo.Element} element The element to align to
1677      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
1678      * the element (defaults to this.defaultAlign)
1679      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1680      */
1681     show : function(el, pos, parentMenu){
1682         this.parentMenu = parentMenu;
1683         if(!this.el){
1684             this.render();
1685         }
1686         this.fireEvent("beforeshow", this);
1687         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
1688     },
1689      /**
1690      * Displays this menu at a specific xy position
1691      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
1692      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1693      */
1694     showAt : function(xy, parentMenu, /* private: */_e){
1695         this.parentMenu = parentMenu;
1696         if(!this.el){
1697             this.render();
1698         }
1699         if(_e !== false){
1700             this.fireEvent("beforeshow", this);
1701             
1702             //xy = this.el.adjustForConstraints(xy);
1703         }
1704         //this.el.setXY(xy);
1705         //this.el.show();
1706         this.hideMenuItems();
1707         this.hidden = false;
1708         this.triggerEl.addClass('open');
1709         this.focus();
1710         this.fireEvent("show", this);
1711     },
1712     
1713     focus : function(){
1714         return;
1715         if(!this.hidden){
1716             this.doFocus.defer(50, this);
1717         }
1718     },
1719
1720     doFocus : function(){
1721         if(!this.hidden){
1722             this.focusEl.focus();
1723         }
1724     },
1725
1726     /**
1727      * Hides this menu and optionally all parent menus
1728      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
1729      */
1730     hide : function(deep){
1731         
1732         this.hideMenuItems();
1733         if(this.el && this.isVisible()){
1734             this.fireEvent("beforehide", this);
1735             if(this.activeItem){
1736                 this.activeItem.deactivate();
1737                 this.activeItem = null;
1738             }
1739             this.triggerEl.removeClass('open');;
1740             this.hidden = true;
1741             this.fireEvent("hide", this);
1742         }
1743         if(deep === true && this.parentMenu){
1744             this.parentMenu.hide(true);
1745         }
1746     },
1747     
1748     onTriggerPress  : function(e)
1749     {
1750         
1751         Roo.log('trigger press');
1752         //Roo.log(e.getTarget());
1753        // Roo.log(this.triggerEl.dom);
1754         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
1755             return;
1756         }
1757         if (this.isVisible()) {
1758             Roo.log('hide');
1759             this.hide();
1760         } else {
1761             this.show(this.triggerEl, false, false);
1762         }
1763         
1764         
1765     },
1766     
1767          
1768        
1769     
1770     hideMenuItems : function()
1771     {
1772         //$(backdrop).remove()
1773         Roo.select('.open',true).each(function(aa) {
1774             
1775             aa.removeClass('open');
1776           //var parent = getParent($(this))
1777           //var relatedTarget = { relatedTarget: this }
1778           
1779            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
1780           //if (e.isDefaultPrevented()) return
1781            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
1782         })
1783     },
1784     addxtypeChild : function (tree, cntr) {
1785         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
1786           
1787         this.menuitems.add(comp);
1788         return comp;
1789
1790     },
1791     getEl : function()
1792     {
1793         Roo.log(this.el);
1794         return this.el;
1795     }
1796 });
1797
1798  
1799  /*
1800  * - LGPL
1801  *
1802  * menu item
1803  * 
1804  */
1805
1806
1807 /**
1808  * @class Roo.bootstrap.MenuItem
1809  * @extends Roo.bootstrap.Component
1810  * Bootstrap MenuItem class
1811  * @cfg {String} html the menu label
1812  * @cfg {String} href the link
1813  * @cfg {Boolean} preventDefault (true | false) default true
1814  * 
1815  * 
1816  * @constructor
1817  * Create a new MenuItem
1818  * @param {Object} config The config object
1819  */
1820
1821
1822 Roo.bootstrap.MenuItem = function(config){
1823     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
1824     this.addEvents({
1825         // raw events
1826         /**
1827          * @event click
1828          * The raw click event for the entire grid.
1829          * @param {Roo.EventObject} e
1830          */
1831         "click" : true
1832     });
1833 };
1834
1835 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
1836     
1837     href : false,
1838     html : false,
1839     preventDefault: true,
1840     
1841     getAutoCreate : function(){
1842         var cfg= {
1843             tag: 'li',
1844             cls: 'dropdown-menu-item',
1845             cn: [
1846                     {
1847                         tag : 'a',
1848                         href : '#',
1849                         html : 'Link'
1850                     }
1851                 ]
1852         };
1853         if (this.parent().type == 'treeview') {
1854             cfg.cls = 'treeview-menu';
1855         }
1856         
1857         cfg.cn[0].href = this.href || cfg.cn[0].href ;
1858         cfg.cn[0].html = this.html || cfg.cn[0].html ;
1859         return cfg;
1860     },
1861     
1862     initEvents: function() {
1863         
1864         //this.el.select('a').on('click', this.onClick, this);
1865         
1866     },
1867     onClick : function(e)
1868     {
1869         Roo.log('item on click ');
1870         //if(this.preventDefault){
1871         //    e.preventDefault();
1872         //}
1873         //this.parent().hideMenuItems();
1874         
1875         this.fireEvent('click', this, e);
1876     },
1877     getEl : function()
1878     {
1879         return this.el;
1880     }
1881 });
1882
1883  
1884
1885  /*
1886  * - LGPL
1887  *
1888  * menu separator
1889  * 
1890  */
1891
1892
1893 /**
1894  * @class Roo.bootstrap.MenuSeparator
1895  * @extends Roo.bootstrap.Component
1896  * Bootstrap MenuSeparator class
1897  * 
1898  * @constructor
1899  * Create a new MenuItem
1900  * @param {Object} config The config object
1901  */
1902
1903
1904 Roo.bootstrap.MenuSeparator = function(config){
1905     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
1906 };
1907
1908 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
1909     
1910     getAutoCreate : function(){
1911         var cfg = {
1912             cls: 'divider',
1913             tag : 'li'
1914         };
1915         
1916         return cfg;
1917     }
1918    
1919 });
1920
1921  
1922
1923  
1924 /*
1925 <div class="modal fade">
1926   <div class="modal-dialog">
1927     <div class="modal-content">
1928       <div class="modal-header">
1929         <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
1930         <h4 class="modal-title">Modal title</h4>
1931       </div>
1932       <div class="modal-body">
1933         <p>One fine body&hellip;</p>
1934       </div>
1935       <div class="modal-footer">
1936         <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
1937         <button type="button" class="btn btn-primary">Save changes</button>
1938       </div>
1939     </div><!-- /.modal-content -->
1940   </div><!-- /.modal-dialog -->
1941 </div><!-- /.modal -->
1942 */
1943 /*
1944  * - LGPL
1945  *
1946  * page contgainer.
1947  * 
1948  */
1949
1950 /**
1951  * @class Roo.bootstrap.Modal
1952  * @extends Roo.bootstrap.Component
1953  * Bootstrap Modal class
1954  * @cfg {String} title Title of dialog
1955  * @cfg {Boolean} specificTitle (true|false) default false
1956  * @cfg {Array} buttons Array of buttons or standard button set..
1957  * @cfg {String} buttonPosition (left|right|center) default right
1958  * 
1959  * @constructor
1960  * Create a new Modal Dialog
1961  * @param {Object} config The config object
1962  */
1963
1964 Roo.bootstrap.Modal = function(config){
1965     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
1966     this.addEvents({
1967         // raw events
1968         /**
1969          * @event btnclick
1970          * The raw btnclick event for the button
1971          * @param {Roo.EventObject} e
1972          */
1973         "btnclick" : true
1974     });
1975     this.buttons = this.buttons || [];
1976 };
1977
1978 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
1979     
1980     title : 'test dialog',
1981    
1982     buttons : false,
1983     
1984     // set on load...
1985     body:  false,
1986     
1987     specificTitle: false,
1988     
1989     buttonPosition: 'right',
1990     
1991     onRender : function(ct, position)
1992     {
1993         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
1994      
1995         if(!this.el){
1996             var cfg = Roo.apply({},  this.getAutoCreate());
1997             cfg.id = Roo.id();
1998             //if(!cfg.name){
1999             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2000             //}
2001             //if (!cfg.name.length) {
2002             //    delete cfg.name;
2003            // }
2004             if (this.cls) {
2005                 cfg.cls += ' ' + this.cls;
2006             }
2007             if (this.style) {
2008                 cfg.style = this.style;
2009             }
2010             this.el = Roo.get(document.body).createChild(cfg, position);
2011         }
2012         //var type = this.el.dom.type;
2013         
2014         if(this.tabIndex !== undefined){
2015             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2016         }
2017         
2018         
2019         
2020         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2021         this.maskEl.enableDisplayMode("block");
2022         this.maskEl.hide();
2023         //this.el.addClass("x-dlg-modal");
2024     
2025         if (this.buttons.length) {
2026             Roo.each(this.buttons, function(bb) {
2027                 b = Roo.apply({}, bb);
2028                 b.xns = b.xns || Roo.bootstrap;
2029                 b.xtype = b.xtype || 'Button';
2030                 if (typeof(b.listeners) == 'undefined') {
2031                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2032                 }
2033                 
2034                 var btn = Roo.factory(b);
2035                 
2036                 btn.onRender(this.el.select('.modal-footer div').first());
2037                 
2038             },this);
2039         }
2040         // render the children.
2041         var nitems = [];
2042         
2043         if(typeof(this.items) != 'undefined'){
2044             var items = this.items;
2045             delete this.items;
2046
2047             for(var i =0;i < items.length;i++) {
2048                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2049             }
2050         }
2051         
2052         this.items = nitems;
2053         
2054         this.body = this.el.select('.modal-body',true).first();
2055         this.close = this.el.select('.modal-header .close', true).first();
2056         this.footer = this.el.select('.modal-footer',true).first();
2057         this.initEvents();
2058         //this.el.addClass([this.fieldClass, this.cls]);
2059         
2060     },
2061     getAutoCreate : function(){
2062         
2063         
2064         var bdy = {
2065                 cls : 'modal-body',
2066                 html : this.html || ''
2067         };
2068         
2069         var title = {
2070             tag: 'h4',
2071             cls : 'modal-title',
2072             html : this.title
2073         };
2074         
2075         if(this.specificTitle){
2076             title = this.title;
2077         };
2078         
2079         return modal = {
2080             cls: "modal fade",
2081             style : 'display: none',
2082             cn : [
2083                 {
2084                     cls: "modal-dialog",
2085                     cn : [
2086                         {
2087                             cls : "modal-content",
2088                             cn : [
2089                                 {
2090                                     cls : 'modal-header',
2091                                     cn : [
2092                                         {
2093                                             tag: 'button',
2094                                             cls : 'close',
2095                                             html : '&times'
2096                                         },
2097                                         title
2098                                     ]
2099                                 },
2100                                 bdy,
2101                                 {
2102                                     cls : 'modal-footer',
2103                                     cn : [
2104                                         {
2105                                             tag: 'div',
2106                                             cls: 'btn-' + this.buttonPosition
2107                                         }
2108                                     ]
2109                                     
2110                                 }
2111                                 
2112                                 
2113                             ]
2114                             
2115                         }
2116                     ]
2117                         
2118                 }
2119             ]
2120             
2121             
2122         };
2123           
2124     },
2125     getChildContainer : function() {
2126          
2127          return this.el.select('.modal-body',true).first();
2128         
2129     },
2130     getButtonContainer : function() {
2131          return this.el.select('.modal-footer div',true).first();
2132         
2133     },
2134     initEvents : function()
2135     {
2136         this.el.select('.modal-header .close').on('click', this.hide, this);
2137 //        
2138 //        this.addxtype(this);
2139     },
2140     show : function() {
2141         
2142         if (!this.rendered) {
2143             this.render();
2144         }
2145        
2146         this.el.addClass('on');
2147         this.el.removeClass('fade');
2148         this.el.setStyle('display', 'block');
2149         Roo.get(document.body).addClass("x-body-masked");
2150         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2151         this.maskEl.show();
2152         this.el.setStyle('zIndex', '10001');
2153         this.fireEvent('show', this);
2154         
2155         
2156     },
2157     hide : function()
2158     {
2159         Roo.log('Modal hide?!');
2160         this.maskEl.hide();
2161         Roo.get(document.body).removeClass("x-body-masked");
2162         this.el.removeClass('on');
2163         this.el.addClass('fade');
2164         this.el.setStyle('display', 'none');
2165         this.fireEvent('hide', this);
2166     },
2167     
2168     addButton : function(str, cb)
2169     {
2170          
2171         
2172         var b = Roo.apply({}, { html : str } );
2173         b.xns = b.xns || Roo.bootstrap;
2174         b.xtype = b.xtype || 'Button';
2175         if (typeof(b.listeners) == 'undefined') {
2176             b.listeners = { click : cb.createDelegate(this)  };
2177         }
2178         
2179         var btn = Roo.factory(b);
2180            
2181         btn.onRender(this.el.select('.modal-footer div').first());
2182         
2183         return btn;   
2184        
2185     },
2186     
2187     setDefaultButton : function(btn)
2188     {
2189         //this.el.select('.modal-footer').()
2190     },
2191     resizeTo: function(w,h)
2192     {
2193         // skip..
2194     },
2195     setContentSize  : function(w, h)
2196     {
2197         
2198     },
2199     onButtonClick: function(btn,e)
2200     {
2201         //Roo.log([a,b,c]);
2202         this.fireEvent('btnclick', btn.name, e);
2203     },
2204     setTitle: function(str) {
2205         this.el.select('.modal-title',true).first().dom.innerHTML = str;
2206         
2207     }
2208 });
2209
2210
2211 Roo.apply(Roo.bootstrap.Modal,  {
2212     /**
2213          * Button config that displays a single OK button
2214          * @type Object
2215          */
2216         OK :  [{
2217             name : 'ok',
2218             weight : 'primary',
2219             html : 'OK'
2220         }], 
2221         /**
2222          * Button config that displays Yes and No buttons
2223          * @type Object
2224          */
2225         YESNO : [
2226             {
2227                 name  : 'no',
2228                 html : 'No'
2229             },
2230             {
2231                 name  :'yes',
2232                 weight : 'primary',
2233                 html : 'Yes'
2234             }
2235         ],
2236         
2237         /**
2238          * Button config that displays OK and Cancel buttons
2239          * @type Object
2240          */
2241         OKCANCEL : [
2242             {
2243                name : 'cancel',
2244                 html : 'Cancel'
2245             },
2246             {
2247                 name : 'ok',
2248                 weight : 'primary',
2249                 html : 'OK'
2250             }
2251         ],
2252         /**
2253          * Button config that displays Yes, No and Cancel buttons
2254          * @type Object
2255          */
2256         YESNOCANCEL : [
2257             {
2258                 name : 'yes',
2259                 weight : 'primary',
2260                 html : 'Yes'
2261             },
2262             {
2263                 name : 'no',
2264                 html : 'No'
2265             },
2266             {
2267                 name : 'cancel',
2268                 html : 'Cancel'
2269             }
2270         ]
2271 });
2272  /*
2273  * - LGPL
2274  *
2275  * messagebox - can be used as a replace
2276  * 
2277  */
2278 /**
2279  * @class Roo.MessageBox
2280  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2281  * Example usage:
2282  *<pre><code>
2283 // Basic alert:
2284 Roo.Msg.alert('Status', 'Changes saved successfully.');
2285
2286 // Prompt for user data:
2287 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2288     if (btn == 'ok'){
2289         // process text value...
2290     }
2291 });
2292
2293 // Show a dialog using config options:
2294 Roo.Msg.show({
2295    title:'Save Changes?',
2296    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2297    buttons: Roo.Msg.YESNOCANCEL,
2298    fn: processResult,
2299    animEl: 'elId'
2300 });
2301 </code></pre>
2302  * @singleton
2303  */
2304 Roo.bootstrap.MessageBox = function(){
2305     var dlg, opt, mask, waitTimer;
2306     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2307     var buttons, activeTextEl, bwidth;
2308
2309     
2310     // private
2311     var handleButton = function(button){
2312         dlg.hide();
2313         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2314     };
2315
2316     // private
2317     var handleHide = function(){
2318         if(opt && opt.cls){
2319             dlg.el.removeClass(opt.cls);
2320         }
2321         //if(waitTimer){
2322         //    Roo.TaskMgr.stop(waitTimer);
2323         //    waitTimer = null;
2324         //}
2325     };
2326
2327     // private
2328     var updateButtons = function(b){
2329         var width = 0;
2330         if(!b){
2331             buttons["ok"].hide();
2332             buttons["cancel"].hide();
2333             buttons["yes"].hide();
2334             buttons["no"].hide();
2335             //dlg.footer.dom.style.display = 'none';
2336             return width;
2337         }
2338         dlg.footer.dom.style.display = '';
2339         for(var k in buttons){
2340             if(typeof buttons[k] != "function"){
2341                 if(b[k]){
2342                     buttons[k].show();
2343                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2344                     width += buttons[k].el.getWidth()+15;
2345                 }else{
2346                     buttons[k].hide();
2347                 }
2348             }
2349         }
2350         return width;
2351     };
2352
2353     // private
2354     var handleEsc = function(d, k, e){
2355         if(opt && opt.closable !== false){
2356             dlg.hide();
2357         }
2358         if(e){
2359             e.stopEvent();
2360         }
2361     };
2362
2363     return {
2364         /**
2365          * Returns a reference to the underlying {@link Roo.BasicDialog} element
2366          * @return {Roo.BasicDialog} The BasicDialog element
2367          */
2368         getDialog : function(){
2369            if(!dlg){
2370                 dlg = new Roo.bootstrap.Modal( {
2371                     //draggable: true,
2372                     //resizable:false,
2373                     //constraintoviewport:false,
2374                     //fixedcenter:true,
2375                     //collapsible : false,
2376                     //shim:true,
2377                     //modal: true,
2378                   //  width:400,
2379                   //  height:100,
2380                     //buttonAlign:"center",
2381                     closeClick : function(){
2382                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2383                             handleButton("no");
2384                         }else{
2385                             handleButton("cancel");
2386                         }
2387                     }
2388                 });
2389                 dlg.render();
2390                 dlg.on("hide", handleHide);
2391                 mask = dlg.mask;
2392                 //dlg.addKeyListener(27, handleEsc);
2393                 buttons = {};
2394                 this.buttons = buttons;
2395                 var bt = this.buttonText;
2396                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2397                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2398                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2399                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2400                 Roo.log(buttons)
2401                 bodyEl = dlg.body.createChild({
2402
2403                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2404                         '<textarea class="roo-mb-textarea"></textarea>' +
2405                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
2406                 });
2407                 msgEl = bodyEl.dom.firstChild;
2408                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2409                 textboxEl.enableDisplayMode();
2410                 textboxEl.addKeyListener([10,13], function(){
2411                     if(dlg.isVisible() && opt && opt.buttons){
2412                         if(opt.buttons.ok){
2413                             handleButton("ok");
2414                         }else if(opt.buttons.yes){
2415                             handleButton("yes");
2416                         }
2417                     }
2418                 });
2419                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2420                 textareaEl.enableDisplayMode();
2421                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2422                 progressEl.enableDisplayMode();
2423                 var pf = progressEl.dom.firstChild;
2424                 if (pf) {
2425                     pp = Roo.get(pf.firstChild);
2426                     pp.setHeight(pf.offsetHeight);
2427                 }
2428                 
2429             }
2430             return dlg;
2431         },
2432
2433         /**
2434          * Updates the message box body text
2435          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2436          * the XHTML-compliant non-breaking space character '&amp;#160;')
2437          * @return {Roo.MessageBox} This message box
2438          */
2439         updateText : function(text){
2440             if(!dlg.isVisible() && !opt.width){
2441                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2442             }
2443             msgEl.innerHTML = text || '&#160;';
2444       
2445             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2446             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2447             var w = Math.max(
2448                     Math.min(opt.width || cw , this.maxWidth), 
2449                     Math.max(opt.minWidth || this.minWidth, bwidth)
2450             );
2451             if(opt.prompt){
2452                 activeTextEl.setWidth(w);
2453             }
2454             if(dlg.isVisible()){
2455                 dlg.fixedcenter = false;
2456             }
2457             // to big, make it scroll. = But as usual stupid IE does not support
2458             // !important..
2459             
2460             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2461                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2462                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2463             } else {
2464                 bodyEl.dom.style.height = '';
2465                 bodyEl.dom.style.overflowY = '';
2466             }
2467             if (cw > w) {
2468                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2469             } else {
2470                 bodyEl.dom.style.overflowX = '';
2471             }
2472             
2473             dlg.setContentSize(w, bodyEl.getHeight());
2474             if(dlg.isVisible()){
2475                 dlg.fixedcenter = true;
2476             }
2477             return this;
2478         },
2479
2480         /**
2481          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
2482          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2483          * @param {Number} value Any number between 0 and 1 (e.g., .5)
2484          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2485          * @return {Roo.MessageBox} This message box
2486          */
2487         updateProgress : function(value, text){
2488             if(text){
2489                 this.updateText(text);
2490             }
2491             if (pp) { // weird bug on my firefox - for some reason this is not defined
2492                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2493             }
2494             return this;
2495         },        
2496
2497         /**
2498          * Returns true if the message box is currently displayed
2499          * @return {Boolean} True if the message box is visible, else false
2500          */
2501         isVisible : function(){
2502             return dlg && dlg.isVisible();  
2503         },
2504
2505         /**
2506          * Hides the message box if it is displayed
2507          */
2508         hide : function(){
2509             if(this.isVisible()){
2510                 dlg.hide();
2511             }  
2512         },
2513
2514         /**
2515          * Displays a new message box, or reinitializes an existing message box, based on the config options
2516          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2517          * The following config object properties are supported:
2518          * <pre>
2519 Property    Type             Description
2520 ----------  ---------------  ------------------------------------------------------------------------------------
2521 animEl            String/Element   An id or Element from which the message box should animate as it opens and
2522                                    closes (defaults to undefined)
2523 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
2524                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
2525 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
2526                                    progress and wait dialogs will ignore this property and always hide the
2527                                    close button as they can only be closed programmatically.
2528 cls               String           A custom CSS class to apply to the message box element
2529 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
2530                                    displayed (defaults to 75)
2531 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
2532                                    function will be btn (the name of the button that was clicked, if applicable,
2533                                    e.g. "ok"), and text (the value of the active text field, if applicable).
2534                                    Progress and wait dialogs will ignore this option since they do not respond to
2535                                    user actions and can only be closed programmatically, so any required function
2536                                    should be called by the same code after it closes the dialog.
2537 icon              String           A CSS class that provides a background image to be used as an icon for
2538                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
2539 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
2540 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
2541 modal             Boolean          False to allow user interaction with the page while the message box is
2542                                    displayed (defaults to true)
2543 msg               String           A string that will replace the existing message box body text (defaults
2544                                    to the XHTML-compliant non-breaking space character '&#160;')
2545 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
2546 progress          Boolean          True to display a progress bar (defaults to false)
2547 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
2548 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
2549 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
2550 title             String           The title text
2551 value             String           The string value to set into the active textbox element if displayed
2552 wait              Boolean          True to display a progress bar (defaults to false)
2553 width             Number           The width of the dialog in pixels
2554 </pre>
2555          *
2556          * Example usage:
2557          * <pre><code>
2558 Roo.Msg.show({
2559    title: 'Address',
2560    msg: 'Please enter your address:',
2561    width: 300,
2562    buttons: Roo.MessageBox.OKCANCEL,
2563    multiline: true,
2564    fn: saveAddress,
2565    animEl: 'addAddressBtn'
2566 });
2567 </code></pre>
2568          * @param {Object} config Configuration options
2569          * @return {Roo.MessageBox} This message box
2570          */
2571         show : function(options)
2572         {
2573             
2574             // this causes nightmares if you show one dialog after another
2575             // especially on callbacks..
2576              
2577             if(this.isVisible()){
2578                 
2579                 this.hide();
2580                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
2581                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
2582                 Roo.log("New Dialog Message:" +  options.msg )
2583                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
2584                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
2585                 
2586             }
2587             var d = this.getDialog();
2588             opt = options;
2589             d.setTitle(opt.title || "&#160;");
2590             d.close.setDisplayed(opt.closable !== false);
2591             activeTextEl = textboxEl;
2592             opt.prompt = opt.prompt || (opt.multiline ? true : false);
2593             if(opt.prompt){
2594                 if(opt.multiline){
2595                     textboxEl.hide();
2596                     textareaEl.show();
2597                     textareaEl.setHeight(typeof opt.multiline == "number" ?
2598                         opt.multiline : this.defaultTextHeight);
2599                     activeTextEl = textareaEl;
2600                 }else{
2601                     textboxEl.show();
2602                     textareaEl.hide();
2603                 }
2604             }else{
2605                 textboxEl.hide();
2606                 textareaEl.hide();
2607             }
2608             progressEl.setDisplayed(opt.progress === true);
2609             this.updateProgress(0);
2610             activeTextEl.dom.value = opt.value || "";
2611             if(opt.prompt){
2612                 dlg.setDefaultButton(activeTextEl);
2613             }else{
2614                 var bs = opt.buttons;
2615                 var db = null;
2616                 if(bs && bs.ok){
2617                     db = buttons["ok"];
2618                 }else if(bs && bs.yes){
2619                     db = buttons["yes"];
2620                 }
2621                 dlg.setDefaultButton(db);
2622             }
2623             bwidth = updateButtons(opt.buttons);
2624             this.updateText(opt.msg);
2625             if(opt.cls){
2626                 d.el.addClass(opt.cls);
2627             }
2628             d.proxyDrag = opt.proxyDrag === true;
2629             d.modal = opt.modal !== false;
2630             d.mask = opt.modal !== false ? mask : false;
2631             if(!d.isVisible()){
2632                 // force it to the end of the z-index stack so it gets a cursor in FF
2633                 document.body.appendChild(dlg.el.dom);
2634                 d.animateTarget = null;
2635                 d.show(options.animEl);
2636             }
2637             return this;
2638         },
2639
2640         /**
2641          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
2642          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
2643          * and closing the message box when the process is complete.
2644          * @param {String} title The title bar text
2645          * @param {String} msg The message box body text
2646          * @return {Roo.MessageBox} This message box
2647          */
2648         progress : function(title, msg){
2649             this.show({
2650                 title : title,
2651                 msg : msg,
2652                 buttons: false,
2653                 progress:true,
2654                 closable:false,
2655                 minWidth: this.minProgressWidth,
2656                 modal : true
2657             });
2658             return this;
2659         },
2660
2661         /**
2662          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
2663          * If a callback function is passed it will be called after the user clicks the button, and the
2664          * id of the button that was clicked will be passed as the only parameter to the callback
2665          * (could also be the top-right close button).
2666          * @param {String} title The title bar text
2667          * @param {String} msg The message box body text
2668          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2669          * @param {Object} scope (optional) The scope of the callback function
2670          * @return {Roo.MessageBox} This message box
2671          */
2672         alert : function(title, msg, fn, scope){
2673             this.show({
2674                 title : title,
2675                 msg : msg,
2676                 buttons: this.OK,
2677                 fn: fn,
2678                 scope : scope,
2679                 modal : true
2680             });
2681             return this;
2682         },
2683
2684         /**
2685          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
2686          * interaction while waiting for a long-running process to complete that does not have defined intervals.
2687          * You are responsible for closing the message box when the process is complete.
2688          * @param {String} msg The message box body text
2689          * @param {String} title (optional) The title bar text
2690          * @return {Roo.MessageBox} This message box
2691          */
2692         wait : function(msg, title){
2693             this.show({
2694                 title : title,
2695                 msg : msg,
2696                 buttons: false,
2697                 closable:false,
2698                 progress:true,
2699                 modal:true,
2700                 width:300,
2701                 wait:true
2702             });
2703             waitTimer = Roo.TaskMgr.start({
2704                 run: function(i){
2705                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
2706                 },
2707                 interval: 1000
2708             });
2709             return this;
2710         },
2711
2712         /**
2713          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
2714          * If a callback function is passed it will be called after the user clicks either button, and the id of the
2715          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
2716          * @param {String} title The title bar text
2717          * @param {String} msg The message box body text
2718          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2719          * @param {Object} scope (optional) The scope of the callback function
2720          * @return {Roo.MessageBox} This message box
2721          */
2722         confirm : function(title, msg, fn, scope){
2723             this.show({
2724                 title : title,
2725                 msg : msg,
2726                 buttons: this.YESNO,
2727                 fn: fn,
2728                 scope : scope,
2729                 modal : true
2730             });
2731             return this;
2732         },
2733
2734         /**
2735          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
2736          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
2737          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
2738          * (could also be the top-right close button) and the text that was entered will be passed as the two
2739          * parameters to the callback.
2740          * @param {String} title The title bar text
2741          * @param {String} msg The message box body text
2742          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2743          * @param {Object} scope (optional) The scope of the callback function
2744          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
2745          * property, or the height in pixels to create the textbox (defaults to false / single-line)
2746          * @return {Roo.MessageBox} This message box
2747          */
2748         prompt : function(title, msg, fn, scope, multiline){
2749             this.show({
2750                 title : title,
2751                 msg : msg,
2752                 buttons: this.OKCANCEL,
2753                 fn: fn,
2754                 minWidth:250,
2755                 scope : scope,
2756                 prompt:true,
2757                 multiline: multiline,
2758                 modal : true
2759             });
2760             return this;
2761         },
2762
2763         /**
2764          * Button config that displays a single OK button
2765          * @type Object
2766          */
2767         OK : {ok:true},
2768         /**
2769          * Button config that displays Yes and No buttons
2770          * @type Object
2771          */
2772         YESNO : {yes:true, no:true},
2773         /**
2774          * Button config that displays OK and Cancel buttons
2775          * @type Object
2776          */
2777         OKCANCEL : {ok:true, cancel:true},
2778         /**
2779          * Button config that displays Yes, No and Cancel buttons
2780          * @type Object
2781          */
2782         YESNOCANCEL : {yes:true, no:true, cancel:true},
2783
2784         /**
2785          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
2786          * @type Number
2787          */
2788         defaultTextHeight : 75,
2789         /**
2790          * The maximum width in pixels of the message box (defaults to 600)
2791          * @type Number
2792          */
2793         maxWidth : 600,
2794         /**
2795          * The minimum width in pixels of the message box (defaults to 100)
2796          * @type Number
2797          */
2798         minWidth : 100,
2799         /**
2800          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
2801          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
2802          * @type Number
2803          */
2804         minProgressWidth : 250,
2805         /**
2806          * An object containing the default button text strings that can be overriden for localized language support.
2807          * Supported properties are: ok, cancel, yes and no.
2808          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
2809          * @type Object
2810          */
2811         buttonText : {
2812             ok : "OK",
2813             cancel : "Cancel",
2814             yes : "Yes",
2815             no : "No"
2816         }
2817     };
2818 }();
2819
2820 /**
2821  * Shorthand for {@link Roo.MessageBox}
2822  */
2823 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox 
2824 Roo.Msg = Roo.Msg || Roo.MessageBox;
2825 /*
2826  * - LGPL
2827  *
2828  * navbar
2829  * 
2830  */
2831
2832 /**
2833  * @class Roo.bootstrap.Navbar
2834  * @extends Roo.bootstrap.Component
2835  * Bootstrap Navbar class
2836
2837  * @constructor
2838  * Create a new Navbar
2839  * @param {Object} config The config object
2840  */
2841
2842
2843 Roo.bootstrap.Navbar = function(config){
2844     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
2845     
2846 };
2847
2848 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
2849     
2850     
2851    
2852     // private
2853     navItems : false,
2854     loadMask : false,
2855     
2856     
2857     getAutoCreate : function(){
2858         
2859         
2860         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
2861         
2862     },
2863     
2864     initEvents :function ()
2865     {
2866         //Roo.log(this.el.select('.navbar-toggle',true));
2867         this.el.select('.navbar-toggle',true).on('click', function() {
2868            // Roo.log('click');
2869             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
2870         }, this);
2871         
2872         var mark = {
2873             tag: "div",
2874             cls:"x-dlg-mask"
2875         }
2876         
2877         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
2878         
2879         var size = this.el.getSize();
2880         this.maskEl.setSize(size.width, size.height);
2881         this.maskEl.enableDisplayMode("block");
2882         this.maskEl.hide();
2883         
2884         if(this.loadMask){
2885             this.maskEl.show();
2886         }
2887     },
2888     
2889     
2890     getChildContainer : function()
2891     {
2892         if (this.el.select('.collapse').getCount()) {
2893             return this.el.select('.collapse',true).first();
2894         }
2895         
2896         return this.el;
2897     },
2898     
2899     mask : function()
2900     {
2901         this.maskEl.show();
2902     },
2903     
2904     unmask : function()
2905     {
2906         this.maskEl.hide();
2907     } 
2908     
2909     
2910     
2911     
2912 });
2913
2914
2915
2916  
2917
2918  /*
2919  * - LGPL
2920  *
2921  * navbar
2922  * 
2923  */
2924
2925 /**
2926  * @class Roo.bootstrap.NavSimplebar
2927  * @extends Roo.bootstrap.Navbar
2928  * Bootstrap Sidebar class
2929  *
2930  * @cfg {Boolean} inverse is inverted color
2931  * 
2932  * @cfg {String} type (nav | pills | tabs)
2933  * @cfg {Boolean} arrangement stacked | justified
2934  * @cfg {String} align (left | right) alignment
2935  * 
2936  * @cfg {Boolean} main (true|false) main nav bar? default false
2937  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
2938  * 
2939  * @cfg {String} tag (header|footer|nav|div) default is nav 
2940
2941  * 
2942  * 
2943  * 
2944  * @constructor
2945  * Create a new Sidebar
2946  * @param {Object} config The config object
2947  */
2948
2949
2950 Roo.bootstrap.NavSimplebar = function(config){
2951     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
2952 };
2953
2954 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
2955     
2956     inverse: false,
2957     
2958     type: false,
2959     arrangement: '',
2960     align : false,
2961     
2962     
2963     
2964     main : false,
2965     
2966     
2967     tag : false,
2968     
2969     
2970     getAutoCreate : function(){
2971         
2972         
2973         var cfg = {
2974             tag : this.tag || 'div',
2975             cls : 'navbar'
2976         };
2977           
2978         
2979         cfg.cn = [
2980             {
2981                 cls: 'nav',
2982                 tag : 'ul'
2983             }
2984         ];
2985         
2986          
2987         this.type = this.type || 'nav';
2988         if (['tabs','pills'].indexOf(this.type)!==-1) {
2989             cfg.cn[0].cls += ' nav-' + this.type
2990         
2991         
2992         } else {
2993             if (this.type!=='nav') {
2994                 Roo.log('nav type must be nav/tabs/pills')
2995             }
2996             cfg.cn[0].cls += ' navbar-nav'
2997         }
2998         
2999         
3000         
3001         
3002         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3003             cfg.cn[0].cls += ' nav-' + this.arrangement;
3004         }
3005         
3006         
3007         if (this.align === 'right') {
3008             cfg.cn[0].cls += ' navbar-right';
3009         }
3010         
3011         if (this.inverse) {
3012             cfg.cls += ' navbar-inverse';
3013             
3014         }
3015         
3016         
3017         return cfg;
3018     
3019         
3020     }
3021     
3022     
3023     
3024 });
3025
3026
3027
3028  
3029
3030  
3031        /*
3032  * - LGPL
3033  *
3034  * navbar
3035  * 
3036  */
3037
3038 /**
3039  * @class Roo.bootstrap.NavHeaderbar
3040  * @extends Roo.bootstrap.NavSimplebar
3041  * Bootstrap Sidebar class
3042  *
3043  * @cfg {String} brand what is brand
3044  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3045  * @cfg {String} brand_href href of the brand
3046  * @cfg {Boolean} srButton generate the sr-only button (true | false) default true
3047  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3048  * 
3049  * @constructor
3050  * Create a new Sidebar
3051  * @param {Object} config The config object
3052  */
3053
3054
3055 Roo.bootstrap.NavHeaderbar = function(config){
3056     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3057 };
3058
3059 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3060     
3061     position: '',
3062     brand: '',
3063     brand_href: false,
3064     srButton : true,
3065     autohide : false,
3066     
3067     getAutoCreate : function(){
3068         
3069         var   cfg = {
3070             tag: this.nav || 'nav',
3071             cls: 'navbar',
3072             role: 'navigation',
3073             cn: []
3074         };
3075         
3076         if(this.srButton){
3077             cfg.cn.push({
3078                 tag: 'div',
3079                 cls: 'navbar-header',
3080                 cn: [
3081                     {
3082                         tag: 'button',
3083                         type: 'button',
3084                         cls: 'navbar-toggle',
3085                         'data-toggle': 'collapse',
3086                         cn: [
3087                             {
3088                                 tag: 'span',
3089                                 cls: 'sr-only',
3090                                 html: 'Toggle navigation'
3091                             },
3092                             {
3093                                 tag: 'span',
3094                                 cls: 'icon-bar'
3095                             },
3096                             {
3097                                 tag: 'span',
3098                                 cls: 'icon-bar'
3099                             },
3100                             {
3101                                 tag: 'span',
3102                                 cls: 'icon-bar'
3103                             }
3104                         ]
3105                     }
3106                 ]
3107             });
3108         }
3109         
3110         cfg.cn.push({
3111             tag: 'div',
3112             cls: 'collapse navbar-collapse',
3113             cn : []
3114         });
3115         
3116         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3117         
3118         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3119             cfg.cls += ' navbar-' + this.position;
3120             
3121             // tag can override this..
3122             
3123             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3124         }
3125         
3126         if (this.brand !== '') {
3127             cfg.cn[0].cn.push({
3128                 tag: 'a',
3129                 href: this.brand_href ? this.brand_href : '#',
3130                 cls: 'navbar-brand',
3131                 cn: [
3132                 this.brand
3133                 ]
3134             });
3135         }
3136         
3137         if(this.main){
3138             cfg.cls += ' main-nav';
3139         }
3140         
3141         
3142         return cfg;
3143
3144         
3145     },
3146     initEvents : function()
3147     {
3148         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3149         
3150         if (this.autohide) {
3151             
3152             var prevScroll = 0;
3153             var ft = this.el;
3154             
3155             Roo.get(document).on('scroll',function(e) {
3156                 var ns = Roo.get(document).getScroll().top;
3157                 var os = prevScroll;
3158                 prevScroll = ns;
3159                 
3160                 if(ns > os){
3161                     ft.removeClass('slideDown');
3162                     ft.addClass('slideUp');
3163                     return;
3164                 }
3165                 ft.removeClass('slideUp');
3166                 ft.addClass('slideDown');
3167                  
3168               
3169           },this);
3170         }
3171     }    
3172           
3173       
3174     
3175     
3176 });
3177
3178
3179
3180  
3181
3182  /*
3183  * - LGPL
3184  *
3185  * navbar
3186  * 
3187  */
3188
3189 /**
3190  * @class Roo.bootstrap.NavSidebar
3191  * @extends Roo.bootstrap.Navbar
3192  * Bootstrap Sidebar class
3193  * 
3194  * @constructor
3195  * Create a new Sidebar
3196  * @param {Object} config The config object
3197  */
3198
3199
3200 Roo.bootstrap.NavSidebar = function(config){
3201     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3202 };
3203
3204 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3205     
3206     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3207     
3208     getAutoCreate : function(){
3209         
3210         
3211         return  {
3212             tag: 'div',
3213             cls: 'sidebar sidebar-nav'
3214         };
3215     
3216         
3217     }
3218     
3219     
3220     
3221 });
3222
3223
3224
3225  
3226
3227  /*
3228  * - LGPL
3229  *
3230  * nav group
3231  * 
3232  */
3233
3234 /**
3235  * @class Roo.bootstrap.NavGroup
3236  * @extends Roo.bootstrap.Component
3237  * Bootstrap NavGroup class
3238  * @cfg {String} align left | right
3239  * @cfg {Boolean} inverse false | true
3240  * @cfg {String} type (nav|pills|tab) default nav
3241  * @cfg {String} navId - reference Id for navbar.
3242
3243  * 
3244  * @constructor
3245  * Create a new nav group
3246  * @param {Object} config The config object
3247  */
3248
3249 Roo.bootstrap.NavGroup = function(config){
3250     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3251     this.navItems = [];
3252    
3253     Roo.bootstrap.NavGroup.register(this);
3254      this.addEvents({
3255         /**
3256              * @event changed
3257              * Fires when the active item changes
3258              * @param {Roo.bootstrap.NavGroup} this
3259              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3260              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3261          */
3262         'changed': true
3263      });
3264     
3265 };
3266
3267 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3268     
3269     align: '',
3270     inverse: false,
3271     form: false,
3272     type: 'nav',
3273     navId : '',
3274     // private
3275     
3276     navItems : false, 
3277     
3278     getAutoCreate : function()
3279     {
3280         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3281         
3282         cfg = {
3283             tag : 'ul',
3284             cls: 'nav' 
3285         }
3286         
3287         if (['tabs','pills'].indexOf(this.type)!==-1) {
3288             cfg.cls += ' nav-' + this.type
3289         } else {
3290             if (this.type!=='nav') {
3291                 Roo.log('nav type must be nav/tabs/pills')
3292             }
3293             cfg.cls += ' navbar-nav'
3294         }
3295         
3296         if (this.parent().sidebar) {
3297             cfg = {
3298                 tag: 'ul',
3299                 cls: 'dashboard-menu sidebar-menu'
3300             }
3301             
3302             return cfg;
3303         }
3304         
3305         if (this.form === true) {
3306             cfg = {
3307                 tag: 'form',
3308                 cls: 'navbar-form'
3309             }
3310             
3311             if (this.align === 'right') {
3312                 cfg.cls += ' navbar-right';
3313             } else {
3314                 cfg.cls += ' navbar-left';
3315             }
3316         }
3317         
3318         if (this.align === 'right') {
3319             cfg.cls += ' navbar-right';
3320         }
3321         
3322         if (this.inverse) {
3323             cfg.cls += ' navbar-inverse';
3324             
3325         }
3326         
3327         
3328         return cfg;
3329     },
3330     /**
3331     * sets the active Navigation item
3332     * @param {Roo.bootstrap.NavItem} the new current navitem
3333     */
3334     setActiveItem : function(item)
3335     {
3336         var prev = false;
3337         Roo.each(this.navItems, function(v){
3338             if (v == item) {
3339                 return ;
3340             }
3341             if (v.isActive()) {
3342                 v.setActive(false, true);
3343                 prev = v;
3344                 
3345             }
3346             
3347         });
3348
3349         item.setActive(true, true);
3350         this.fireEvent('changed', this, item, prev);
3351         
3352         
3353     },
3354     /**
3355     * gets the active Navigation item
3356     * @return {Roo.bootstrap.NavItem} the current navitem
3357     */
3358     getActive : function()
3359     {
3360         
3361         var prev = false;
3362         Roo.each(this.navItems, function(v){
3363             
3364             if (v.isActive()) {
3365                 prev = v;
3366                 
3367             }
3368             
3369         });
3370         return prev;
3371     },
3372     
3373     indexOfNav : function()
3374     {
3375         
3376         var prev = false;
3377         Roo.each(this.navItems, function(v,i){
3378             
3379             if (v.isActive()) {
3380                 prev = i;
3381                 
3382             }
3383             
3384         });
3385         return prev;
3386     },
3387     /**
3388     * adds a Navigation item
3389     * @param {Roo.bootstrap.NavItem} the navitem to add
3390     */
3391     addItem : function(cfg)
3392     {
3393         var cn = new Roo.bootstrap.NavItem(cfg);
3394         this.register(cn);
3395         cn.parentId = this.id;
3396         cn.onRender(this.el, null);
3397         return cn;
3398     },
3399     /**
3400     * register a Navigation item
3401     * @param {Roo.bootstrap.NavItem} the navitem to add
3402     */
3403     register : function(item)
3404     {
3405         this.navItems.push( item);
3406         item.navId = this.navId;
3407     
3408     },
3409   
3410     
3411     getNavItem: function(tabId)
3412     {
3413         var ret = false;
3414         Roo.each(this.navItems, function(e) {
3415             if (e.tabId == tabId) {
3416                ret =  e;
3417                return false;
3418             }
3419             return true;
3420             
3421         });
3422         return ret;
3423     },
3424     
3425     setActiveNext : function()
3426     {
3427         var i = this.indexOfNav(this.getActive());
3428         if (i > this.navItems.length) {
3429             return;
3430         }
3431         this.setActiveItem(this.navItems[i+1]);
3432     },
3433     setActivePrev : function()
3434     {
3435         var i = this.indexOfNav(this.getActive());
3436         if (i  < 1) {
3437             return;
3438         }
3439         this.setActiveItem(this.navItems[i-1]);
3440     },
3441     clearWasActive : function(except) {
3442         Roo.each(this.navItems, function(e) {
3443             if (e.tabId != except.tabId && e.was_active) {
3444                e.was_active = false;
3445                return false;
3446             }
3447             return true;
3448             
3449         });
3450     },
3451     getWasActive : function ()
3452     {
3453         var r = false;
3454         Roo.each(this.navItems, function(e) {
3455             if (e.was_active) {
3456                r = e;
3457                return false;
3458             }
3459             return true;
3460             
3461         });
3462         return r;
3463     }
3464     
3465     
3466 });
3467
3468  
3469 Roo.apply(Roo.bootstrap.NavGroup, {
3470     
3471     groups: {},
3472      /**
3473     * register a Navigation Group
3474     * @param {Roo.bootstrap.NavGroup} the navgroup to add
3475     */
3476     register : function(navgrp)
3477     {
3478         this.groups[navgrp.navId] = navgrp;
3479         
3480     },
3481     /**
3482     * fetch a Navigation Group based on the navigation ID
3483     * @param {string} the navgroup to add
3484     * @returns {Roo.bootstrap.NavGroup} the navgroup 
3485     */
3486     get: function(navId) {
3487         if (typeof(this.groups[navId]) == 'undefined') {
3488             return false;
3489             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
3490         }
3491         return this.groups[navId] ;
3492     }
3493     
3494     
3495     
3496 });
3497
3498  /*
3499  * - LGPL
3500  *
3501  * row
3502  * 
3503  */
3504
3505 /**
3506  * @class Roo.bootstrap.NavItem
3507  * @extends Roo.bootstrap.Component
3508  * Bootstrap Navbar.NavItem class
3509  * @cfg {String} href  link to
3510  * @cfg {String} html content of button
3511  * @cfg {String} badge text inside badge
3512  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
3513  * @cfg {String} glyphicon name of glyphicon
3514  * @cfg {String} icon name of font awesome icon
3515  * @cfg {Boolean} active Is item active
3516  * @cfg {Boolean} disabled Is item disabled
3517  
3518  * @cfg {Boolean} preventDefault (true | false) default false
3519  * @cfg {String} tabId the tab that this item activates.
3520  * @cfg {String} tagtype (a|span) render as a href or span?
3521   
3522  * @constructor
3523  * Create a new Navbar Item
3524  * @param {Object} config The config object
3525  */
3526 Roo.bootstrap.NavItem = function(config){
3527     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
3528     this.addEvents({
3529         // raw events
3530         /**
3531          * @event click
3532          * The raw click event for the entire grid.
3533          * @param {Roo.EventObject} e
3534          */
3535         "click" : true,
3536          /**
3537             * @event changed
3538             * Fires when the active item active state changes
3539             * @param {Roo.bootstrap.NavItem} this
3540             * @param {boolean} state the new state
3541              
3542          */
3543         'changed': true
3544     });
3545    
3546 };
3547
3548 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
3549     
3550     href: false,
3551     html: '',
3552     badge: '',
3553     icon: false,
3554     glyphicon: false,
3555     active: false,
3556     preventDefault : false,
3557     tabId : false,
3558     tagtype : 'a',
3559     disabled : false,
3560     
3561     was_active : false,
3562     
3563     getAutoCreate : function(){
3564          
3565         var cfg = {
3566             tag: 'li',
3567             cls: 'nav-item'
3568             
3569         }
3570         if (this.active) {
3571             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
3572         }
3573         if (this.disabled) {
3574             cfg.cls += ' disabled';
3575         }
3576         
3577         if (this.href || this.html || this.glyphicon || this.icon) {
3578             cfg.cn = [
3579                 {
3580                     tag: this.tagtype,
3581                     href : this.href || "#",
3582                     html: this.html || ''
3583                 }
3584             ];
3585             
3586             if (this.icon) {
3587                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
3588             }
3589
3590             if(this.glyphicon) {
3591                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
3592             }
3593             
3594             if (this.menu) {
3595                 
3596                 cfg.cn[0].html += " <span class='caret'></span>";
3597              
3598             }
3599             
3600             if (this.badge !== '') {
3601                  
3602                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
3603             }
3604         }
3605         
3606         
3607         
3608         return cfg;
3609     },
3610     initEvents: function() {
3611        // Roo.log('init events?');
3612        // Roo.log(this.el.dom);
3613         if (typeof (this.menu) != 'undefined') {
3614             this.menu.parentType = this.xtype;
3615             this.menu.triggerEl = this.el;
3616             this.addxtype(Roo.apply({}, this.menu));
3617         }
3618
3619        
3620         this.el.select('a',true).on('click', this.onClick, this);
3621         // at this point parent should be available..
3622         this.parent().register(this);
3623     },
3624     
3625     onClick : function(e)
3626     {
3627          
3628         if(this.preventDefault){
3629             e.preventDefault();
3630         }
3631         if (this.disabled) {
3632             return;
3633         }
3634         
3635         var tg = Roo.bootstrap.TabGroup.get(this.navId);
3636         if (tg && tg.transition) {
3637             Roo.log("waiting for the transitionend");
3638             return;
3639         }
3640         
3641         Roo.log("fire event clicked");
3642         if(this.fireEvent('click', this, e) === false){
3643             return;
3644         };
3645         
3646         if (['tabs','pills'].indexOf(this.parent().type)!==-1) {
3647             if (typeof(this.parent().setActiveItem) !== 'undefined') {
3648                 this.parent().setActiveItem(this);
3649             }
3650         } 
3651     },
3652     
3653     isActive: function () {
3654         return this.active
3655     },
3656     setActive : function(state, fire, is_was_active)
3657     {
3658         if (this.active && !state & this.navId) {
3659             this.was_active = true;
3660             var nv = Roo.bootstrap.NavGroup.get(this.navId);
3661             if (nv) {
3662                 nv.clearWasActive(this);
3663             }
3664             
3665         }
3666         this.active = state;
3667         
3668         if (!state ) {
3669             this.el.removeClass('active');
3670         } else if (!this.el.hasClass('active')) {
3671             this.el.addClass('active');
3672         }
3673         if (fire) {
3674             this.fireEvent('changed', this, state);
3675         }
3676         
3677         // show a panel if it's registered and related..
3678         
3679         if (!this.navId || !this.tabId || !state || is_was_active) {
3680             return;
3681         }
3682         
3683         var tg = Roo.bootstrap.TabGroup.get(this.navId);
3684         if (!tg) {
3685             return;
3686         }
3687         var pan = tg.getPanelByName(this.tabId);
3688         if (!pan) {
3689             return;
3690         }
3691         // if we can not flip to new panel - go back to old nav highlight..
3692         if (false == tg.showPanel(pan)) {
3693             var nv = Roo.bootstrap.NavGroup.get(this.navId);
3694             if (nv) {
3695                 var onav = nv.getWasActive();
3696                 if (onav) {
3697                     onav.setActive(true, false, true);
3698                 }
3699             }
3700             
3701         }
3702         
3703         
3704         
3705     },
3706      // this should not be here...
3707     setDisabled : function(state)
3708     {
3709         this.disabled = state;
3710         if (!state ) {
3711             this.el.removeClass('disabled');
3712         } else if (!this.el.hasClass('disabled')) {
3713             this.el.addClass('disabled');
3714         }
3715         
3716     }
3717 });
3718  
3719
3720  /*
3721  * - LGPL
3722  *
3723  * sidebar item
3724  *
3725  *  li
3726  *    <span> icon </span>
3727  *    <span> text </span>
3728  *    <span>badge </span>
3729  */
3730
3731 /**
3732  * @class Roo.bootstrap.NavSidebarItem
3733  * @extends Roo.bootstrap.NavItem
3734  * Bootstrap Navbar.NavSidebarItem class
3735  * @constructor
3736  * Create a new Navbar Button
3737  * @param {Object} config The config object
3738  */
3739 Roo.bootstrap.NavSidebarItem = function(config){
3740     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
3741     this.addEvents({
3742         // raw events
3743         /**
3744          * @event click
3745          * The raw click event for the entire grid.
3746          * @param {Roo.EventObject} e
3747          */
3748         "click" : true,
3749          /**
3750             * @event changed
3751             * Fires when the active item active state changes
3752             * @param {Roo.bootstrap.NavSidebarItem} this
3753             * @param {boolean} state the new state
3754              
3755          */
3756         'changed': true
3757     });
3758    
3759 };
3760
3761 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
3762     
3763     
3764     getAutoCreate : function(){
3765         
3766         
3767         var a = {
3768                 tag: 'a',
3769                 href : this.href || '#',
3770                 cls: '',
3771                 html : '',
3772                 cn : []
3773         };
3774         var cfg = {
3775             tag: 'li',
3776             cls: '',
3777             cn: [ a ]
3778         }
3779         var span = {
3780             tag: 'span',
3781             html : this.html || ''
3782         }
3783         
3784         
3785         if (this.active) {
3786             cfg.cls += ' active';
3787         }
3788         
3789         // left icon..
3790         if (this.glyphicon || this.icon) {
3791             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
3792             a.cn.push({ tag : 'i', cls : c }) ;
3793         }
3794         // html..
3795         a.cn.push(span);
3796         // then badge..
3797         if (this.badge !== '') {
3798             a.cn.push({ tag: 'span',  cls : 'badge pull-right ' + (this.badgecls || ''), html: this.badge }); 
3799         }
3800         // fi
3801         if (this.menu) {
3802             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
3803             a.cls += 'dropdown-toggle treeview' ;
3804             
3805         }
3806         
3807         
3808         
3809         return cfg;
3810          
3811            
3812     }
3813    
3814      
3815  
3816 });
3817  
3818
3819  /*
3820  * - LGPL
3821  *
3822  * row
3823  * 
3824  */
3825
3826 /**
3827  * @class Roo.bootstrap.Row
3828  * @extends Roo.bootstrap.Component
3829  * Bootstrap Row class (contains columns...)
3830  * 
3831  * @constructor
3832  * Create a new Row
3833  * @param {Object} config The config object
3834  */
3835
3836 Roo.bootstrap.Row = function(config){
3837     Roo.bootstrap.Row.superclass.constructor.call(this, config);
3838 };
3839
3840 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
3841     
3842     getAutoCreate : function(){
3843        return {
3844             cls: 'row clearfix'
3845        };
3846     }
3847     
3848     
3849 });
3850
3851  
3852
3853  /*
3854  * - LGPL
3855  *
3856  * element
3857  * 
3858  */
3859
3860 /**
3861  * @class Roo.bootstrap.Element
3862  * @extends Roo.bootstrap.Component
3863  * Bootstrap Element class
3864  * @cfg {String} html contents of the element
3865  * @cfg {String} tag tag of the element
3866  * @cfg {String} cls class of the element
3867  * 
3868  * @constructor
3869  * Create a new Element
3870  * @param {Object} config The config object
3871  */
3872
3873 Roo.bootstrap.Element = function(config){
3874     Roo.bootstrap.Element.superclass.constructor.call(this, config);
3875 };
3876
3877 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
3878     
3879     tag: 'div',
3880     cls: '',
3881     html: '',
3882      
3883     
3884     getAutoCreate : function(){
3885         
3886         var cfg = {
3887             tag: this.tag,
3888             cls: this.cls,
3889             html: this.html
3890         }
3891         
3892         
3893         
3894         return cfg;
3895     }
3896    
3897 });
3898
3899  
3900
3901  /*
3902  * - LGPL
3903  *
3904  * pagination
3905  * 
3906  */
3907
3908 /**
3909  * @class Roo.bootstrap.Pagination
3910  * @extends Roo.bootstrap.Component
3911  * Bootstrap Pagination class
3912  * @cfg {String} size xs | sm | md | lg
3913  * @cfg {Boolean} inverse false | true
3914  * 
3915  * @constructor
3916  * Create a new Pagination
3917  * @param {Object} config The config object
3918  */
3919
3920 Roo.bootstrap.Pagination = function(config){
3921     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
3922 };
3923
3924 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
3925     
3926     cls: false,
3927     size: false,
3928     inverse: false,
3929     
3930     getAutoCreate : function(){
3931         var cfg = {
3932             tag: 'ul',
3933                 cls: 'pagination'
3934         };
3935         if (this.inverse) {
3936             cfg.cls += ' inverse';
3937         }
3938         if (this.html) {
3939             cfg.html=this.html;
3940         }
3941         if (this.cls) {
3942             cfg.cls += " " + this.cls;
3943         }
3944         return cfg;
3945     }
3946    
3947 });
3948
3949  
3950
3951  /*
3952  * - LGPL
3953  *
3954  * Pagination item
3955  * 
3956  */
3957
3958
3959 /**
3960  * @class Roo.bootstrap.PaginationItem
3961  * @extends Roo.bootstrap.Component
3962  * Bootstrap PaginationItem class
3963  * @cfg {String} html text
3964  * @cfg {String} href the link
3965  * @cfg {Boolean} preventDefault (true | false) default true
3966  * @cfg {Boolean} active (true | false) default false
3967  * 
3968  * 
3969  * @constructor
3970  * Create a new PaginationItem
3971  * @param {Object} config The config object
3972  */
3973
3974
3975 Roo.bootstrap.PaginationItem = function(config){
3976     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
3977     this.addEvents({
3978         // raw events
3979         /**
3980          * @event click
3981          * The raw click event for the entire grid.
3982          * @param {Roo.EventObject} e
3983          */
3984         "click" : true
3985     });
3986 };
3987
3988 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
3989     
3990     href : false,
3991     html : false,
3992     preventDefault: true,
3993     active : false,
3994     cls : false,
3995     
3996     getAutoCreate : function(){
3997         var cfg= {
3998             tag: 'li',
3999             cn: [
4000                 {
4001                     tag : 'a',
4002                     href : this.href ? this.href : '#',
4003                     html : this.html ? this.html : ''
4004                 }
4005             ]
4006         };
4007         
4008         if(this.cls){
4009             cfg.cls = this.cls;
4010         }
4011         
4012         if(this.active){
4013             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4014         }
4015         
4016         return cfg;
4017     },
4018     
4019     initEvents: function() {
4020         
4021         this.el.on('click', this.onClick, this);
4022         
4023     },
4024     onClick : function(e)
4025     {
4026         Roo.log('PaginationItem on click ');
4027         if(this.preventDefault){
4028             e.preventDefault();
4029         }
4030         
4031         this.fireEvent('click', this, e);
4032     }
4033    
4034 });
4035
4036  
4037
4038  /*
4039  * - LGPL
4040  *
4041  * slider
4042  * 
4043  */
4044
4045
4046 /**
4047  * @class Roo.bootstrap.Slider
4048  * @extends Roo.bootstrap.Component
4049  * Bootstrap Slider class
4050  *    
4051  * @constructor
4052  * Create a new Slider
4053  * @param {Object} config The config object
4054  */
4055
4056 Roo.bootstrap.Slider = function(config){
4057     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4058 };
4059
4060 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
4061     
4062     getAutoCreate : function(){
4063         
4064         var cfg = {
4065             tag: 'div',
4066             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4067             cn: [
4068                 {
4069                     tag: 'a',
4070                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
4071                 }
4072             ]
4073         }
4074         
4075         return cfg;
4076     }
4077    
4078 });
4079
4080  /*
4081  * Based on:
4082  * Ext JS Library 1.1.1
4083  * Copyright(c) 2006-2007, Ext JS, LLC.
4084  *
4085  * Originally Released Under LGPL - original licence link has changed is not relivant.
4086  *
4087  * Fork - LGPL
4088  * <script type="text/javascript">
4089  */
4090  
4091
4092 /**
4093  * @class Roo.grid.ColumnModel
4094  * @extends Roo.util.Observable
4095  * This is the default implementation of a ColumnModel used by the Grid. It defines
4096  * the columns in the grid.
4097  * <br>Usage:<br>
4098  <pre><code>
4099  var colModel = new Roo.grid.ColumnModel([
4100         {header: "Ticker", width: 60, sortable: true, locked: true},
4101         {header: "Company Name", width: 150, sortable: true},
4102         {header: "Market Cap.", width: 100, sortable: true},
4103         {header: "$ Sales", width: 100, sortable: true, renderer: money},
4104         {header: "Employees", width: 100, sortable: true, resizable: false}
4105  ]);
4106  </code></pre>
4107  * <p>
4108  
4109  * The config options listed for this class are options which may appear in each
4110  * individual column definition.
4111  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4112  * @constructor
4113  * @param {Object} config An Array of column config objects. See this class's
4114  * config objects for details.
4115 */
4116 Roo.grid.ColumnModel = function(config){
4117         /**
4118      * The config passed into the constructor
4119      */
4120     this.config = config;
4121     this.lookup = {};
4122
4123     // if no id, create one
4124     // if the column does not have a dataIndex mapping,
4125     // map it to the order it is in the config
4126     for(var i = 0, len = config.length; i < len; i++){
4127         var c = config[i];
4128         if(typeof c.dataIndex == "undefined"){
4129             c.dataIndex = i;
4130         }
4131         if(typeof c.renderer == "string"){
4132             c.renderer = Roo.util.Format[c.renderer];
4133         }
4134         if(typeof c.id == "undefined"){
4135             c.id = Roo.id();
4136         }
4137         if(c.editor && c.editor.xtype){
4138             c.editor  = Roo.factory(c.editor, Roo.grid);
4139         }
4140         if(c.editor && c.editor.isFormField){
4141             c.editor = new Roo.grid.GridEditor(c.editor);
4142         }
4143         this.lookup[c.id] = c;
4144     }
4145
4146     /**
4147      * The width of columns which have no width specified (defaults to 100)
4148      * @type Number
4149      */
4150     this.defaultWidth = 100;
4151
4152     /**
4153      * Default sortable of columns which have no sortable specified (defaults to false)
4154      * @type Boolean
4155      */
4156     this.defaultSortable = false;
4157
4158     this.addEvents({
4159         /**
4160              * @event widthchange
4161              * Fires when the width of a column changes.
4162              * @param {ColumnModel} this
4163              * @param {Number} columnIndex The column index
4164              * @param {Number} newWidth The new width
4165              */
4166             "widthchange": true,
4167         /**
4168              * @event headerchange
4169              * Fires when the text of a header changes.
4170              * @param {ColumnModel} this
4171              * @param {Number} columnIndex The column index
4172              * @param {Number} newText The new header text
4173              */
4174             "headerchange": true,
4175         /**
4176              * @event hiddenchange
4177              * Fires when a column is hidden or "unhidden".
4178              * @param {ColumnModel} this
4179              * @param {Number} columnIndex The column index
4180              * @param {Boolean} hidden true if hidden, false otherwise
4181              */
4182             "hiddenchange": true,
4183             /**
4184          * @event columnmoved
4185          * Fires when a column is moved.
4186          * @param {ColumnModel} this
4187          * @param {Number} oldIndex
4188          * @param {Number} newIndex
4189          */
4190         "columnmoved" : true,
4191         /**
4192          * @event columlockchange
4193          * Fires when a column's locked state is changed
4194          * @param {ColumnModel} this
4195          * @param {Number} colIndex
4196          * @param {Boolean} locked true if locked
4197          */
4198         "columnlockchange" : true
4199     });
4200     Roo.grid.ColumnModel.superclass.constructor.call(this);
4201 };
4202 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4203     /**
4204      * @cfg {String} header The header text to display in the Grid view.
4205      */
4206     /**
4207      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4208      * {@link Roo.data.Record} definition from which to draw the column's value. If not
4209      * specified, the column's index is used as an index into the Record's data Array.
4210      */
4211     /**
4212      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4213      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4214      */
4215     /**
4216      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4217      * Defaults to the value of the {@link #defaultSortable} property.
4218      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4219      */
4220     /**
4221      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
4222      */
4223     /**
4224      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
4225      */
4226     /**
4227      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4228      */
4229     /**
4230      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4231      */
4232     /**
4233      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4234      * given the cell's data value. See {@link #setRenderer}. If not specified, the
4235      * default renderer uses the raw data value. If an object is returned (bootstrap only)
4236      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4237      */
4238        /**
4239      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
4240      */
4241     /**
4242      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
4243      */
4244
4245     /**
4246      * Returns the id of the column at the specified index.
4247      * @param {Number} index The column index
4248      * @return {String} the id
4249      */
4250     getColumnId : function(index){
4251         return this.config[index].id;
4252     },
4253
4254     /**
4255      * Returns the column for a specified id.
4256      * @param {String} id The column id
4257      * @return {Object} the column
4258      */
4259     getColumnById : function(id){
4260         return this.lookup[id];
4261     },
4262
4263     
4264     /**
4265      * Returns the column for a specified dataIndex.
4266      * @param {String} dataIndex The column dataIndex
4267      * @return {Object|Boolean} the column or false if not found
4268      */
4269     getColumnByDataIndex: function(dataIndex){
4270         var index = this.findColumnIndex(dataIndex);
4271         return index > -1 ? this.config[index] : false;
4272     },
4273     
4274     /**
4275      * Returns the index for a specified column id.
4276      * @param {String} id The column id
4277      * @return {Number} the index, or -1 if not found
4278      */
4279     getIndexById : function(id){
4280         for(var i = 0, len = this.config.length; i < len; i++){
4281             if(this.config[i].id == id){
4282                 return i;
4283             }
4284         }
4285         return -1;
4286     },
4287     
4288     /**
4289      * Returns the index for a specified column dataIndex.
4290      * @param {String} dataIndex The column dataIndex
4291      * @return {Number} the index, or -1 if not found
4292      */
4293     
4294     findColumnIndex : function(dataIndex){
4295         for(var i = 0, len = this.config.length; i < len; i++){
4296             if(this.config[i].dataIndex == dataIndex){
4297                 return i;
4298             }
4299         }
4300         return -1;
4301     },
4302     
4303     
4304     moveColumn : function(oldIndex, newIndex){
4305         var c = this.config[oldIndex];
4306         this.config.splice(oldIndex, 1);
4307         this.config.splice(newIndex, 0, c);
4308         this.dataMap = null;
4309         this.fireEvent("columnmoved", this, oldIndex, newIndex);
4310     },
4311
4312     isLocked : function(colIndex){
4313         return this.config[colIndex].locked === true;
4314     },
4315
4316     setLocked : function(colIndex, value, suppressEvent){
4317         if(this.isLocked(colIndex) == value){
4318             return;
4319         }
4320         this.config[colIndex].locked = value;
4321         if(!suppressEvent){
4322             this.fireEvent("columnlockchange", this, colIndex, value);
4323         }
4324     },
4325
4326     getTotalLockedWidth : function(){
4327         var totalWidth = 0;
4328         for(var i = 0; i < this.config.length; i++){
4329             if(this.isLocked(i) && !this.isHidden(i)){
4330                 this.totalWidth += this.getColumnWidth(i);
4331             }
4332         }
4333         return totalWidth;
4334     },
4335
4336     getLockedCount : function(){
4337         for(var i = 0, len = this.config.length; i < len; i++){
4338             if(!this.isLocked(i)){
4339                 return i;
4340             }
4341         }
4342     },
4343
4344     /**
4345      * Returns the number of columns.
4346      * @return {Number}
4347      */
4348     getColumnCount : function(visibleOnly){
4349         if(visibleOnly === true){
4350             var c = 0;
4351             for(var i = 0, len = this.config.length; i < len; i++){
4352                 if(!this.isHidden(i)){
4353                     c++;
4354                 }
4355             }
4356             return c;
4357         }
4358         return this.config.length;
4359     },
4360
4361     /**
4362      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
4363      * @param {Function} fn
4364      * @param {Object} scope (optional)
4365      * @return {Array} result
4366      */
4367     getColumnsBy : function(fn, scope){
4368         var r = [];
4369         for(var i = 0, len = this.config.length; i < len; i++){
4370             var c = this.config[i];
4371             if(fn.call(scope||this, c, i) === true){
4372                 r[r.length] = c;
4373             }
4374         }
4375         return r;
4376     },
4377
4378     /**
4379      * Returns true if the specified column is sortable.
4380      * @param {Number} col The column index
4381      * @return {Boolean}
4382      */
4383     isSortable : function(col){
4384         if(typeof this.config[col].sortable == "undefined"){
4385             return this.defaultSortable;
4386         }
4387         return this.config[col].sortable;
4388     },
4389
4390     /**
4391      * Returns the rendering (formatting) function defined for the column.
4392      * @param {Number} col The column index.
4393      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
4394      */
4395     getRenderer : function(col){
4396         if(!this.config[col].renderer){
4397             return Roo.grid.ColumnModel.defaultRenderer;
4398         }
4399         return this.config[col].renderer;
4400     },
4401
4402     /**
4403      * Sets the rendering (formatting) function for a column.
4404      * @param {Number} col The column index
4405      * @param {Function} fn The function to use to process the cell's raw data
4406      * to return HTML markup for the grid view. The render function is called with
4407      * the following parameters:<ul>
4408      * <li>Data value.</li>
4409      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
4410      * <li>css A CSS style string to apply to the table cell.</li>
4411      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
4412      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
4413      * <li>Row index</li>
4414      * <li>Column index</li>
4415      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
4416      */
4417     setRenderer : function(col, fn){
4418         this.config[col].renderer = fn;
4419     },
4420
4421     /**
4422      * Returns the width for the specified column.
4423      * @param {Number} col The column index
4424      * @return {Number}
4425      */
4426     getColumnWidth : function(col){
4427         return this.config[col].width * 1 || this.defaultWidth;
4428     },
4429
4430     /**
4431      * Sets the width for a column.
4432      * @param {Number} col The column index
4433      * @param {Number} width The new width
4434      */
4435     setColumnWidth : function(col, width, suppressEvent){
4436         this.config[col].width = width;
4437         this.totalWidth = null;
4438         if(!suppressEvent){
4439              this.fireEvent("widthchange", this, col, width);
4440         }
4441     },
4442
4443     /**
4444      * Returns the total width of all columns.
4445      * @param {Boolean} includeHidden True to include hidden column widths
4446      * @return {Number}
4447      */
4448     getTotalWidth : function(includeHidden){
4449         if(!this.totalWidth){
4450             this.totalWidth = 0;
4451             for(var i = 0, len = this.config.length; i < len; i++){
4452                 if(includeHidden || !this.isHidden(i)){
4453                     this.totalWidth += this.getColumnWidth(i);
4454                 }
4455             }
4456         }
4457         return this.totalWidth;
4458     },
4459
4460     /**
4461      * Returns the header for the specified column.
4462      * @param {Number} col The column index
4463      * @return {String}
4464      */
4465     getColumnHeader : function(col){
4466         return this.config[col].header;
4467     },
4468
4469     /**
4470      * Sets the header for a column.
4471      * @param {Number} col The column index
4472      * @param {String} header The new header
4473      */
4474     setColumnHeader : function(col, header){
4475         this.config[col].header = header;
4476         this.fireEvent("headerchange", this, col, header);
4477     },
4478
4479     /**
4480      * Returns the tooltip for the specified column.
4481      * @param {Number} col The column index
4482      * @return {String}
4483      */
4484     getColumnTooltip : function(col){
4485             return this.config[col].tooltip;
4486     },
4487     /**
4488      * Sets the tooltip for a column.
4489      * @param {Number} col The column index
4490      * @param {String} tooltip The new tooltip
4491      */
4492     setColumnTooltip : function(col, tooltip){
4493             this.config[col].tooltip = tooltip;
4494     },
4495
4496     /**
4497      * Returns the dataIndex for the specified column.
4498      * @param {Number} col The column index
4499      * @return {Number}
4500      */
4501     getDataIndex : function(col){
4502         return this.config[col].dataIndex;
4503     },
4504
4505     /**
4506      * Sets the dataIndex for a column.
4507      * @param {Number} col The column index
4508      * @param {Number} dataIndex The new dataIndex
4509      */
4510     setDataIndex : function(col, dataIndex){
4511         this.config[col].dataIndex = dataIndex;
4512     },
4513
4514     
4515     
4516     /**
4517      * Returns true if the cell is editable.
4518      * @param {Number} colIndex The column index
4519      * @param {Number} rowIndex The row index
4520      * @return {Boolean}
4521      */
4522     isCellEditable : function(colIndex, rowIndex){
4523         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
4524     },
4525
4526     /**
4527      * Returns the editor defined for the cell/column.
4528      * return false or null to disable editing.
4529      * @param {Number} colIndex The column index
4530      * @param {Number} rowIndex The row index
4531      * @return {Object}
4532      */
4533     getCellEditor : function(colIndex, rowIndex){
4534         return this.config[colIndex].editor;
4535     },
4536
4537     /**
4538      * Sets if a column is editable.
4539      * @param {Number} col The column index
4540      * @param {Boolean} editable True if the column is editable
4541      */
4542     setEditable : function(col, editable){
4543         this.config[col].editable = editable;
4544     },
4545
4546
4547     /**
4548      * Returns true if the column is hidden.
4549      * @param {Number} colIndex The column index
4550      * @return {Boolean}
4551      */
4552     isHidden : function(colIndex){
4553         return this.config[colIndex].hidden;
4554     },
4555
4556
4557     /**
4558      * Returns true if the column width cannot be changed
4559      */
4560     isFixed : function(colIndex){
4561         return this.config[colIndex].fixed;
4562     },
4563
4564     /**
4565      * Returns true if the column can be resized
4566      * @return {Boolean}
4567      */
4568     isResizable : function(colIndex){
4569         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
4570     },
4571     /**
4572      * Sets if a column is hidden.
4573      * @param {Number} colIndex The column index
4574      * @param {Boolean} hidden True if the column is hidden
4575      */
4576     setHidden : function(colIndex, hidden){
4577         this.config[colIndex].hidden = hidden;
4578         this.totalWidth = null;
4579         this.fireEvent("hiddenchange", this, colIndex, hidden);
4580     },
4581
4582     /**
4583      * Sets the editor for a column.
4584      * @param {Number} col The column index
4585      * @param {Object} editor The editor object
4586      */
4587     setEditor : function(col, editor){
4588         this.config[col].editor = editor;
4589     }
4590 });
4591
4592 Roo.grid.ColumnModel.defaultRenderer = function(value){
4593         if(typeof value == "string" && value.length < 1){
4594             return "&#160;";
4595         }
4596         return value;
4597 };
4598
4599 // Alias for backwards compatibility
4600 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
4601 /*
4602  * Based on:
4603  * Ext JS Library 1.1.1
4604  * Copyright(c) 2006-2007, Ext JS, LLC.
4605  *
4606  * Originally Released Under LGPL - original licence link has changed is not relivant.
4607  *
4608  * Fork - LGPL
4609  * <script type="text/javascript">
4610  */
4611  
4612 /**
4613  * @class Roo.LoadMask
4614  * A simple utility class for generically masking elements while loading data.  If the element being masked has
4615  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
4616  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
4617  * element's UpdateManager load indicator and will be destroyed after the initial load.
4618  * @constructor
4619  * Create a new LoadMask
4620  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
4621  * @param {Object} config The config object
4622  */
4623 Roo.LoadMask = function(el, config){
4624     this.el = Roo.get(el);
4625     Roo.apply(this, config);
4626     if(this.store){
4627         this.store.on('beforeload', this.onBeforeLoad, this);
4628         this.store.on('load', this.onLoad, this);
4629         this.store.on('loadexception', this.onLoadException, this);
4630         this.removeMask = false;
4631     }else{
4632         var um = this.el.getUpdateManager();
4633         um.showLoadIndicator = false; // disable the default indicator
4634         um.on('beforeupdate', this.onBeforeLoad, this);
4635         um.on('update', this.onLoad, this);
4636         um.on('failure', this.onLoad, this);
4637         this.removeMask = true;
4638     }
4639 };
4640
4641 Roo.LoadMask.prototype = {
4642     /**
4643      * @cfg {Boolean} removeMask
4644      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
4645      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
4646      */
4647     /**
4648      * @cfg {String} msg
4649      * The text to display in a centered loading message box (defaults to 'Loading...')
4650      */
4651     msg : 'Loading...',
4652     /**
4653      * @cfg {String} msgCls
4654      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
4655      */
4656     msgCls : 'x-mask-loading',
4657
4658     /**
4659      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
4660      * @type Boolean
4661      */
4662     disabled: false,
4663
4664     /**
4665      * Disables the mask to prevent it from being displayed
4666      */
4667     disable : function(){
4668        this.disabled = true;
4669     },
4670
4671     /**
4672      * Enables the mask so that it can be displayed
4673      */
4674     enable : function(){
4675         this.disabled = false;
4676     },
4677     
4678     onLoadException : function()
4679     {
4680         Roo.log(arguments);
4681         
4682         if (typeof(arguments[3]) != 'undefined') {
4683             Roo.MessageBox.alert("Error loading",arguments[3]);
4684         } 
4685         /*
4686         try {
4687             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
4688                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
4689             }   
4690         } catch(e) {
4691             
4692         }
4693         */
4694     
4695         
4696         
4697         this.el.unmask(this.removeMask);
4698     },
4699     // private
4700     onLoad : function()
4701     {
4702         this.el.unmask(this.removeMask);
4703     },
4704
4705     // private
4706     onBeforeLoad : function(){
4707         if(!this.disabled){
4708             this.el.mask(this.msg, this.msgCls);
4709         }
4710     },
4711
4712     // private
4713     destroy : function(){
4714         if(this.store){
4715             this.store.un('beforeload', this.onBeforeLoad, this);
4716             this.store.un('load', this.onLoad, this);
4717             this.store.un('loadexception', this.onLoadException, this);
4718         }else{
4719             var um = this.el.getUpdateManager();
4720             um.un('beforeupdate', this.onBeforeLoad, this);
4721             um.un('update', this.onLoad, this);
4722             um.un('failure', this.onLoad, this);
4723         }
4724     }
4725 };/*
4726  * - LGPL
4727  *
4728  * table
4729  * 
4730  */
4731
4732 /**
4733  * @class Roo.bootstrap.Table
4734  * @extends Roo.bootstrap.Component
4735  * Bootstrap Table class
4736  * @cfg {String} cls table class
4737  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
4738  * @cfg {String} bgcolor Specifies the background color for a table
4739  * @cfg {Number} border Specifies whether the table cells should have borders or not
4740  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
4741  * @cfg {Number} cellspacing Specifies the space between cells
4742  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
4743  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
4744  * @cfg {String} sortable Specifies that the table should be sortable
4745  * @cfg {String} summary Specifies a summary of the content of a table
4746  * @cfg {Number} width Specifies the width of a table
4747  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
4748  * 
4749  * @cfg {boolean} striped Should the rows be alternative striped
4750  * @cfg {boolean} bordered Add borders to the table
4751  * @cfg {boolean} hover Add hover highlighting
4752  * @cfg {boolean} condensed Format condensed
4753  * @cfg {boolean} responsive Format condensed
4754  * @cfg {Boolean} loadMask (true|false) default false
4755  * @cfg {Boolean} tfoot (true|false) generate tfoot, default true
4756  * @cfg {Boolean} thead (true|false) generate thead, default true
4757  * @cfg {Boolean} RowSelection (true|false) default false
4758  * @cfg {Boolean} CellSelection (true|false) default false
4759  *
4760  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
4761  
4762  * 
4763  * @constructor
4764  * Create a new Table
4765  * @param {Object} config The config object
4766  */
4767
4768 Roo.bootstrap.Table = function(config){
4769     Roo.bootstrap.Table.superclass.constructor.call(this, config);
4770     
4771     if (this.sm) {
4772         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
4773         this.sm = this.selModel;
4774         this.sm.xmodule = this.xmodule || false;
4775     }
4776     if (this.cm && typeof(this.cm.config) == 'undefined') {
4777         this.colModel = new Roo.grid.ColumnModel(this.cm);
4778         this.cm = this.colModel;
4779         this.cm.xmodule = this.xmodule || false;
4780     }
4781     if (this.store) {
4782         this.store= Roo.factory(this.store, Roo.data);
4783         this.ds = this.store;
4784         this.ds.xmodule = this.xmodule || false;
4785          
4786     }
4787     if (this.footer && this.store) {
4788         this.footer.dataSource = this.ds;
4789         this.footer = Roo.factory(this.footer);
4790     }
4791     
4792     /** @private */
4793     this.addEvents({
4794         /**
4795          * @event cellclick
4796          * Fires when a cell is clicked
4797          * @param {Roo.bootstrap.Table} this
4798          * @param {Roo.Element} el
4799          * @param {Number} rowIndex
4800          * @param {Number} columnIndex
4801          * @param {Roo.EventObject} e
4802          */
4803         "cellclick" : true,
4804         /**
4805          * @event celldblclick
4806          * Fires when a cell is double clicked
4807          * @param {Roo.bootstrap.Table} this
4808          * @param {Roo.Element} el
4809          * @param {Number} rowIndex
4810          * @param {Number} columnIndex
4811          * @param {Roo.EventObject} e
4812          */
4813         "celldblclick" : true,
4814         /**
4815          * @event rowclick
4816          * Fires when a row is clicked
4817          * @param {Roo.bootstrap.Table} this
4818          * @param {Roo.Element} el
4819          * @param {Number} rowIndex
4820          * @param {Roo.EventObject} e
4821          */
4822         "rowclick" : true,
4823         /**
4824          * @event rowdblclick
4825          * Fires when a row is double clicked
4826          * @param {Roo.bootstrap.Table} this
4827          * @param {Roo.Element} el
4828          * @param {Number} rowIndex
4829          * @param {Roo.EventObject} e
4830          */
4831         "rowdblclick" : true,
4832         /**
4833          * @event mouseover
4834          * Fires when a mouseover occur
4835          * @param {Roo.bootstrap.Table} this
4836          * @param {Roo.Element} el
4837          * @param {Number} rowIndex
4838          * @param {Number} columnIndex
4839          * @param {Roo.EventObject} e
4840          */
4841         "mouseover" : true,
4842         /**
4843          * @event mouseout
4844          * Fires when a mouseout occur
4845          * @param {Roo.bootstrap.Table} this
4846          * @param {Roo.Element} el
4847          * @param {Number} rowIndex
4848          * @param {Number} columnIndex
4849          * @param {Roo.EventObject} e
4850          */
4851         "mouseout" : true,
4852         /**
4853          * @event rowclass
4854          * Fires when a row is rendered, so you can change add a style to it.
4855          * @param {Roo.bootstrap.Table} this
4856          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
4857          */
4858         'rowclass' : true
4859         
4860     });
4861 };
4862
4863 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
4864     
4865     cls: false,
4866     align: false,
4867     bgcolor: false,
4868     border: false,
4869     cellpadding: false,
4870     cellspacing: false,
4871     frame: false,
4872     rules: false,
4873     sortable: false,
4874     summary: false,
4875     width: false,
4876     striped : false,
4877     bordered: false,
4878     hover:  false,
4879     condensed : false,
4880     responsive : false,
4881     sm : false,
4882     cm : false,
4883     store : false,
4884     loadMask : false,
4885     tfoot : true,
4886     thead : true,
4887     RowSelection : false,
4888     CellSelection : false,
4889     layout : false,
4890     
4891     // Roo.Element - the tbody
4892     mainBody: false, 
4893     
4894     getAutoCreate : function(){
4895         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
4896         
4897         cfg = {
4898             tag: 'table',
4899             cls : 'table',
4900             cn : []
4901         }
4902             
4903         if (this.striped) {
4904             cfg.cls += ' table-striped';
4905         }
4906         
4907         if (this.hover) {
4908             cfg.cls += ' table-hover';
4909         }
4910         if (this.bordered) {
4911             cfg.cls += ' table-bordered';
4912         }
4913         if (this.condensed) {
4914             cfg.cls += ' table-condensed';
4915         }
4916         if (this.responsive) {
4917             cfg.cls += ' table-responsive';
4918         }
4919         
4920         if (this.cls) {
4921             cfg.cls+=  ' ' +this.cls;
4922         }
4923         
4924         // this lot should be simplifed...
4925         
4926         if (this.align) {
4927             cfg.align=this.align;
4928         }
4929         if (this.bgcolor) {
4930             cfg.bgcolor=this.bgcolor;
4931         }
4932         if (this.border) {
4933             cfg.border=this.border;
4934         }
4935         if (this.cellpadding) {
4936             cfg.cellpadding=this.cellpadding;
4937         }
4938         if (this.cellspacing) {
4939             cfg.cellspacing=this.cellspacing;
4940         }
4941         if (this.frame) {
4942             cfg.frame=this.frame;
4943         }
4944         if (this.rules) {
4945             cfg.rules=this.rules;
4946         }
4947         if (this.sortable) {
4948             cfg.sortable=this.sortable;
4949         }
4950         if (this.summary) {
4951             cfg.summary=this.summary;
4952         }
4953         if (this.width) {
4954             cfg.width=this.width;
4955         }
4956         if (this.layout) {
4957             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
4958         }
4959         
4960         if(this.store || this.cm){
4961             if(this.thead){
4962                 cfg.cn.push(this.renderHeader());
4963             }
4964             
4965             cfg.cn.push(this.renderBody());
4966             
4967             if(this.tfoot){
4968                 cfg.cn.push(this.renderFooter());
4969             }
4970             
4971             cfg.cls+=  ' TableGrid';
4972         }
4973         
4974         return { cn : [ cfg ] };
4975     },
4976     
4977     initEvents : function()
4978     {   
4979         if(!this.store || !this.cm){
4980             return;
4981         }
4982         
4983         //Roo.log('initEvents with ds!!!!');
4984         
4985         this.mainBody = this.el.select('tbody', true).first();
4986         
4987         
4988         var _this = this;
4989         
4990         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
4991             e.on('click', _this.sort, _this);
4992         });
4993         
4994         this.el.on("click", this.onClick, this);
4995         this.el.on("dblclick", this.onDblClick, this);
4996         
4997         this.parent().el.setStyle('position', 'relative');
4998         if (this.footer) {
4999             this.footer.parentId = this.id;
5000             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
5001         }
5002         
5003         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5004         
5005         this.store.on('load', this.onLoad, this);
5006         this.store.on('beforeload', this.onBeforeLoad, this);
5007         this.store.on('update', this.onUpdate, this);
5008         
5009     },
5010     
5011     onMouseover : function(e, el)
5012     {
5013         var cell = Roo.get(el);
5014         
5015         if(!cell){
5016             return;
5017         }
5018         
5019         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5020             cell = cell.findParent('td', false, true);
5021         }
5022         
5023         var row = cell.findParent('tr', false, true);
5024         var cellIndex = cell.dom.cellIndex;
5025         var rowIndex = row.dom.rowIndex - 1; // start from 0
5026         
5027         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5028         
5029     },
5030     
5031     onMouseout : function(e, el)
5032     {
5033         var cell = Roo.get(el);
5034         
5035         if(!cell){
5036             return;
5037         }
5038         
5039         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5040             cell = cell.findParent('td', false, true);
5041         }
5042         
5043         var row = cell.findParent('tr', false, true);
5044         var cellIndex = cell.dom.cellIndex;
5045         var rowIndex = row.dom.rowIndex - 1; // start from 0
5046         
5047         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5048         
5049     },
5050     
5051     onClick : function(e, el)
5052     {
5053         var cell = Roo.get(el);
5054         
5055         if(!cell || (!this.CellSelection && !this.RowSelection)){
5056             return;
5057         }
5058         
5059         
5060         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5061             cell = cell.findParent('td', false, true);
5062         }
5063         
5064         var row = cell.findParent('tr', false, true);
5065         var cellIndex = cell.dom.cellIndex;
5066         var rowIndex = row.dom.rowIndex - 1;
5067         
5068         if(this.CellSelection){
5069             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5070         }
5071         
5072         if(this.RowSelection){
5073             this.fireEvent('rowclick', this, row, rowIndex, e);
5074         }
5075         
5076         
5077     },
5078     
5079     onDblClick : function(e,el)
5080     {
5081         var cell = Roo.get(el);
5082         
5083         if(!cell || (!this.CellSelection && !this.RowSelection)){
5084             return;
5085         }
5086         
5087         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5088             cell = cell.findParent('td', false, true);
5089         }
5090         
5091         var row = cell.findParent('tr', false, true);
5092         var cellIndex = cell.dom.cellIndex;
5093         var rowIndex = row.dom.rowIndex - 1;
5094         
5095         if(this.CellSelection){
5096             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5097         }
5098         
5099         if(this.RowSelection){
5100             this.fireEvent('rowdblclick', this, row, rowIndex, e);
5101         }
5102     },
5103     
5104     sort : function(e,el)
5105     {
5106         var col = Roo.get(el)
5107         
5108         if(!col.hasClass('sortable')){
5109             return;
5110         }
5111         
5112         var sort = col.attr('sort');
5113         var dir = 'ASC';
5114         
5115         if(col.hasClass('glyphicon-arrow-up')){
5116             dir = 'DESC';
5117         }
5118         
5119         this.store.sortInfo = {field : sort, direction : dir};
5120         
5121         if (this.footer) {
5122             Roo.log("calling footer first");
5123             this.footer.onClick('first');
5124         } else {
5125         
5126             this.store.load({ params : { start : 0 } });
5127         }
5128     },
5129     
5130     renderHeader : function()
5131     {
5132         var header = {
5133             tag: 'thead',
5134             cn : []
5135         };
5136         
5137         var cm = this.cm;
5138         
5139         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5140             
5141             var config = cm.config[i];
5142                     
5143             var c = {
5144                 tag: 'th',
5145                 style : '',
5146                 html: cm.getColumnHeader(i)
5147             };
5148             
5149             if(typeof(config.hidden) != 'undefined' && config.hidden){
5150                 c.style += ' display:none;';
5151             }
5152             
5153             if(typeof(config.dataIndex) != 'undefined'){
5154                 c.sort = config.dataIndex;
5155             }
5156             
5157             if(typeof(config.sortable) != 'undefined' && config.sortable){
5158                 c.cls = 'sortable';
5159             }
5160             
5161             if(typeof(config.align) != 'undefined' && config.align.length){
5162                 c.style += ' text-align:' + config.align + ';';
5163             }
5164             
5165             if(typeof(config.width) != 'undefined'){
5166                 c.style += ' width:' + config.width + 'px;';
5167             }
5168             
5169             header.cn.push(c)
5170         }
5171         
5172         return header;
5173     },
5174     
5175     renderBody : function()
5176     {
5177         var body = {
5178             tag: 'tbody',
5179             cn : [
5180                 {
5181                     tag: 'tr',
5182                     cn : [
5183                         {
5184                             tag : 'td',
5185                             colspan :  this.cm.getColumnCount()
5186                         }
5187                     ]
5188                 }
5189             ]
5190         };
5191         
5192         return body;
5193     },
5194     
5195     renderFooter : function()
5196     {
5197         var footer = {
5198             tag: 'tfoot',
5199             cn : [
5200                 {
5201                     tag: 'tr',
5202                     cn : [
5203                         {
5204                             tag : 'td',
5205                             colspan :  this.cm.getColumnCount()
5206                         }
5207                     ]
5208                 }
5209             ]
5210         };
5211         
5212         return footer;
5213     },
5214     
5215     
5216     
5217     onLoad : function()
5218     {
5219         Roo.log('ds onload');
5220         this.clear();
5221         
5222         var _this = this;
5223         var cm = this.cm;
5224         var ds = this.store;
5225         
5226         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5227             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
5228             
5229             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
5230                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
5231             }
5232             
5233             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
5234                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
5235             }
5236         });
5237         
5238         var tbody =  this.mainBody;
5239               
5240         if(ds.getCount() > 0){
5241             ds.data.each(function(d,rowIndex){
5242                 var row =  this.renderRow(cm, ds, rowIndex);
5243                 
5244                 tbody.createChild(row);
5245                 
5246                 var _this = this;
5247                 
5248                 if(row.cellObjects.length){
5249                     Roo.each(row.cellObjects, function(r){
5250                         _this.renderCellObject(r);
5251                     })
5252                 }
5253                 
5254             }, this);
5255         }
5256         
5257         Roo.each(this.el.select('tbody td', true).elements, function(e){
5258             e.on('mouseover', _this.onMouseover, _this);
5259         });
5260         
5261         Roo.each(this.el.select('tbody td', true).elements, function(e){
5262             e.on('mouseout', _this.onMouseout, _this);
5263         });
5264
5265         //if(this.loadMask){
5266         //    this.maskEl.hide();
5267         //}
5268     },
5269     
5270     
5271     onUpdate : function(ds,record)
5272     {
5273         this.refreshRow(record);
5274     },
5275     onRemove : function(ds, record, index, isUpdate){
5276         if(isUpdate !== true){
5277             this.fireEvent("beforerowremoved", this, index, record);
5278         }
5279         var bt = this.mainBody.dom;
5280         if(bt.rows[index]){
5281             bt.removeChild(bt.rows[index]);
5282         }
5283         
5284         if(isUpdate !== true){
5285             //this.stripeRows(index);
5286             //this.syncRowHeights(index, index);
5287             //this.layout();
5288             this.fireEvent("rowremoved", this, index, record);
5289         }
5290     },
5291     
5292     
5293     refreshRow : function(record){
5294         var ds = this.store, index;
5295         if(typeof record == 'number'){
5296             index = record;
5297             record = ds.getAt(index);
5298         }else{
5299             index = ds.indexOf(record);
5300         }
5301         this.insertRow(ds, index, true);
5302         this.onRemove(ds, record, index+1, true);
5303         //this.syncRowHeights(index, index);
5304         //this.layout();
5305         this.fireEvent("rowupdated", this, index, record);
5306     },
5307     
5308     insertRow : function(dm, rowIndex, isUpdate){
5309         
5310         if(!isUpdate){
5311             this.fireEvent("beforerowsinserted", this, rowIndex);
5312         }
5313             //var s = this.getScrollState();
5314         var row = this.renderRow(this.cm, this.store, rowIndex);
5315         // insert before rowIndex..
5316         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
5317         
5318         var _this = this;
5319                 
5320         if(row.cellObjects.length){
5321             Roo.each(row.cellObjects, function(r){
5322                 _this.renderCellObject(r);
5323             })
5324         }
5325             
5326         if(!isUpdate){
5327             this.fireEvent("rowsinserted", this, rowIndex);
5328             //this.syncRowHeights(firstRow, lastRow);
5329             //this.stripeRows(firstRow);
5330             //this.layout();
5331         }
5332         
5333     },
5334     
5335     
5336     getRowDom : function(rowIndex)
5337     {
5338         // not sure if I need to check this.. but let's do it anyway..
5339         return (this.mainBody.dom.rows && (rowIndex-1) < this.mainBody.dom.rows.length ) ?
5340                 this.mainBody.dom.rows[rowIndex] : false
5341     },
5342     // returns the object tree for a tr..
5343   
5344     
5345     renderRow : function(cm, ds, rowIndex) {
5346         
5347         var d = ds.getAt(rowIndex);
5348         
5349         var row = {
5350             tag : 'tr',
5351             cn : []
5352         };
5353             
5354         var cellObjects = [];
5355         
5356         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5357             var config = cm.config[i];
5358             
5359             var renderer = cm.getRenderer(i);
5360             var value = '';
5361             var id = false;
5362             
5363             if(typeof(renderer) !== 'undefined'){
5364                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
5365             }
5366             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
5367             // and are rendered into the cells after the row is rendered - using the id for the element.
5368             
5369             if(typeof(value) === 'object'){
5370                 id = Roo.id();
5371                 cellObjects.push({
5372                     container : id,
5373                     cfg : value 
5374                 })
5375             }
5376             
5377             var rowcfg = {
5378                 record: d,
5379                 rowIndex : rowIndex,
5380                 colIndex : i,
5381                 rowClass : ''
5382             }
5383
5384             this.fireEvent('rowclass', this, rowcfg);
5385             
5386             var td = {
5387                 tag: 'td',
5388                 cls : rowcfg.rowClass,
5389                 style: '',
5390                 html: (typeof(value) === 'object') ? '' : value
5391             };
5392             
5393             if (id) {
5394                 td.id = id;
5395             }
5396             
5397             if(typeof(config.hidden) != 'undefined' && config.hidden){
5398                 td.style += ' display:none;';
5399             }
5400             
5401             if(typeof(config.align) != 'undefined' && config.align.length){
5402                 td.style += ' text-align:' + config.align + ';';
5403             }
5404             
5405             if(typeof(config.width) != 'undefined'){
5406                 td.style += ' width:' +  config.width + 'px;';
5407             }
5408              
5409             row.cn.push(td);
5410            
5411         }
5412         
5413         row.cellObjects = cellObjects;
5414         
5415         return row;
5416           
5417     },
5418     
5419     
5420     
5421     onBeforeLoad : function()
5422     {
5423         //Roo.log('ds onBeforeLoad');
5424         
5425         //this.clear();
5426         
5427         //if(this.loadMask){
5428         //    this.maskEl.show();
5429         //}
5430     },
5431     
5432     clear : function()
5433     {
5434         this.el.select('tbody', true).first().dom.innerHTML = '';
5435     },
5436     
5437     getSelectionModel : function(){
5438         if(!this.selModel){
5439             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
5440         }
5441         return this.selModel;
5442     },
5443     /*
5444      * Render the Roo.bootstrap object from renderder
5445      */
5446     renderCellObject : function(r)
5447     {
5448         var _this = this;
5449         
5450         var t = r.cfg.render(r.container);
5451         
5452         if(r.cfg.cn){
5453             Roo.each(r.cfg.cn, function(c){
5454                 var child = {
5455                     container: t.getChildContainer(),
5456                     cfg: c
5457                 }
5458                 _this.renderCellObject(child);
5459             })
5460         }
5461     }
5462    
5463 });
5464
5465  
5466
5467  /*
5468  * - LGPL
5469  *
5470  * table cell
5471  * 
5472  */
5473
5474 /**
5475  * @class Roo.bootstrap.TableCell
5476  * @extends Roo.bootstrap.Component
5477  * Bootstrap TableCell class
5478  * @cfg {String} html cell contain text
5479  * @cfg {String} cls cell class
5480  * @cfg {String} tag cell tag (td|th) default td
5481  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
5482  * @cfg {String} align Aligns the content in a cell
5483  * @cfg {String} axis Categorizes cells
5484  * @cfg {String} bgcolor Specifies the background color of a cell
5485  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5486  * @cfg {Number} colspan Specifies the number of columns a cell should span
5487  * @cfg {String} headers Specifies one or more header cells a cell is related to
5488  * @cfg {Number} height Sets the height of a cell
5489  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
5490  * @cfg {Number} rowspan Sets the number of rows a cell should span
5491  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
5492  * @cfg {String} valign Vertical aligns the content in a cell
5493  * @cfg {Number} width Specifies the width of a cell
5494  * 
5495  * @constructor
5496  * Create a new TableCell
5497  * @param {Object} config The config object
5498  */
5499
5500 Roo.bootstrap.TableCell = function(config){
5501     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
5502 };
5503
5504 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
5505     
5506     html: false,
5507     cls: false,
5508     tag: false,
5509     abbr: false,
5510     align: false,
5511     axis: false,
5512     bgcolor: false,
5513     charoff: false,
5514     colspan: false,
5515     headers: false,
5516     height: false,
5517     nowrap: false,
5518     rowspan: false,
5519     scope: false,
5520     valign: false,
5521     width: false,
5522     
5523     
5524     getAutoCreate : function(){
5525         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
5526         
5527         cfg = {
5528             tag: 'td'
5529         }
5530         
5531         if(this.tag){
5532             cfg.tag = this.tag;
5533         }
5534         
5535         if (this.html) {
5536             cfg.html=this.html
5537         }
5538         if (this.cls) {
5539             cfg.cls=this.cls
5540         }
5541         if (this.abbr) {
5542             cfg.abbr=this.abbr
5543         }
5544         if (this.align) {
5545             cfg.align=this.align
5546         }
5547         if (this.axis) {
5548             cfg.axis=this.axis
5549         }
5550         if (this.bgcolor) {
5551             cfg.bgcolor=this.bgcolor
5552         }
5553         if (this.charoff) {
5554             cfg.charoff=this.charoff
5555         }
5556         if (this.colspan) {
5557             cfg.colspan=this.colspan
5558         }
5559         if (this.headers) {
5560             cfg.headers=this.headers
5561         }
5562         if (this.height) {
5563             cfg.height=this.height
5564         }
5565         if (this.nowrap) {
5566             cfg.nowrap=this.nowrap
5567         }
5568         if (this.rowspan) {
5569             cfg.rowspan=this.rowspan
5570         }
5571         if (this.scope) {
5572             cfg.scope=this.scope
5573         }
5574         if (this.valign) {
5575             cfg.valign=this.valign
5576         }
5577         if (this.width) {
5578             cfg.width=this.width
5579         }
5580         
5581         
5582         return cfg;
5583     }
5584    
5585 });
5586
5587  
5588
5589  /*
5590  * - LGPL
5591  *
5592  * table row
5593  * 
5594  */
5595
5596 /**
5597  * @class Roo.bootstrap.TableRow
5598  * @extends Roo.bootstrap.Component
5599  * Bootstrap TableRow class
5600  * @cfg {String} cls row class
5601  * @cfg {String} align Aligns the content in a table row
5602  * @cfg {String} bgcolor Specifies a background color for a table row
5603  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5604  * @cfg {String} valign Vertical aligns the content in a table row
5605  * 
5606  * @constructor
5607  * Create a new TableRow
5608  * @param {Object} config The config object
5609  */
5610
5611 Roo.bootstrap.TableRow = function(config){
5612     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
5613 };
5614
5615 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
5616     
5617     cls: false,
5618     align: false,
5619     bgcolor: false,
5620     charoff: false,
5621     valign: false,
5622     
5623     getAutoCreate : function(){
5624         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
5625         
5626         cfg = {
5627             tag: 'tr'
5628         }
5629             
5630         if(this.cls){
5631             cfg.cls = this.cls;
5632         }
5633         if(this.align){
5634             cfg.align = this.align;
5635         }
5636         if(this.bgcolor){
5637             cfg.bgcolor = this.bgcolor;
5638         }
5639         if(this.charoff){
5640             cfg.charoff = this.charoff;
5641         }
5642         if(this.valign){
5643             cfg.valign = this.valign;
5644         }
5645         
5646         return cfg;
5647     }
5648    
5649 });
5650
5651  
5652
5653  /*
5654  * - LGPL
5655  *
5656  * table body
5657  * 
5658  */
5659
5660 /**
5661  * @class Roo.bootstrap.TableBody
5662  * @extends Roo.bootstrap.Component
5663  * Bootstrap TableBody class
5664  * @cfg {String} cls element class
5665  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
5666  * @cfg {String} align Aligns the content inside the element
5667  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
5668  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
5669  * 
5670  * @constructor
5671  * Create a new TableBody
5672  * @param {Object} config The config object
5673  */
5674
5675 Roo.bootstrap.TableBody = function(config){
5676     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
5677 };
5678
5679 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
5680     
5681     cls: false,
5682     tag: false,
5683     align: false,
5684     charoff: false,
5685     valign: false,
5686     
5687     getAutoCreate : function(){
5688         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
5689         
5690         cfg = {
5691             tag: 'tbody'
5692         }
5693             
5694         if (this.cls) {
5695             cfg.cls=this.cls
5696         }
5697         if(this.tag){
5698             cfg.tag = this.tag;
5699         }
5700         
5701         if(this.align){
5702             cfg.align = this.align;
5703         }
5704         if(this.charoff){
5705             cfg.charoff = this.charoff;
5706         }
5707         if(this.valign){
5708             cfg.valign = this.valign;
5709         }
5710         
5711         return cfg;
5712     }
5713     
5714     
5715 //    initEvents : function()
5716 //    {
5717 //        
5718 //        if(!this.store){
5719 //            return;
5720 //        }
5721 //        
5722 //        this.store = Roo.factory(this.store, Roo.data);
5723 //        this.store.on('load', this.onLoad, this);
5724 //        
5725 //        this.store.load();
5726 //        
5727 //    },
5728 //    
5729 //    onLoad: function () 
5730 //    {   
5731 //        this.fireEvent('load', this);
5732 //    }
5733 //    
5734 //   
5735 });
5736
5737  
5738
5739  /*
5740  * Based on:
5741  * Ext JS Library 1.1.1
5742  * Copyright(c) 2006-2007, Ext JS, LLC.
5743  *
5744  * Originally Released Under LGPL - original licence link has changed is not relivant.
5745  *
5746  * Fork - LGPL
5747  * <script type="text/javascript">
5748  */
5749
5750 // as we use this in bootstrap.
5751 Roo.namespace('Roo.form');
5752  /**
5753  * @class Roo.form.Action
5754  * Internal Class used to handle form actions
5755  * @constructor
5756  * @param {Roo.form.BasicForm} el The form element or its id
5757  * @param {Object} config Configuration options
5758  */
5759
5760  
5761  
5762 // define the action interface
5763 Roo.form.Action = function(form, options){
5764     this.form = form;
5765     this.options = options || {};
5766 };
5767 /**
5768  * Client Validation Failed
5769  * @const 
5770  */
5771 Roo.form.Action.CLIENT_INVALID = 'client';
5772 /**
5773  * Server Validation Failed
5774  * @const 
5775  */
5776 Roo.form.Action.SERVER_INVALID = 'server';
5777  /**
5778  * Connect to Server Failed
5779  * @const 
5780  */
5781 Roo.form.Action.CONNECT_FAILURE = 'connect';
5782 /**
5783  * Reading Data from Server Failed
5784  * @const 
5785  */
5786 Roo.form.Action.LOAD_FAILURE = 'load';
5787
5788 Roo.form.Action.prototype = {
5789     type : 'default',
5790     failureType : undefined,
5791     response : undefined,
5792     result : undefined,
5793
5794     // interface method
5795     run : function(options){
5796
5797     },
5798
5799     // interface method
5800     success : function(response){
5801
5802     },
5803
5804     // interface method
5805     handleResponse : function(response){
5806
5807     },
5808
5809     // default connection failure
5810     failure : function(response){
5811         
5812         this.response = response;
5813         this.failureType = Roo.form.Action.CONNECT_FAILURE;
5814         this.form.afterAction(this, false);
5815     },
5816
5817     processResponse : function(response){
5818         this.response = response;
5819         if(!response.responseText){
5820             return true;
5821         }
5822         this.result = this.handleResponse(response);
5823         return this.result;
5824     },
5825
5826     // utility functions used internally
5827     getUrl : function(appendParams){
5828         var url = this.options.url || this.form.url || this.form.el.dom.action;
5829         if(appendParams){
5830             var p = this.getParams();
5831             if(p){
5832                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
5833             }
5834         }
5835         return url;
5836     },
5837
5838     getMethod : function(){
5839         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
5840     },
5841
5842     getParams : function(){
5843         var bp = this.form.baseParams;
5844         var p = this.options.params;
5845         if(p){
5846             if(typeof p == "object"){
5847                 p = Roo.urlEncode(Roo.applyIf(p, bp));
5848             }else if(typeof p == 'string' && bp){
5849                 p += '&' + Roo.urlEncode(bp);
5850             }
5851         }else if(bp){
5852             p = Roo.urlEncode(bp);
5853         }
5854         return p;
5855     },
5856
5857     createCallback : function(){
5858         return {
5859             success: this.success,
5860             failure: this.failure,
5861             scope: this,
5862             timeout: (this.form.timeout*1000),
5863             upload: this.form.fileUpload ? this.success : undefined
5864         };
5865     }
5866 };
5867
5868 Roo.form.Action.Submit = function(form, options){
5869     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
5870 };
5871
5872 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
5873     type : 'submit',
5874
5875     haveProgress : false,
5876     uploadComplete : false,
5877     
5878     // uploadProgress indicator.
5879     uploadProgress : function()
5880     {
5881         if (!this.form.progressUrl) {
5882             return;
5883         }
5884         
5885         if (!this.haveProgress) {
5886             Roo.MessageBox.progress("Uploading", "Uploading");
5887         }
5888         if (this.uploadComplete) {
5889            Roo.MessageBox.hide();
5890            return;
5891         }
5892         
5893         this.haveProgress = true;
5894    
5895         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
5896         
5897         var c = new Roo.data.Connection();
5898         c.request({
5899             url : this.form.progressUrl,
5900             params: {
5901                 id : uid
5902             },
5903             method: 'GET',
5904             success : function(req){
5905                //console.log(data);
5906                 var rdata = false;
5907                 var edata;
5908                 try  {
5909                    rdata = Roo.decode(req.responseText)
5910                 } catch (e) {
5911                     Roo.log("Invalid data from server..");
5912                     Roo.log(edata);
5913                     return;
5914                 }
5915                 if (!rdata || !rdata.success) {
5916                     Roo.log(rdata);
5917                     Roo.MessageBox.alert(Roo.encode(rdata));
5918                     return;
5919                 }
5920                 var data = rdata.data;
5921                 
5922                 if (this.uploadComplete) {
5923                    Roo.MessageBox.hide();
5924                    return;
5925                 }
5926                    
5927                 if (data){
5928                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
5929                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
5930                     );
5931                 }
5932                 this.uploadProgress.defer(2000,this);
5933             },
5934        
5935             failure: function(data) {
5936                 Roo.log('progress url failed ');
5937                 Roo.log(data);
5938             },
5939             scope : this
5940         });
5941            
5942     },
5943     
5944     
5945     run : function()
5946     {
5947         // run get Values on the form, so it syncs any secondary forms.
5948         this.form.getValues();
5949         
5950         var o = this.options;
5951         var method = this.getMethod();
5952         var isPost = method == 'POST';
5953         if(o.clientValidation === false || this.form.isValid()){
5954             
5955             if (this.form.progressUrl) {
5956                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
5957                     (new Date() * 1) + '' + Math.random());
5958                     
5959             } 
5960             
5961             
5962             Roo.Ajax.request(Roo.apply(this.createCallback(), {
5963                 form:this.form.el.dom,
5964                 url:this.getUrl(!isPost),
5965                 method: method,
5966                 params:isPost ? this.getParams() : null,
5967                 isUpload: this.form.fileUpload
5968             }));
5969             
5970             this.uploadProgress();
5971
5972         }else if (o.clientValidation !== false){ // client validation failed
5973             this.failureType = Roo.form.Action.CLIENT_INVALID;
5974             this.form.afterAction(this, false);
5975         }
5976     },
5977
5978     success : function(response)
5979     {
5980         this.uploadComplete= true;
5981         if (this.haveProgress) {
5982             Roo.MessageBox.hide();
5983         }
5984         
5985         
5986         var result = this.processResponse(response);
5987         if(result === true || result.success){
5988             this.form.afterAction(this, true);
5989             return;
5990         }
5991         if(result.errors){
5992             this.form.markInvalid(result.errors);
5993             this.failureType = Roo.form.Action.SERVER_INVALID;
5994         }
5995         this.form.afterAction(this, false);
5996     },
5997     failure : function(response)
5998     {
5999         this.uploadComplete= true;
6000         if (this.haveProgress) {
6001             Roo.MessageBox.hide();
6002         }
6003         
6004         this.response = response;
6005         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6006         this.form.afterAction(this, false);
6007     },
6008     
6009     handleResponse : function(response){
6010         if(this.form.errorReader){
6011             var rs = this.form.errorReader.read(response);
6012             var errors = [];
6013             if(rs.records){
6014                 for(var i = 0, len = rs.records.length; i < len; i++) {
6015                     var r = rs.records[i];
6016                     errors[i] = r.data;
6017                 }
6018             }
6019             if(errors.length < 1){
6020                 errors = null;
6021             }
6022             return {
6023                 success : rs.success,
6024                 errors : errors
6025             };
6026         }
6027         var ret = false;
6028         try {
6029             ret = Roo.decode(response.responseText);
6030         } catch (e) {
6031             ret = {
6032                 success: false,
6033                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6034                 errors : []
6035             };
6036         }
6037         return ret;
6038         
6039     }
6040 });
6041
6042
6043 Roo.form.Action.Load = function(form, options){
6044     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6045     this.reader = this.form.reader;
6046 };
6047
6048 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6049     type : 'load',
6050
6051     run : function(){
6052         
6053         Roo.Ajax.request(Roo.apply(
6054                 this.createCallback(), {
6055                     method:this.getMethod(),
6056                     url:this.getUrl(false),
6057                     params:this.getParams()
6058         }));
6059     },
6060
6061     success : function(response){
6062         
6063         var result = this.processResponse(response);
6064         if(result === true || !result.success || !result.data){
6065             this.failureType = Roo.form.Action.LOAD_FAILURE;
6066             this.form.afterAction(this, false);
6067             return;
6068         }
6069         this.form.clearInvalid();
6070         this.form.setValues(result.data);
6071         this.form.afterAction(this, true);
6072     },
6073
6074     handleResponse : function(response){
6075         if(this.form.reader){
6076             var rs = this.form.reader.read(response);
6077             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
6078             return {
6079                 success : rs.success,
6080                 data : data
6081             };
6082         }
6083         return Roo.decode(response.responseText);
6084     }
6085 });
6086
6087 Roo.form.Action.ACTION_TYPES = {
6088     'load' : Roo.form.Action.Load,
6089     'submit' : Roo.form.Action.Submit
6090 };/*
6091  * - LGPL
6092  *
6093  * form
6094  * 
6095  */
6096
6097 /**
6098  * @class Roo.bootstrap.Form
6099  * @extends Roo.bootstrap.Component
6100  * Bootstrap Form class
6101  * @cfg {String} method  GET | POST (default POST)
6102  * @cfg {String} labelAlign top | left (default top)
6103   * @cfg {String} align left  | right - for navbars
6104
6105  * 
6106  * @constructor
6107  * Create a new Form
6108  * @param {Object} config The config object
6109  */
6110
6111
6112 Roo.bootstrap.Form = function(config){
6113     Roo.bootstrap.Form.superclass.constructor.call(this, config);
6114     this.addEvents({
6115         /**
6116          * @event clientvalidation
6117          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
6118          * @param {Form} this
6119          * @param {Boolean} valid true if the form has passed client-side validation
6120          */
6121         clientvalidation: true,
6122         /**
6123          * @event beforeaction
6124          * Fires before any action is performed. Return false to cancel the action.
6125          * @param {Form} this
6126          * @param {Action} action The action to be performed
6127          */
6128         beforeaction: true,
6129         /**
6130          * @event actionfailed
6131          * Fires when an action fails.
6132          * @param {Form} this
6133          * @param {Action} action The action that failed
6134          */
6135         actionfailed : true,
6136         /**
6137          * @event actioncomplete
6138          * Fires when an action is completed.
6139          * @param {Form} this
6140          * @param {Action} action The action that completed
6141          */
6142         actioncomplete : true
6143     });
6144     
6145 };
6146
6147 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
6148       
6149      /**
6150      * @cfg {String} method
6151      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
6152      */
6153     method : 'POST',
6154     /**
6155      * @cfg {String} url
6156      * The URL to use for form actions if one isn't supplied in the action options.
6157      */
6158     /**
6159      * @cfg {Boolean} fileUpload
6160      * Set to true if this form is a file upload.
6161      */
6162      
6163     /**
6164      * @cfg {Object} baseParams
6165      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
6166      */
6167       
6168     /**
6169      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
6170      */
6171     timeout: 30,
6172     /**
6173      * @cfg {Sting} align (left|right) for navbar forms
6174      */
6175     align : 'left',
6176
6177     // private
6178     activeAction : null,
6179  
6180     /**
6181      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
6182      * element by passing it or its id or mask the form itself by passing in true.
6183      * @type Mixed
6184      */
6185     waitMsgTarget : false,
6186     
6187      
6188     
6189     /**
6190      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
6191      * element by passing it or its id or mask the form itself by passing in true.
6192      * @type Mixed
6193      */
6194     
6195     getAutoCreate : function(){
6196         
6197         var cfg = {
6198             tag: 'form',
6199             method : this.method || 'POST',
6200             id : this.id || Roo.id(),
6201             cls : ''
6202         }
6203         if (this.parent().xtype.match(/^Nav/)) {
6204             cfg.cls = 'navbar-form navbar-' + this.align;
6205             
6206         }
6207         
6208         if (this.labelAlign == 'left' ) {
6209             cfg.cls += ' form-horizontal';
6210         }
6211         
6212         
6213         return cfg;
6214     },
6215     initEvents : function()
6216     {
6217         this.el.on('submit', this.onSubmit, this);
6218         // this was added as random key presses on the form where triggering form submit.
6219         this.el.on('keypress', function(e) {
6220             if (e.getCharCode() != 13) {
6221                 return true;
6222             }
6223             // we might need to allow it for textareas.. and some other items.
6224             // check e.getTarget().
6225             
6226             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
6227                 return true;
6228             }
6229         
6230             Roo.log("keypress blocked");
6231             
6232             e.preventDefault();
6233             return false;
6234         });
6235         
6236     },
6237     // private
6238     onSubmit : function(e){
6239         e.stopEvent();
6240     },
6241     
6242      /**
6243      * Returns true if client-side validation on the form is successful.
6244      * @return Boolean
6245      */
6246     isValid : function(){
6247         var items = this.getItems();
6248         var valid = true;
6249         items.each(function(f){
6250            if(!f.validate()){
6251                valid = false;
6252                
6253            }
6254         });
6255         return valid;
6256     },
6257     /**
6258      * Returns true if any fields in this form have changed since their original load.
6259      * @return Boolean
6260      */
6261     isDirty : function(){
6262         var dirty = false;
6263         var items = this.getItems();
6264         items.each(function(f){
6265            if(f.isDirty()){
6266                dirty = true;
6267                return false;
6268            }
6269            return true;
6270         });
6271         return dirty;
6272     },
6273      /**
6274      * Performs a predefined action (submit or load) or custom actions you define on this form.
6275      * @param {String} actionName The name of the action type
6276      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
6277      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
6278      * accept other config options):
6279      * <pre>
6280 Property          Type             Description
6281 ----------------  ---------------  ----------------------------------------------------------------------------------
6282 url               String           The url for the action (defaults to the form's url)
6283 method            String           The form method to use (defaults to the form's method, or POST if not defined)
6284 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
6285 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
6286                                    validate the form on the client (defaults to false)
6287      * </pre>
6288      * @return {BasicForm} this
6289      */
6290     doAction : function(action, options){
6291         if(typeof action == 'string'){
6292             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
6293         }
6294         if(this.fireEvent('beforeaction', this, action) !== false){
6295             this.beforeAction(action);
6296             action.run.defer(100, action);
6297         }
6298         return this;
6299     },
6300     
6301     // private
6302     beforeAction : function(action){
6303         var o = action.options;
6304         
6305         // not really supported yet.. ??
6306         
6307         //if(this.waitMsgTarget === true){
6308             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6309         //}else if(this.waitMsgTarget){
6310         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
6311         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
6312         //}else {
6313         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
6314        // }
6315          
6316     },
6317
6318     // private
6319     afterAction : function(action, success){
6320         this.activeAction = null;
6321         var o = action.options;
6322         
6323         //if(this.waitMsgTarget === true){
6324             this.el.unmask();
6325         //}else if(this.waitMsgTarget){
6326         //    this.waitMsgTarget.unmask();
6327         //}else{
6328         //    Roo.MessageBox.updateProgress(1);
6329         //    Roo.MessageBox.hide();
6330        // }
6331         // 
6332         if(success){
6333             if(o.reset){
6334                 this.reset();
6335             }
6336             Roo.callback(o.success, o.scope, [this, action]);
6337             this.fireEvent('actioncomplete', this, action);
6338             
6339         }else{
6340             
6341             // failure condition..
6342             // we have a scenario where updates need confirming.
6343             // eg. if a locking scenario exists..
6344             // we look for { errors : { needs_confirm : true }} in the response.
6345             if (
6346                 (typeof(action.result) != 'undefined')  &&
6347                 (typeof(action.result.errors) != 'undefined')  &&
6348                 (typeof(action.result.errors.needs_confirm) != 'undefined')
6349            ){
6350                 var _t = this;
6351                 Roo.log("not supported yet");
6352                  /*
6353                 
6354                 Roo.MessageBox.confirm(
6355                     "Change requires confirmation",
6356                     action.result.errorMsg,
6357                     function(r) {
6358                         if (r != 'yes') {
6359                             return;
6360                         }
6361                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
6362                     }
6363                     
6364                 );
6365                 */
6366                 
6367                 
6368                 return;
6369             }
6370             
6371             Roo.callback(o.failure, o.scope, [this, action]);
6372             // show an error message if no failed handler is set..
6373             if (!this.hasListener('actionfailed')) {
6374                 Roo.log("need to add dialog support");
6375                 /*
6376                 Roo.MessageBox.alert("Error",
6377                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
6378                         action.result.errorMsg :
6379                         "Saving Failed, please check your entries or try again"
6380                 );
6381                 */
6382             }
6383             
6384             this.fireEvent('actionfailed', this, action);
6385         }
6386         
6387     },
6388     /**
6389      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
6390      * @param {String} id The value to search for
6391      * @return Field
6392      */
6393     findField : function(id){
6394         var items = this.getItems();
6395         var field = items.get(id);
6396         if(!field){
6397              items.each(function(f){
6398                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
6399                     field = f;
6400                     return false;
6401                 }
6402                 return true;
6403             });
6404         }
6405         return field || null;
6406     },
6407      /**
6408      * Mark fields in this form invalid in bulk.
6409      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
6410      * @return {BasicForm} this
6411      */
6412     markInvalid : function(errors){
6413         if(errors instanceof Array){
6414             for(var i = 0, len = errors.length; i < len; i++){
6415                 var fieldError = errors[i];
6416                 var f = this.findField(fieldError.id);
6417                 if(f){
6418                     f.markInvalid(fieldError.msg);
6419                 }
6420             }
6421         }else{
6422             var field, id;
6423             for(id in errors){
6424                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
6425                     field.markInvalid(errors[id]);
6426                 }
6427             }
6428         }
6429         //Roo.each(this.childForms || [], function (f) {
6430         //    f.markInvalid(errors);
6431         //});
6432         
6433         return this;
6434     },
6435
6436     /**
6437      * Set values for fields in this form in bulk.
6438      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
6439      * @return {BasicForm} this
6440      */
6441     setValues : function(values){
6442         if(values instanceof Array){ // array of objects
6443             for(var i = 0, len = values.length; i < len; i++){
6444                 var v = values[i];
6445                 var f = this.findField(v.id);
6446                 if(f){
6447                     f.setValue(v.value);
6448                     if(this.trackResetOnLoad){
6449                         f.originalValue = f.getValue();
6450                     }
6451                 }
6452             }
6453         }else{ // object hash
6454             var field, id;
6455             for(id in values){
6456                 if(typeof values[id] != 'function' && (field = this.findField(id))){
6457                     
6458                     if (field.setFromData && 
6459                         field.valueField && 
6460                         field.displayField &&
6461                         // combos' with local stores can 
6462                         // be queried via setValue()
6463                         // to set their value..
6464                         (field.store && !field.store.isLocal)
6465                         ) {
6466                         // it's a combo
6467                         var sd = { };
6468                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
6469                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
6470                         field.setFromData(sd);
6471                         
6472                     } else {
6473                         field.setValue(values[id]);
6474                     }
6475                     
6476                     
6477                     if(this.trackResetOnLoad){
6478                         field.originalValue = field.getValue();
6479                     }
6480                 }
6481             }
6482         }
6483          
6484         //Roo.each(this.childForms || [], function (f) {
6485         //    f.setValues(values);
6486         //});
6487                 
6488         return this;
6489     },
6490
6491     /**
6492      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
6493      * they are returned as an array.
6494      * @param {Boolean} asString
6495      * @return {Object}
6496      */
6497     getValues : function(asString){
6498         //if (this.childForms) {
6499             // copy values from the child forms
6500         //    Roo.each(this.childForms, function (f) {
6501         //        this.setValues(f.getValues());
6502         //    }, this);
6503         //}
6504         
6505         
6506         
6507         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
6508         if(asString === true){
6509             return fs;
6510         }
6511         return Roo.urlDecode(fs);
6512     },
6513     
6514     /**
6515      * Returns the fields in this form as an object with key/value pairs. 
6516      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
6517      * @return {Object}
6518      */
6519     getFieldValues : function(with_hidden)
6520     {
6521         var items = this.getItems();
6522         var ret = {};
6523         items.each(function(f){
6524             if (!f.getName()) {
6525                 return;
6526             }
6527             var v = f.getValue();
6528             if (f.inputType =='radio') {
6529                 if (typeof(ret[f.getName()]) == 'undefined') {
6530                     ret[f.getName()] = ''; // empty..
6531                 }
6532                 
6533                 if (!f.el.dom.checked) {
6534                     return;
6535                     
6536                 }
6537                 v = f.el.dom.value;
6538                 
6539             }
6540             
6541             // not sure if this supported any more..
6542             if ((typeof(v) == 'object') && f.getRawValue) {
6543                 v = f.getRawValue() ; // dates..
6544             }
6545             // combo boxes where name != hiddenName...
6546             if (f.name != f.getName()) {
6547                 ret[f.name] = f.getRawValue();
6548             }
6549             ret[f.getName()] = v;
6550         });
6551         
6552         return ret;
6553     },
6554
6555     /**
6556      * Clears all invalid messages in this form.
6557      * @return {BasicForm} this
6558      */
6559     clearInvalid : function(){
6560         var items = this.getItems();
6561         
6562         items.each(function(f){
6563            f.clearInvalid();
6564         });
6565         
6566         
6567         
6568         return this;
6569     },
6570
6571     /**
6572      * Resets this form.
6573      * @return {BasicForm} this
6574      */
6575     reset : function(){
6576         var items = this.getItems();
6577         items.each(function(f){
6578             f.reset();
6579         });
6580         
6581         Roo.each(this.childForms || [], function (f) {
6582             f.reset();
6583         });
6584        
6585         
6586         return this;
6587     },
6588     getItems : function()
6589     {
6590         var r=new Roo.util.MixedCollection(false, function(o){
6591             return o.id || (o.id = Roo.id());
6592         });
6593         var iter = function(el) {
6594             if (el.inputEl) {
6595                 r.add(el);
6596             }
6597             if (!el.items) {
6598                 return;
6599             }
6600             Roo.each(el.items,function(e) {
6601                 iter(e);
6602             });
6603             
6604             
6605         };
6606         iter(this);
6607         return r;
6608         
6609         
6610         
6611         
6612     }
6613     
6614 });
6615
6616  
6617 /*
6618  * Based on:
6619  * Ext JS Library 1.1.1
6620  * Copyright(c) 2006-2007, Ext JS, LLC.
6621  *
6622  * Originally Released Under LGPL - original licence link has changed is not relivant.
6623  *
6624  * Fork - LGPL
6625  * <script type="text/javascript">
6626  */
6627 /**
6628  * @class Roo.form.VTypes
6629  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
6630  * @singleton
6631  */
6632 Roo.form.VTypes = function(){
6633     // closure these in so they are only created once.
6634     var alpha = /^[a-zA-Z_]+$/;
6635     var alphanum = /^[a-zA-Z0-9_]+$/;
6636     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
6637     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
6638
6639     // All these messages and functions are configurable
6640     return {
6641         /**
6642          * The function used to validate email addresses
6643          * @param {String} value The email address
6644          */
6645         'email' : function(v){
6646             return email.test(v);
6647         },
6648         /**
6649          * The error text to display when the email validation function returns false
6650          * @type String
6651          */
6652         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
6653         /**
6654          * The keystroke filter mask to be applied on email input
6655          * @type RegExp
6656          */
6657         'emailMask' : /[a-z0-9_\.\-@]/i,
6658
6659         /**
6660          * The function used to validate URLs
6661          * @param {String} value The URL
6662          */
6663         'url' : function(v){
6664             return url.test(v);
6665         },
6666         /**
6667          * The error text to display when the url validation function returns false
6668          * @type String
6669          */
6670         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
6671         
6672         /**
6673          * The function used to validate alpha values
6674          * @param {String} value The value
6675          */
6676         'alpha' : function(v){
6677             return alpha.test(v);
6678         },
6679         /**
6680          * The error text to display when the alpha validation function returns false
6681          * @type String
6682          */
6683         'alphaText' : 'This field should only contain letters and _',
6684         /**
6685          * The keystroke filter mask to be applied on alpha input
6686          * @type RegExp
6687          */
6688         'alphaMask' : /[a-z_]/i,
6689
6690         /**
6691          * The function used to validate alphanumeric values
6692          * @param {String} value The value
6693          */
6694         'alphanum' : function(v){
6695             return alphanum.test(v);
6696         },
6697         /**
6698          * The error text to display when the alphanumeric validation function returns false
6699          * @type String
6700          */
6701         'alphanumText' : 'This field should only contain letters, numbers and _',
6702         /**
6703          * The keystroke filter mask to be applied on alphanumeric input
6704          * @type RegExp
6705          */
6706         'alphanumMask' : /[a-z0-9_]/i
6707     };
6708 }();/*
6709  * - LGPL
6710  *
6711  * Input
6712  * 
6713  */
6714
6715 /**
6716  * @class Roo.bootstrap.Input
6717  * @extends Roo.bootstrap.Component
6718  * Bootstrap Input class
6719  * @cfg {Boolean} disabled is it disabled
6720  * @cfg {String} fieldLabel - the label associated
6721  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
6722  * @cfg {String} name name of the input
6723  * @cfg {string} fieldLabel - the label associated
6724  * @cfg {string}  inputType - input / file submit ...
6725  * @cfg {string} placeholder - placeholder to put in text.
6726  * @cfg {string}  before - input group add on before
6727  * @cfg {string} after - input group add on after
6728  * @cfg {string} size - (lg|sm) or leave empty..
6729  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
6730  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
6731  * @cfg {Number} md colspan out of 12 for computer-sized screens
6732  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
6733  * @cfg {string} value default value of the input
6734  * @cfg {Number} labelWidth set the width of label (0-12)
6735  * @cfg {String} labelAlign (top|left)
6736  * @cfg {Boolean} readOnly Specifies that the field should be read-only
6737  * @cfg {String} align (left|center|right) Default left
6738  * 
6739  * 
6740  * @constructor
6741  * Create a new Input
6742  * @param {Object} config The config object
6743  */
6744
6745 Roo.bootstrap.Input = function(config){
6746     Roo.bootstrap.Input.superclass.constructor.call(this, config);
6747    
6748         this.addEvents({
6749             /**
6750              * @event focus
6751              * Fires when this field receives input focus.
6752              * @param {Roo.form.Field} this
6753              */
6754             focus : true,
6755             /**
6756              * @event blur
6757              * Fires when this field loses input focus.
6758              * @param {Roo.form.Field} this
6759              */
6760             blur : true,
6761             /**
6762              * @event specialkey
6763              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
6764              * {@link Roo.EventObject#getKey} to determine which key was pressed.
6765              * @param {Roo.form.Field} this
6766              * @param {Roo.EventObject} e The event object
6767              */
6768             specialkey : true,
6769             /**
6770              * @event change
6771              * Fires just before the field blurs if the field value has changed.
6772              * @param {Roo.form.Field} this
6773              * @param {Mixed} newValue The new value
6774              * @param {Mixed} oldValue The original value
6775              */
6776             change : true,
6777             /**
6778              * @event invalid
6779              * Fires after the field has been marked as invalid.
6780              * @param {Roo.form.Field} this
6781              * @param {String} msg The validation message
6782              */
6783             invalid : true,
6784             /**
6785              * @event valid
6786              * Fires after the field has been validated with no errors.
6787              * @param {Roo.form.Field} this
6788              */
6789             valid : true,
6790              /**
6791              * @event keyup
6792              * Fires after the key up
6793              * @param {Roo.form.Field} this
6794              * @param {Roo.EventObject}  e The event Object
6795              */
6796             keyup : true
6797         });
6798 };
6799
6800 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
6801      /**
6802      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
6803       automatic validation (defaults to "keyup").
6804      */
6805     validationEvent : "keyup",
6806      /**
6807      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
6808      */
6809     validateOnBlur : true,
6810     /**
6811      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
6812      */
6813     validationDelay : 250,
6814      /**
6815      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
6816      */
6817     focusClass : "x-form-focus",  // not needed???
6818     
6819        
6820     /**
6821      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
6822      */
6823     invalidClass : "has-error",
6824     
6825     /**
6826      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
6827      */
6828     selectOnFocus : false,
6829     
6830      /**
6831      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
6832      */
6833     maskRe : null,
6834        /**
6835      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
6836      */
6837     vtype : null,
6838     
6839       /**
6840      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
6841      */
6842     disableKeyFilter : false,
6843     
6844        /**
6845      * @cfg {Boolean} disabled True to disable the field (defaults to false).
6846      */
6847     disabled : false,
6848      /**
6849      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
6850      */
6851     allowBlank : true,
6852     /**
6853      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
6854      */
6855     blankText : "This field is required",
6856     
6857      /**
6858      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
6859      */
6860     minLength : 0,
6861     /**
6862      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
6863      */
6864     maxLength : Number.MAX_VALUE,
6865     /**
6866      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
6867      */
6868     minLengthText : "The minimum length for this field is {0}",
6869     /**
6870      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
6871      */
6872     maxLengthText : "The maximum length for this field is {0}",
6873   
6874     
6875     /**
6876      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
6877      * If available, this function will be called only after the basic validators all return true, and will be passed the
6878      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
6879      */
6880     validator : null,
6881     /**
6882      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
6883      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
6884      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
6885      */
6886     regex : null,
6887     /**
6888      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
6889      */
6890     regexText : "",
6891     
6892     
6893     
6894     fieldLabel : '',
6895     inputType : 'text',
6896     
6897     name : false,
6898     placeholder: false,
6899     before : false,
6900     after : false,
6901     size : false,
6902     // private
6903     hasFocus : false,
6904     preventMark: false,
6905     isFormField : true,
6906     value : '',
6907     labelWidth : 2,
6908     labelAlign : false,
6909     readOnly : false,
6910     align : false,
6911     formatedValue : false,
6912     
6913     parentLabelAlign : function()
6914     {
6915         var parent = this;
6916         while (parent.parent()) {
6917             parent = parent.parent();
6918             if (typeof(parent.labelAlign) !='undefined') {
6919                 return parent.labelAlign;
6920             }
6921         }
6922         return 'left';
6923         
6924     },
6925     
6926     getAutoCreate : function(){
6927         
6928         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
6929         
6930         var id = Roo.id();
6931         
6932         var cfg = {};
6933         
6934         if(this.inputType != 'hidden'){
6935             cfg.cls = 'form-group' //input-group
6936         }
6937         
6938         var input =  {
6939             tag: 'input',
6940             id : id,
6941             type : this.inputType,
6942             value : this.value,
6943             cls : 'form-control',
6944             placeholder : this.placeholder || ''
6945             
6946         };
6947         
6948         if(this.align){
6949             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
6950         }
6951         
6952         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
6953             input.maxLength = this.maxLength;
6954         }
6955         
6956         if (this.disabled) {
6957             input.disabled=true;
6958         }
6959         
6960         if (this.readOnly) {
6961             input.readonly=true;
6962         }
6963         
6964         if (this.name) {
6965             input.name = this.name;
6966         }
6967         if (this.size) {
6968             input.cls += ' input-' + this.size;
6969         }
6970         var settings=this;
6971         ['xs','sm','md','lg'].map(function(size){
6972             if (settings[size]) {
6973                 cfg.cls += ' col-' + size + '-' + settings[size];
6974             }
6975         });
6976         
6977         var inputblock = input;
6978         
6979         if (this.before || this.after) {
6980             
6981             inputblock = {
6982                 cls : 'input-group',
6983                 cn :  [] 
6984             };
6985             if (this.before && typeof(this.before) == 'string') {
6986                 
6987                 inputblock.cn.push({
6988                     tag :'span',
6989                     cls : 'roo-input-before input-group-addon',
6990                     html : this.before
6991                 });
6992             }
6993             if (this.before && typeof(this.before) == 'object') {
6994                 this.before = Roo.factory(this.before);
6995                 Roo.log(this.before);
6996                 inputblock.cn.push({
6997                     tag :'span',
6998                     cls : 'roo-input-before input-group-' +
6999                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7000                 });
7001             }
7002             
7003             inputblock.cn.push(input);
7004             
7005             if (this.after && typeof(this.after) == 'string') {
7006                 inputblock.cn.push({
7007                     tag :'span',
7008                     cls : 'roo-input-after input-group-addon',
7009                     html : this.after
7010                 });
7011             }
7012             if (this.after && typeof(this.after) == 'object') {
7013                 this.after = Roo.factory(this.after);
7014                 Roo.log(this.after);
7015                 inputblock.cn.push({
7016                     tag :'span',
7017                     cls : 'roo-input-after input-group-' +
7018                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7019                 });
7020             }
7021         };
7022         
7023         if (align ==='left' && this.fieldLabel.length) {
7024                 Roo.log("left and has label");
7025                 cfg.cn = [
7026                     
7027                     {
7028                         tag: 'label',
7029                         'for' :  id,
7030                         cls : 'control-label col-sm-' + this.labelWidth,
7031                         html : this.fieldLabel
7032                         
7033                     },
7034                     {
7035                         cls : "col-sm-" + (12 - this.labelWidth), 
7036                         cn: [
7037                             inputblock
7038                         ]
7039                     }
7040                     
7041                 ];
7042         } else if ( this.fieldLabel.length) {
7043                 Roo.log(" label");
7044                  cfg.cn = [
7045                    
7046                     {
7047                         tag: 'label',
7048                         //cls : 'input-group-addon',
7049                         html : this.fieldLabel
7050                         
7051                     },
7052                     
7053                     inputblock
7054                     
7055                 ];
7056
7057         } else {
7058             
7059                 Roo.log(" no label && no align");
7060                 cfg.cn = [
7061                     
7062                         inputblock
7063                     
7064                 ];
7065                 
7066                 
7067         };
7068         Roo.log('input-parentType: ' + this.parentType);
7069         
7070         if (this.parentType === 'Navbar' &&  this.parent().bar) {
7071            cfg.cls += ' navbar-form';
7072            Roo.log(cfg);
7073         }
7074         
7075         return cfg;
7076         
7077     },
7078     /**
7079      * return the real input element.
7080      */
7081     inputEl: function ()
7082     {
7083         return this.el.select('input.form-control',true).first();
7084     },
7085     setDisabled : function(v)
7086     {
7087         var i  = this.inputEl().dom;
7088         if (!v) {
7089             i.removeAttribute('disabled');
7090             return;
7091             
7092         }
7093         i.setAttribute('disabled','true');
7094     },
7095     initEvents : function()
7096     {
7097         
7098         this.inputEl().on("keydown" , this.fireKey,  this);
7099         this.inputEl().on("focus", this.onFocus,  this);
7100         this.inputEl().on("blur", this.onBlur,  this);
7101         
7102         this.inputEl().relayEvent('keyup', this);
7103
7104         // reference to original value for reset
7105         this.originalValue = this.getValue();
7106         //Roo.form.TextField.superclass.initEvents.call(this);
7107         if(this.validationEvent == 'keyup'){
7108             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
7109             this.inputEl().on('keyup', this.filterValidation, this);
7110         }
7111         else if(this.validationEvent !== false){
7112             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
7113         }
7114         
7115         if(this.selectOnFocus){
7116             this.on("focus", this.preFocus, this);
7117             
7118         }
7119         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
7120             this.inputEl().on("keypress", this.filterKeys, this);
7121         }
7122        /* if(this.grow){
7123             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
7124             this.el.on("click", this.autoSize,  this);
7125         }
7126         */
7127         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
7128             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
7129         }
7130         
7131         if (typeof(this.before) == 'object') {
7132             this.before.render(this.el.select('.roo-input-before',true).first());
7133         }
7134         if (typeof(this.after) == 'object') {
7135             this.after.render(this.el.select('.roo-input-after',true).first());
7136         }
7137         
7138         
7139     },
7140     filterValidation : function(e){
7141         if(!e.isNavKeyPress()){
7142             this.validationTask.delay(this.validationDelay);
7143         }
7144     },
7145      /**
7146      * Validates the field value
7147      * @return {Boolean} True if the value is valid, else false
7148      */
7149     validate : function(){
7150         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
7151         if(this.disabled || this.validateValue(this.getRawValue())){
7152             this.clearInvalid();
7153             return true;
7154         }
7155         return false;
7156     },
7157     
7158     
7159     /**
7160      * Validates a value according to the field's validation rules and marks the field as invalid
7161      * if the validation fails
7162      * @param {Mixed} value The value to validate
7163      * @return {Boolean} True if the value is valid, else false
7164      */
7165     validateValue : function(value){
7166         if(value.length < 1)  { // if it's blank
7167              if(this.allowBlank){
7168                 this.clearInvalid();
7169                 return true;
7170              }else{
7171                 this.markInvalid(this.blankText);
7172                 return false;
7173              }
7174         }
7175         if(value.length < this.minLength){
7176             this.markInvalid(String.format(this.minLengthText, this.minLength));
7177             return false;
7178         }
7179         if(value.length > this.maxLength){
7180             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
7181             return false;
7182         }
7183         if(this.vtype){
7184             var vt = Roo.form.VTypes;
7185             if(!vt[this.vtype](value, this)){
7186                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
7187                 return false;
7188             }
7189         }
7190         if(typeof this.validator == "function"){
7191             var msg = this.validator(value);
7192             if(msg !== true){
7193                 this.markInvalid(msg);
7194                 return false;
7195             }
7196         }
7197         if(this.regex && !this.regex.test(value)){
7198             this.markInvalid(this.regexText);
7199             return false;
7200         }
7201         return true;
7202     },
7203
7204     
7205     
7206      // private
7207     fireKey : function(e){
7208         //Roo.log('field ' + e.getKey());
7209         if(e.isNavKeyPress()){
7210             this.fireEvent("specialkey", this, e);
7211         }
7212     },
7213     focus : function (selectText){
7214         if(this.rendered){
7215             this.inputEl().focus();
7216             if(selectText === true){
7217                 this.inputEl().dom.select();
7218             }
7219         }
7220         return this;
7221     } ,
7222     
7223     onFocus : function(){
7224         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7225            // this.el.addClass(this.focusClass);
7226         }
7227         if(!this.hasFocus){
7228             this.hasFocus = true;
7229             this.startValue = this.getValue();
7230             this.fireEvent("focus", this);
7231         }
7232     },
7233     
7234     beforeBlur : Roo.emptyFn,
7235
7236     
7237     // private
7238     onBlur : function(){
7239         this.beforeBlur();
7240         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7241             //this.el.removeClass(this.focusClass);
7242         }
7243         this.hasFocus = false;
7244         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
7245             this.validate();
7246         }
7247         var v = this.getValue();
7248         if(String(v) !== String(this.startValue)){
7249             this.fireEvent('change', this, v, this.startValue);
7250         }
7251         this.fireEvent("blur", this);
7252     },
7253     
7254     /**
7255      * Resets the current field value to the originally loaded value and clears any validation messages
7256      */
7257     reset : function(){
7258         this.setValue(this.originalValue);
7259         this.clearInvalid();
7260     },
7261      /**
7262      * Returns the name of the field
7263      * @return {Mixed} name The name field
7264      */
7265     getName: function(){
7266         return this.name;
7267     },
7268      /**
7269      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
7270      * @return {Mixed} value The field value
7271      */
7272     getValue : function(){
7273         
7274         var v = this.inputEl().getValue();
7275         
7276         return v;
7277     },
7278     /**
7279      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
7280      * @return {Mixed} value The field value
7281      */
7282     getRawValue : function(){
7283         var v = this.inputEl().getValue();
7284         
7285         return v;
7286     },
7287     
7288     /**
7289      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
7290      * @param {Mixed} value The value to set
7291      */
7292     setRawValue : function(v){
7293         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7294     },
7295     
7296     selectText : function(start, end){
7297         var v = this.getRawValue();
7298         if(v.length > 0){
7299             start = start === undefined ? 0 : start;
7300             end = end === undefined ? v.length : end;
7301             var d = this.inputEl().dom;
7302             if(d.setSelectionRange){
7303                 d.setSelectionRange(start, end);
7304             }else if(d.createTextRange){
7305                 var range = d.createTextRange();
7306                 range.moveStart("character", start);
7307                 range.moveEnd("character", v.length-end);
7308                 range.select();
7309             }
7310         }
7311     },
7312     
7313     /**
7314      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
7315      * @param {Mixed} value The value to set
7316      */
7317     setValue : function(v){
7318         this.value = v;
7319         if(this.rendered){
7320             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7321             this.validate();
7322         }
7323     },
7324     
7325     /*
7326     processValue : function(value){
7327         if(this.stripCharsRe){
7328             var newValue = value.replace(this.stripCharsRe, '');
7329             if(newValue !== value){
7330                 this.setRawValue(newValue);
7331                 return newValue;
7332             }
7333         }
7334         return value;
7335     },
7336   */
7337     preFocus : function(){
7338         
7339         if(this.selectOnFocus){
7340             this.inputEl().dom.select();
7341         }
7342     },
7343     filterKeys : function(e){
7344         var k = e.getKey();
7345         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
7346             return;
7347         }
7348         var c = e.getCharCode(), cc = String.fromCharCode(c);
7349         if(Roo.isIE && (e.isSpecialKey() || !cc)){
7350             return;
7351         }
7352         if(!this.maskRe.test(cc)){
7353             e.stopEvent();
7354         }
7355     },
7356      /**
7357      * Clear any invalid styles/messages for this field
7358      */
7359     clearInvalid : function(){
7360         
7361         if(!this.el || this.preventMark){ // not rendered
7362             return;
7363         }
7364         this.el.removeClass(this.invalidClass);
7365         /*
7366         switch(this.msgTarget){
7367             case 'qtip':
7368                 this.el.dom.qtip = '';
7369                 break;
7370             case 'title':
7371                 this.el.dom.title = '';
7372                 break;
7373             case 'under':
7374                 if(this.errorEl){
7375                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
7376                 }
7377                 break;
7378             case 'side':
7379                 if(this.errorIcon){
7380                     this.errorIcon.dom.qtip = '';
7381                     this.errorIcon.hide();
7382                     this.un('resize', this.alignErrorIcon, this);
7383                 }
7384                 break;
7385             default:
7386                 var t = Roo.getDom(this.msgTarget);
7387                 t.innerHTML = '';
7388                 t.style.display = 'none';
7389                 break;
7390         }
7391         */
7392         this.fireEvent('valid', this);
7393     },
7394      /**
7395      * Mark this field as invalid
7396      * @param {String} msg The validation message
7397      */
7398     markInvalid : function(msg){
7399         if(!this.el  || this.preventMark){ // not rendered
7400             return;
7401         }
7402         this.el.addClass(this.invalidClass);
7403         /*
7404         msg = msg || this.invalidText;
7405         switch(this.msgTarget){
7406             case 'qtip':
7407                 this.el.dom.qtip = msg;
7408                 this.el.dom.qclass = 'x-form-invalid-tip';
7409                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
7410                     Roo.QuickTips.enable();
7411                 }
7412                 break;
7413             case 'title':
7414                 this.el.dom.title = msg;
7415                 break;
7416             case 'under':
7417                 if(!this.errorEl){
7418                     var elp = this.el.findParent('.x-form-element', 5, true);
7419                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
7420                     this.errorEl.setWidth(elp.getWidth(true)-20);
7421                 }
7422                 this.errorEl.update(msg);
7423                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
7424                 break;
7425             case 'side':
7426                 if(!this.errorIcon){
7427                     var elp = this.el.findParent('.x-form-element', 5, true);
7428                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
7429                 }
7430                 this.alignErrorIcon();
7431                 this.errorIcon.dom.qtip = msg;
7432                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
7433                 this.errorIcon.show();
7434                 this.on('resize', this.alignErrorIcon, this);
7435                 break;
7436             default:
7437                 var t = Roo.getDom(this.msgTarget);
7438                 t.innerHTML = msg;
7439                 t.style.display = this.msgDisplay;
7440                 break;
7441         }
7442         */
7443         this.fireEvent('invalid', this, msg);
7444     },
7445     // private
7446     SafariOnKeyDown : function(event)
7447     {
7448         // this is a workaround for a password hang bug on chrome/ webkit.
7449         
7450         var isSelectAll = false;
7451         
7452         if(this.inputEl().dom.selectionEnd > 0){
7453             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
7454         }
7455         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
7456             event.preventDefault();
7457             this.setValue('');
7458             return;
7459         }
7460         
7461         if(isSelectAll){ // backspace and delete key
7462             
7463             event.preventDefault();
7464             // this is very hacky as keydown always get's upper case.
7465             //
7466             var cc = String.fromCharCode(event.getCharCode());
7467             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
7468             
7469         }
7470     },
7471     adjustWidth : function(tag, w){
7472         tag = tag.toLowerCase();
7473         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
7474             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
7475                 if(tag == 'input'){
7476                     return w + 2;
7477                 }
7478                 if(tag == 'textarea'){
7479                     return w-2;
7480                 }
7481             }else if(Roo.isOpera){
7482                 if(tag == 'input'){
7483                     return w + 2;
7484                 }
7485                 if(tag == 'textarea'){
7486                     return w-2;
7487                 }
7488             }
7489         }
7490         return w;
7491     }
7492     
7493 });
7494
7495  
7496 /*
7497  * - LGPL
7498  *
7499  * Input
7500  * 
7501  */
7502
7503 /**
7504  * @class Roo.bootstrap.TextArea
7505  * @extends Roo.bootstrap.Input
7506  * Bootstrap TextArea class
7507  * @cfg {Number} cols Specifies the visible width of a text area
7508  * @cfg {Number} rows Specifies the visible number of lines in a text area
7509  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
7510  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
7511  * @cfg {string} html text
7512  * 
7513  * @constructor
7514  * Create a new TextArea
7515  * @param {Object} config The config object
7516  */
7517
7518 Roo.bootstrap.TextArea = function(config){
7519     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
7520    
7521 };
7522
7523 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
7524      
7525     cols : false,
7526     rows : 5,
7527     readOnly : false,
7528     warp : 'soft',
7529     resize : false,
7530     value: false,
7531     html: false,
7532     
7533     getAutoCreate : function(){
7534         
7535         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7536         
7537         var id = Roo.id();
7538         
7539         var cfg = {};
7540         
7541         var input =  {
7542             tag: 'textarea',
7543             id : id,
7544             warp : this.warp,
7545             rows : this.rows,
7546             value : this.value || '',
7547             html: this.html || '',
7548             cls : 'form-control',
7549             placeholder : this.placeholder || '' 
7550             
7551         };
7552         
7553         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7554             input.maxLength = this.maxLength;
7555         }
7556         
7557         if(this.resize){
7558             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
7559         }
7560         
7561         if(this.cols){
7562             input.cols = this.cols;
7563         }
7564         
7565         if (this.readOnly) {
7566             input.readonly = true;
7567         }
7568         
7569         if (this.name) {
7570             input.name = this.name;
7571         }
7572         
7573         if (this.size) {
7574             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
7575         }
7576         
7577         var settings=this;
7578         ['xs','sm','md','lg'].map(function(size){
7579             if (settings[size]) {
7580                 cfg.cls += ' col-' + size + '-' + settings[size];
7581             }
7582         });
7583         
7584         var inputblock = input;
7585         
7586         if (this.before || this.after) {
7587             
7588             inputblock = {
7589                 cls : 'input-group',
7590                 cn :  [] 
7591             };
7592             if (this.before) {
7593                 inputblock.cn.push({
7594                     tag :'span',
7595                     cls : 'input-group-addon',
7596                     html : this.before
7597                 });
7598             }
7599             inputblock.cn.push(input);
7600             if (this.after) {
7601                 inputblock.cn.push({
7602                     tag :'span',
7603                     cls : 'input-group-addon',
7604                     html : this.after
7605                 });
7606             }
7607             
7608         }
7609         
7610         if (align ==='left' && this.fieldLabel.length) {
7611                 Roo.log("left and has label");
7612                 cfg.cn = [
7613                     
7614                     {
7615                         tag: 'label',
7616                         'for' :  id,
7617                         cls : 'control-label col-sm-' + this.labelWidth,
7618                         html : this.fieldLabel
7619                         
7620                     },
7621                     {
7622                         cls : "col-sm-" + (12 - this.labelWidth), 
7623                         cn: [
7624                             inputblock
7625                         ]
7626                     }
7627                     
7628                 ];
7629         } else if ( this.fieldLabel.length) {
7630                 Roo.log(" label");
7631                  cfg.cn = [
7632                    
7633                     {
7634                         tag: 'label',
7635                         //cls : 'input-group-addon',
7636                         html : this.fieldLabel
7637                         
7638                     },
7639                     
7640                     inputblock
7641                     
7642                 ];
7643
7644         } else {
7645             
7646                    Roo.log(" no label && no align");
7647                 cfg.cn = [
7648                     
7649                         inputblock
7650                     
7651                 ];
7652                 
7653                 
7654         }
7655         
7656         if (this.disabled) {
7657             input.disabled=true;
7658         }
7659         
7660         return cfg;
7661         
7662     },
7663     /**
7664      * return the real textarea element.
7665      */
7666     inputEl: function ()
7667     {
7668         return this.el.select('textarea.form-control',true).first();
7669     }
7670 });
7671
7672  
7673 /*
7674  * - LGPL
7675  *
7676  * trigger field - base class for combo..
7677  * 
7678  */
7679  
7680 /**
7681  * @class Roo.bootstrap.TriggerField
7682  * @extends Roo.bootstrap.Input
7683  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
7684  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
7685  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
7686  * for which you can provide a custom implementation.  For example:
7687  * <pre><code>
7688 var trigger = new Roo.bootstrap.TriggerField();
7689 trigger.onTriggerClick = myTriggerFn;
7690 trigger.applyTo('my-field');
7691 </code></pre>
7692  *
7693  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
7694  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
7695  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
7696  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
7697  * @constructor
7698  * Create a new TriggerField.
7699  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
7700  * to the base TextField)
7701  */
7702 Roo.bootstrap.TriggerField = function(config){
7703     this.mimicing = false;
7704     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
7705 };
7706
7707 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
7708     /**
7709      * @cfg {String} triggerClass A CSS class to apply to the trigger
7710      */
7711      /**
7712      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
7713      */
7714     hideTrigger:false,
7715
7716     /** @cfg {Boolean} grow @hide */
7717     /** @cfg {Number} growMin @hide */
7718     /** @cfg {Number} growMax @hide */
7719
7720     /**
7721      * @hide 
7722      * @method
7723      */
7724     autoSize: Roo.emptyFn,
7725     // private
7726     monitorTab : true,
7727     // private
7728     deferHeight : true,
7729
7730     
7731     actionMode : 'wrap',
7732     
7733     
7734     
7735     getAutoCreate : function(){
7736        
7737         var align = this.labelAlign || this.parentLabelAlign();
7738         
7739         var id = Roo.id();
7740         
7741         var cfg = {
7742             cls: 'form-group' //input-group
7743         };
7744         
7745         
7746         var input =  {
7747             tag: 'input',
7748             id : id,
7749             type : this.inputType,
7750             cls : 'form-control',
7751             autocomplete: 'off',
7752             placeholder : this.placeholder || '' 
7753             
7754         };
7755         if (this.name) {
7756             input.name = this.name;
7757         }
7758         if (this.size) {
7759             input.cls += ' input-' + this.size;
7760         }
7761         
7762         if (this.disabled) {
7763             input.disabled=true;
7764         }
7765         
7766         var inputblock = input;
7767         
7768         if (this.before || this.after) {
7769             
7770             inputblock = {
7771                 cls : 'input-group',
7772                 cn :  [] 
7773             };
7774             if (this.before) {
7775                 inputblock.cn.push({
7776                     tag :'span',
7777                     cls : 'input-group-addon',
7778                     html : this.before
7779                 });
7780             }
7781             inputblock.cn.push(input);
7782             if (this.after) {
7783                 inputblock.cn.push({
7784                     tag :'span',
7785                     cls : 'input-group-addon',
7786                     html : this.after
7787                 });
7788             }
7789             
7790         };
7791         
7792         var box = {
7793             tag: 'div',
7794             cn: [
7795                 {
7796                     tag: 'input',
7797                     type : 'hidden',
7798                     cls: 'form-hidden-field'
7799                 },
7800                 inputblock
7801             ]
7802             
7803         };
7804         
7805         if(this.multiple){
7806             Roo.log('multiple');
7807             
7808             box = {
7809                 tag: 'div',
7810                 cn: [
7811                     {
7812                         tag: 'input',
7813                         type : 'hidden',
7814                         cls: 'form-hidden-field'
7815                     },
7816                     {
7817                         tag: 'ul',
7818                         cls: 'select2-choices',
7819                         cn:[
7820                             {
7821                                 tag: 'li',
7822                                 cls: 'select2-search-field',
7823                                 cn: [
7824
7825                                     inputblock
7826                                 ]
7827                             }
7828                         ]
7829                     }
7830                 ]
7831             }
7832         };
7833         
7834         var combobox = {
7835             cls: 'select2-container input-group',
7836             cn: [
7837                 box
7838 //                {
7839 //                    tag: 'ul',
7840 //                    cls: 'typeahead typeahead-long dropdown-menu',
7841 //                    style: 'display:none'
7842 //                }
7843             ]
7844         };
7845         
7846         if(!this.multiple && this.showToggleBtn){
7847             combobox.cn.push({
7848                 tag :'span',
7849                 cls : 'input-group-addon btn dropdown-toggle',
7850                 cn : [
7851                     {
7852                         tag: 'span',
7853                         cls: 'caret'
7854                     },
7855                     {
7856                         tag: 'span',
7857                         cls: 'combobox-clear',
7858                         cn  : [
7859                             {
7860                                 tag : 'i',
7861                                 cls: 'icon-remove'
7862                             }
7863                         ]
7864                     }
7865                 ]
7866
7867             })
7868         }
7869         
7870         if(this.multiple){
7871             combobox.cls += ' select2-container-multi';
7872         }
7873         
7874         if (align ==='left' && this.fieldLabel.length) {
7875             
7876                 Roo.log("left and has label");
7877                 cfg.cn = [
7878                     
7879                     {
7880                         tag: 'label',
7881                         'for' :  id,
7882                         cls : 'control-label col-sm-' + this.labelWidth,
7883                         html : this.fieldLabel
7884                         
7885                     },
7886                     {
7887                         cls : "col-sm-" + (12 - this.labelWidth), 
7888                         cn: [
7889                             combobox
7890                         ]
7891                     }
7892                     
7893                 ];
7894         } else if ( this.fieldLabel.length) {
7895                 Roo.log(" label");
7896                  cfg.cn = [
7897                    
7898                     {
7899                         tag: 'label',
7900                         //cls : 'input-group-addon',
7901                         html : this.fieldLabel
7902                         
7903                     },
7904                     
7905                     combobox
7906                     
7907                 ];
7908
7909         } else {
7910             
7911                 Roo.log(" no label && no align");
7912                 cfg = combobox
7913                      
7914                 
7915         }
7916          
7917         var settings=this;
7918         ['xs','sm','md','lg'].map(function(size){
7919             if (settings[size]) {
7920                 cfg.cls += ' col-' + size + '-' + settings[size];
7921             }
7922         });
7923         
7924         return cfg;
7925         
7926     },
7927     
7928     
7929     
7930     // private
7931     onResize : function(w, h){
7932 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
7933 //        if(typeof w == 'number'){
7934 //            var x = w - this.trigger.getWidth();
7935 //            this.inputEl().setWidth(this.adjustWidth('input', x));
7936 //            this.trigger.setStyle('left', x+'px');
7937 //        }
7938     },
7939
7940     // private
7941     adjustSize : Roo.BoxComponent.prototype.adjustSize,
7942
7943     // private
7944     getResizeEl : function(){
7945         return this.inputEl();
7946     },
7947
7948     // private
7949     getPositionEl : function(){
7950         return this.inputEl();
7951     },
7952
7953     // private
7954     alignErrorIcon : function(){
7955         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
7956     },
7957
7958     // private
7959     initEvents : function(){
7960         
7961         this.createList();
7962         
7963         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
7964         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
7965         if(!this.multiple && this.showToggleBtn){
7966             this.trigger = this.el.select('span.dropdown-toggle',true).first();
7967             if(this.hideTrigger){
7968                 this.trigger.setDisplayed(false);
7969             }
7970             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
7971         }
7972         
7973         if(this.multiple){
7974             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
7975         }
7976         
7977         //this.trigger.addClassOnOver('x-form-trigger-over');
7978         //this.trigger.addClassOnClick('x-form-trigger-click');
7979         
7980         //if(!this.width){
7981         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
7982         //}
7983     },
7984     
7985     createList : function()
7986     {
7987         this.list = Roo.get(document.body).createChild({
7988             tag: 'ul',
7989             cls: 'typeahead typeahead-long dropdown-menu',
7990             style: 'display:none'
7991         });
7992         
7993         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
7994         
7995     },
7996
7997     // private
7998     initTrigger : function(){
7999        
8000     },
8001
8002     // private
8003     onDestroy : function(){
8004         if(this.trigger){
8005             this.trigger.removeAllListeners();
8006           //  this.trigger.remove();
8007         }
8008         //if(this.wrap){
8009         //    this.wrap.remove();
8010         //}
8011         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
8012     },
8013
8014     // private
8015     onFocus : function(){
8016         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
8017         /*
8018         if(!this.mimicing){
8019             this.wrap.addClass('x-trigger-wrap-focus');
8020             this.mimicing = true;
8021             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
8022             if(this.monitorTab){
8023                 this.el.on("keydown", this.checkTab, this);
8024             }
8025         }
8026         */
8027     },
8028
8029     // private
8030     checkTab : function(e){
8031         if(e.getKey() == e.TAB){
8032             this.triggerBlur();
8033         }
8034     },
8035
8036     // private
8037     onBlur : function(){
8038         // do nothing
8039     },
8040
8041     // private
8042     mimicBlur : function(e, t){
8043         /*
8044         if(!this.wrap.contains(t) && this.validateBlur()){
8045             this.triggerBlur();
8046         }
8047         */
8048     },
8049
8050     // private
8051     triggerBlur : function(){
8052         this.mimicing = false;
8053         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
8054         if(this.monitorTab){
8055             this.el.un("keydown", this.checkTab, this);
8056         }
8057         //this.wrap.removeClass('x-trigger-wrap-focus');
8058         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
8059     },
8060
8061     // private
8062     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
8063     validateBlur : function(e, t){
8064         return true;
8065     },
8066
8067     // private
8068     onDisable : function(){
8069         this.inputEl().dom.disabled = true;
8070         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
8071         //if(this.wrap){
8072         //    this.wrap.addClass('x-item-disabled');
8073         //}
8074     },
8075
8076     // private
8077     onEnable : function(){
8078         this.inputEl().dom.disabled = false;
8079         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
8080         //if(this.wrap){
8081         //    this.el.removeClass('x-item-disabled');
8082         //}
8083     },
8084
8085     // private
8086     onShow : function(){
8087         var ae = this.getActionEl();
8088         
8089         if(ae){
8090             ae.dom.style.display = '';
8091             ae.dom.style.visibility = 'visible';
8092         }
8093     },
8094
8095     // private
8096     
8097     onHide : function(){
8098         var ae = this.getActionEl();
8099         ae.dom.style.display = 'none';
8100     },
8101
8102     /**
8103      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
8104      * by an implementing function.
8105      * @method
8106      * @param {EventObject} e
8107      */
8108     onTriggerClick : Roo.emptyFn
8109 });
8110  /*
8111  * Based on:
8112  * Ext JS Library 1.1.1
8113  * Copyright(c) 2006-2007, Ext JS, LLC.
8114  *
8115  * Originally Released Under LGPL - original licence link has changed is not relivant.
8116  *
8117  * Fork - LGPL
8118  * <script type="text/javascript">
8119  */
8120
8121
8122 /**
8123  * @class Roo.data.SortTypes
8124  * @singleton
8125  * Defines the default sorting (casting?) comparison functions used when sorting data.
8126  */
8127 Roo.data.SortTypes = {
8128     /**
8129      * Default sort that does nothing
8130      * @param {Mixed} s The value being converted
8131      * @return {Mixed} The comparison value
8132      */
8133     none : function(s){
8134         return s;
8135     },
8136     
8137     /**
8138      * The regular expression used to strip tags
8139      * @type {RegExp}
8140      * @property
8141      */
8142     stripTagsRE : /<\/?[^>]+>/gi,
8143     
8144     /**
8145      * Strips all HTML tags to sort on text only
8146      * @param {Mixed} s The value being converted
8147      * @return {String} The comparison value
8148      */
8149     asText : function(s){
8150         return String(s).replace(this.stripTagsRE, "");
8151     },
8152     
8153     /**
8154      * Strips all HTML tags to sort on text only - Case insensitive
8155      * @param {Mixed} s The value being converted
8156      * @return {String} The comparison value
8157      */
8158     asUCText : function(s){
8159         return String(s).toUpperCase().replace(this.stripTagsRE, "");
8160     },
8161     
8162     /**
8163      * Case insensitive string
8164      * @param {Mixed} s The value being converted
8165      * @return {String} The comparison value
8166      */
8167     asUCString : function(s) {
8168         return String(s).toUpperCase();
8169     },
8170     
8171     /**
8172      * Date sorting
8173      * @param {Mixed} s The value being converted
8174      * @return {Number} The comparison value
8175      */
8176     asDate : function(s) {
8177         if(!s){
8178             return 0;
8179         }
8180         if(s instanceof Date){
8181             return s.getTime();
8182         }
8183         return Date.parse(String(s));
8184     },
8185     
8186     /**
8187      * Float sorting
8188      * @param {Mixed} s The value being converted
8189      * @return {Float} The comparison value
8190      */
8191     asFloat : function(s) {
8192         var val = parseFloat(String(s).replace(/,/g, ""));
8193         if(isNaN(val)) val = 0;
8194         return val;
8195     },
8196     
8197     /**
8198      * Integer sorting
8199      * @param {Mixed} s The value being converted
8200      * @return {Number} The comparison value
8201      */
8202     asInt : function(s) {
8203         var val = parseInt(String(s).replace(/,/g, ""));
8204         if(isNaN(val)) val = 0;
8205         return val;
8206     }
8207 };/*
8208  * Based on:
8209  * Ext JS Library 1.1.1
8210  * Copyright(c) 2006-2007, Ext JS, LLC.
8211  *
8212  * Originally Released Under LGPL - original licence link has changed is not relivant.
8213  *
8214  * Fork - LGPL
8215  * <script type="text/javascript">
8216  */
8217
8218 /**
8219 * @class Roo.data.Record
8220  * Instances of this class encapsulate both record <em>definition</em> information, and record
8221  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
8222  * to access Records cached in an {@link Roo.data.Store} object.<br>
8223  * <p>
8224  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
8225  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
8226  * objects.<br>
8227  * <p>
8228  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
8229  * @constructor
8230  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
8231  * {@link #create}. The parameters are the same.
8232  * @param {Array} data An associative Array of data values keyed by the field name.
8233  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
8234  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
8235  * not specified an integer id is generated.
8236  */
8237 Roo.data.Record = function(data, id){
8238     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
8239     this.data = data;
8240 };
8241
8242 /**
8243  * Generate a constructor for a specific record layout.
8244  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
8245  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
8246  * Each field definition object may contain the following properties: <ul>
8247  * <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,
8248  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
8249  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
8250  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
8251  * is being used, then this is a string containing the javascript expression to reference the data relative to 
8252  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
8253  * to the data item relative to the record element. If the mapping expression is the same as the field name,
8254  * this may be omitted.</p></li>
8255  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
8256  * <ul><li>auto (Default, implies no conversion)</li>
8257  * <li>string</li>
8258  * <li>int</li>
8259  * <li>float</li>
8260  * <li>boolean</li>
8261  * <li>date</li></ul></p></li>
8262  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
8263  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
8264  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
8265  * by the Reader into an object that will be stored in the Record. It is passed the
8266  * following parameters:<ul>
8267  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
8268  * </ul></p></li>
8269  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
8270  * </ul>
8271  * <br>usage:<br><pre><code>
8272 var TopicRecord = Roo.data.Record.create(
8273     {name: 'title', mapping: 'topic_title'},
8274     {name: 'author', mapping: 'username'},
8275     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
8276     {name: 'lastPost', mapping: 'post_time', type: 'date'},
8277     {name: 'lastPoster', mapping: 'user2'},
8278     {name: 'excerpt', mapping: 'post_text'}
8279 );
8280
8281 var myNewRecord = new TopicRecord({
8282     title: 'Do my job please',
8283     author: 'noobie',
8284     totalPosts: 1,
8285     lastPost: new Date(),
8286     lastPoster: 'Animal',
8287     excerpt: 'No way dude!'
8288 });
8289 myStore.add(myNewRecord);
8290 </code></pre>
8291  * @method create
8292  * @static
8293  */
8294 Roo.data.Record.create = function(o){
8295     var f = function(){
8296         f.superclass.constructor.apply(this, arguments);
8297     };
8298     Roo.extend(f, Roo.data.Record);
8299     var p = f.prototype;
8300     p.fields = new Roo.util.MixedCollection(false, function(field){
8301         return field.name;
8302     });
8303     for(var i = 0, len = o.length; i < len; i++){
8304         p.fields.add(new Roo.data.Field(o[i]));
8305     }
8306     f.getField = function(name){
8307         return p.fields.get(name);  
8308     };
8309     return f;
8310 };
8311
8312 Roo.data.Record.AUTO_ID = 1000;
8313 Roo.data.Record.EDIT = 'edit';
8314 Roo.data.Record.REJECT = 'reject';
8315 Roo.data.Record.COMMIT = 'commit';
8316
8317 Roo.data.Record.prototype = {
8318     /**
8319      * Readonly flag - true if this record has been modified.
8320      * @type Boolean
8321      */
8322     dirty : false,
8323     editing : false,
8324     error: null,
8325     modified: null,
8326
8327     // private
8328     join : function(store){
8329         this.store = store;
8330     },
8331
8332     /**
8333      * Set the named field to the specified value.
8334      * @param {String} name The name of the field to set.
8335      * @param {Object} value The value to set the field to.
8336      */
8337     set : function(name, value){
8338         if(this.data[name] == value){
8339             return;
8340         }
8341         this.dirty = true;
8342         if(!this.modified){
8343             this.modified = {};
8344         }
8345         if(typeof this.modified[name] == 'undefined'){
8346             this.modified[name] = this.data[name];
8347         }
8348         this.data[name] = value;
8349         if(!this.editing && this.store){
8350             this.store.afterEdit(this);
8351         }       
8352     },
8353
8354     /**
8355      * Get the value of the named field.
8356      * @param {String} name The name of the field to get the value of.
8357      * @return {Object} The value of the field.
8358      */
8359     get : function(name){
8360         return this.data[name]; 
8361     },
8362
8363     // private
8364     beginEdit : function(){
8365         this.editing = true;
8366         this.modified = {}; 
8367     },
8368
8369     // private
8370     cancelEdit : function(){
8371         this.editing = false;
8372         delete this.modified;
8373     },
8374
8375     // private
8376     endEdit : function(){
8377         this.editing = false;
8378         if(this.dirty && this.store){
8379             this.store.afterEdit(this);
8380         }
8381     },
8382
8383     /**
8384      * Usually called by the {@link Roo.data.Store} which owns the Record.
8385      * Rejects all changes made to the Record since either creation, or the last commit operation.
8386      * Modified fields are reverted to their original values.
8387      * <p>
8388      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8389      * of reject operations.
8390      */
8391     reject : function(){
8392         var m = this.modified;
8393         for(var n in m){
8394             if(typeof m[n] != "function"){
8395                 this.data[n] = m[n];
8396             }
8397         }
8398         this.dirty = false;
8399         delete this.modified;
8400         this.editing = false;
8401         if(this.store){
8402             this.store.afterReject(this);
8403         }
8404     },
8405
8406     /**
8407      * Usually called by the {@link Roo.data.Store} which owns the Record.
8408      * Commits all changes made to the Record since either creation, or the last commit operation.
8409      * <p>
8410      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8411      * of commit operations.
8412      */
8413     commit : function(){
8414         this.dirty = false;
8415         delete this.modified;
8416         this.editing = false;
8417         if(this.store){
8418             this.store.afterCommit(this);
8419         }
8420     },
8421
8422     // private
8423     hasError : function(){
8424         return this.error != null;
8425     },
8426
8427     // private
8428     clearError : function(){
8429         this.error = null;
8430     },
8431
8432     /**
8433      * Creates a copy of this record.
8434      * @param {String} id (optional) A new record id if you don't want to use this record's id
8435      * @return {Record}
8436      */
8437     copy : function(newId) {
8438         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
8439     }
8440 };/*
8441  * Based on:
8442  * Ext JS Library 1.1.1
8443  * Copyright(c) 2006-2007, Ext JS, LLC.
8444  *
8445  * Originally Released Under LGPL - original licence link has changed is not relivant.
8446  *
8447  * Fork - LGPL
8448  * <script type="text/javascript">
8449  */
8450
8451
8452
8453 /**
8454  * @class Roo.data.Store
8455  * @extends Roo.util.Observable
8456  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
8457  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
8458  * <p>
8459  * 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
8460  * has no knowledge of the format of the data returned by the Proxy.<br>
8461  * <p>
8462  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
8463  * instances from the data object. These records are cached and made available through accessor functions.
8464  * @constructor
8465  * Creates a new Store.
8466  * @param {Object} config A config object containing the objects needed for the Store to access data,
8467  * and read the data into Records.
8468  */
8469 Roo.data.Store = function(config){
8470     this.data = new Roo.util.MixedCollection(false);
8471     this.data.getKey = function(o){
8472         return o.id;
8473     };
8474     this.baseParams = {};
8475     // private
8476     this.paramNames = {
8477         "start" : "start",
8478         "limit" : "limit",
8479         "sort" : "sort",
8480         "dir" : "dir",
8481         "multisort" : "_multisort"
8482     };
8483
8484     if(config && config.data){
8485         this.inlineData = config.data;
8486         delete config.data;
8487     }
8488
8489     Roo.apply(this, config);
8490     
8491     if(this.reader){ // reader passed
8492         this.reader = Roo.factory(this.reader, Roo.data);
8493         this.reader.xmodule = this.xmodule || false;
8494         if(!this.recordType){
8495             this.recordType = this.reader.recordType;
8496         }
8497         if(this.reader.onMetaChange){
8498             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
8499         }
8500     }
8501
8502     if(this.recordType){
8503         this.fields = this.recordType.prototype.fields;
8504     }
8505     this.modified = [];
8506
8507     this.addEvents({
8508         /**
8509          * @event datachanged
8510          * Fires when the data cache has changed, and a widget which is using this Store
8511          * as a Record cache should refresh its view.
8512          * @param {Store} this
8513          */
8514         datachanged : true,
8515         /**
8516          * @event metachange
8517          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
8518          * @param {Store} this
8519          * @param {Object} meta The JSON metadata
8520          */
8521         metachange : true,
8522         /**
8523          * @event add
8524          * Fires when Records have been added to the Store
8525          * @param {Store} this
8526          * @param {Roo.data.Record[]} records The array of Records added
8527          * @param {Number} index The index at which the record(s) were added
8528          */
8529         add : true,
8530         /**
8531          * @event remove
8532          * Fires when a Record has been removed from the Store
8533          * @param {Store} this
8534          * @param {Roo.data.Record} record The Record that was removed
8535          * @param {Number} index The index at which the record was removed
8536          */
8537         remove : true,
8538         /**
8539          * @event update
8540          * Fires when a Record has been updated
8541          * @param {Store} this
8542          * @param {Roo.data.Record} record The Record that was updated
8543          * @param {String} operation The update operation being performed.  Value may be one of:
8544          * <pre><code>
8545  Roo.data.Record.EDIT
8546  Roo.data.Record.REJECT
8547  Roo.data.Record.COMMIT
8548          * </code></pre>
8549          */
8550         update : true,
8551         /**
8552          * @event clear
8553          * Fires when the data cache has been cleared.
8554          * @param {Store} this
8555          */
8556         clear : true,
8557         /**
8558          * @event beforeload
8559          * Fires before a request is made for a new data object.  If the beforeload handler returns false
8560          * the load action will be canceled.
8561          * @param {Store} this
8562          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8563          */
8564         beforeload : true,
8565         /**
8566          * @event beforeloadadd
8567          * Fires after a new set of Records has been loaded.
8568          * @param {Store} this
8569          * @param {Roo.data.Record[]} records The Records that were loaded
8570          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8571          */
8572         beforeloadadd : true,
8573         /**
8574          * @event load
8575          * Fires after a new set of Records has been loaded, before they are added to the store.
8576          * @param {Store} this
8577          * @param {Roo.data.Record[]} records The Records that were loaded
8578          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8579          * @params {Object} return from reader
8580          */
8581         load : true,
8582         /**
8583          * @event loadexception
8584          * Fires if an exception occurs in the Proxy during loading.
8585          * Called with the signature of the Proxy's "loadexception" event.
8586          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
8587          * 
8588          * @param {Proxy} 
8589          * @param {Object} return from JsonData.reader() - success, totalRecords, records
8590          * @param {Object} load options 
8591          * @param {Object} jsonData from your request (normally this contains the Exception)
8592          */
8593         loadexception : true
8594     });
8595     
8596     if(this.proxy){
8597         this.proxy = Roo.factory(this.proxy, Roo.data);
8598         this.proxy.xmodule = this.xmodule || false;
8599         this.relayEvents(this.proxy,  ["loadexception"]);
8600     }
8601     this.sortToggle = {};
8602     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
8603
8604     Roo.data.Store.superclass.constructor.call(this);
8605
8606     if(this.inlineData){
8607         this.loadData(this.inlineData);
8608         delete this.inlineData;
8609     }
8610 };
8611
8612 Roo.extend(Roo.data.Store, Roo.util.Observable, {
8613      /**
8614     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
8615     * without a remote query - used by combo/forms at present.
8616     */
8617     
8618     /**
8619     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
8620     */
8621     /**
8622     * @cfg {Array} data Inline data to be loaded when the store is initialized.
8623     */
8624     /**
8625     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
8626     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
8627     */
8628     /**
8629     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
8630     * on any HTTP request
8631     */
8632     /**
8633     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
8634     */
8635     /**
8636     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
8637     */
8638     multiSort: false,
8639     /**
8640     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
8641     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
8642     */
8643     remoteSort : false,
8644
8645     /**
8646     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
8647      * loaded or when a record is removed. (defaults to false).
8648     */
8649     pruneModifiedRecords : false,
8650
8651     // private
8652     lastOptions : null,
8653
8654     /**
8655      * Add Records to the Store and fires the add event.
8656      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8657      */
8658     add : function(records){
8659         records = [].concat(records);
8660         for(var i = 0, len = records.length; i < len; i++){
8661             records[i].join(this);
8662         }
8663         var index = this.data.length;
8664         this.data.addAll(records);
8665         this.fireEvent("add", this, records, index);
8666     },
8667
8668     /**
8669      * Remove a Record from the Store and fires the remove event.
8670      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
8671      */
8672     remove : function(record){
8673         var index = this.data.indexOf(record);
8674         this.data.removeAt(index);
8675         if(this.pruneModifiedRecords){
8676             this.modified.remove(record);
8677         }
8678         this.fireEvent("remove", this, record, index);
8679     },
8680
8681     /**
8682      * Remove all Records from the Store and fires the clear event.
8683      */
8684     removeAll : function(){
8685         this.data.clear();
8686         if(this.pruneModifiedRecords){
8687             this.modified = [];
8688         }
8689         this.fireEvent("clear", this);
8690     },
8691
8692     /**
8693      * Inserts Records to the Store at the given index and fires the add event.
8694      * @param {Number} index The start index at which to insert the passed Records.
8695      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8696      */
8697     insert : function(index, records){
8698         records = [].concat(records);
8699         for(var i = 0, len = records.length; i < len; i++){
8700             this.data.insert(index, records[i]);
8701             records[i].join(this);
8702         }
8703         this.fireEvent("add", this, records, index);
8704     },
8705
8706     /**
8707      * Get the index within the cache of the passed Record.
8708      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
8709      * @return {Number} The index of the passed Record. Returns -1 if not found.
8710      */
8711     indexOf : function(record){
8712         return this.data.indexOf(record);
8713     },
8714
8715     /**
8716      * Get the index within the cache of the Record with the passed id.
8717      * @param {String} id The id of the Record to find.
8718      * @return {Number} The index of the Record. Returns -1 if not found.
8719      */
8720     indexOfId : function(id){
8721         return this.data.indexOfKey(id);
8722     },
8723
8724     /**
8725      * Get the Record with the specified id.
8726      * @param {String} id The id of the Record to find.
8727      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
8728      */
8729     getById : function(id){
8730         return this.data.key(id);
8731     },
8732
8733     /**
8734      * Get the Record at the specified index.
8735      * @param {Number} index The index of the Record to find.
8736      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
8737      */
8738     getAt : function(index){
8739         return this.data.itemAt(index);
8740     },
8741
8742     /**
8743      * Returns a range of Records between specified indices.
8744      * @param {Number} startIndex (optional) The starting index (defaults to 0)
8745      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
8746      * @return {Roo.data.Record[]} An array of Records
8747      */
8748     getRange : function(start, end){
8749         return this.data.getRange(start, end);
8750     },
8751
8752     // private
8753     storeOptions : function(o){
8754         o = Roo.apply({}, o);
8755         delete o.callback;
8756         delete o.scope;
8757         this.lastOptions = o;
8758     },
8759
8760     /**
8761      * Loads the Record cache from the configured Proxy using the configured Reader.
8762      * <p>
8763      * If using remote paging, then the first load call must specify the <em>start</em>
8764      * and <em>limit</em> properties in the options.params property to establish the initial
8765      * position within the dataset, and the number of Records to cache on each read from the Proxy.
8766      * <p>
8767      * <strong>It is important to note that for remote data sources, loading is asynchronous,
8768      * and this call will return before the new data has been loaded. Perform any post-processing
8769      * in a callback function, or in a "load" event handler.</strong>
8770      * <p>
8771      * @param {Object} options An object containing properties which control loading options:<ul>
8772      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
8773      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
8774      * passed the following arguments:<ul>
8775      * <li>r : Roo.data.Record[]</li>
8776      * <li>options: Options object from the load call</li>
8777      * <li>success: Boolean success indicator</li></ul></li>
8778      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
8779      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
8780      * </ul>
8781      */
8782     load : function(options){
8783         options = options || {};
8784         if(this.fireEvent("beforeload", this, options) !== false){
8785             this.storeOptions(options);
8786             var p = Roo.apply(options.params || {}, this.baseParams);
8787             // if meta was not loaded from remote source.. try requesting it.
8788             if (!this.reader.metaFromRemote) {
8789                 p._requestMeta = 1;
8790             }
8791             if(this.sortInfo && this.remoteSort){
8792                 var pn = this.paramNames;
8793                 p[pn["sort"]] = this.sortInfo.field;
8794                 p[pn["dir"]] = this.sortInfo.direction;
8795             }
8796             if (this.multiSort) {
8797                 var pn = this.paramNames;
8798                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
8799             }
8800             
8801             this.proxy.load(p, this.reader, this.loadRecords, this, options);
8802         }
8803     },
8804
8805     /**
8806      * Reloads the Record cache from the configured Proxy using the configured Reader and
8807      * the options from the last load operation performed.
8808      * @param {Object} options (optional) An object containing properties which may override the options
8809      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
8810      * the most recently used options are reused).
8811      */
8812     reload : function(options){
8813         this.load(Roo.applyIf(options||{}, this.lastOptions));
8814     },
8815
8816     // private
8817     // Called as a callback by the Reader during a load operation.
8818     loadRecords : function(o, options, success){
8819         if(!o || success === false){
8820             if(success !== false){
8821                 this.fireEvent("load", this, [], options, o);
8822             }
8823             if(options.callback){
8824                 options.callback.call(options.scope || this, [], options, false);
8825             }
8826             return;
8827         }
8828         // if data returned failure - throw an exception.
8829         if (o.success === false) {
8830             // show a message if no listener is registered.
8831             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
8832                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
8833             }
8834             // loadmask wil be hooked into this..
8835             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
8836             return;
8837         }
8838         var r = o.records, t = o.totalRecords || r.length;
8839         
8840         this.fireEvent("beforeloadadd", this, r, options, o);
8841         
8842         if(!options || options.add !== true){
8843             if(this.pruneModifiedRecords){
8844                 this.modified = [];
8845             }
8846             for(var i = 0, len = r.length; i < len; i++){
8847                 r[i].join(this);
8848             }
8849             if(this.snapshot){
8850                 this.data = this.snapshot;
8851                 delete this.snapshot;
8852             }
8853             this.data.clear();
8854             this.data.addAll(r);
8855             this.totalLength = t;
8856             this.applySort();
8857             this.fireEvent("datachanged", this);
8858         }else{
8859             this.totalLength = Math.max(t, this.data.length+r.length);
8860             this.add(r);
8861         }
8862         this.fireEvent("load", this, r, options, o);
8863         if(options.callback){
8864             options.callback.call(options.scope || this, r, options, true);
8865         }
8866     },
8867
8868
8869     /**
8870      * Loads data from a passed data block. A Reader which understands the format of the data
8871      * must have been configured in the constructor.
8872      * @param {Object} data The data block from which to read the Records.  The format of the data expected
8873      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
8874      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
8875      */
8876     loadData : function(o, append){
8877         var r = this.reader.readRecords(o);
8878         this.loadRecords(r, {add: append}, true);
8879     },
8880
8881     /**
8882      * Gets the number of cached records.
8883      * <p>
8884      * <em>If using paging, this may not be the total size of the dataset. If the data object
8885      * used by the Reader contains the dataset size, then the getTotalCount() function returns
8886      * the data set size</em>
8887      */
8888     getCount : function(){
8889         return this.data.length || 0;
8890     },
8891
8892     /**
8893      * Gets the total number of records in the dataset as returned by the server.
8894      * <p>
8895      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
8896      * the dataset size</em>
8897      */
8898     getTotalCount : function(){
8899         return this.totalLength || 0;
8900     },
8901
8902     /**
8903      * Returns the sort state of the Store as an object with two properties:
8904      * <pre><code>
8905  field {String} The name of the field by which the Records are sorted
8906  direction {String} The sort order, "ASC" or "DESC"
8907      * </code></pre>
8908      */
8909     getSortState : function(){
8910         return this.sortInfo;
8911     },
8912
8913     // private
8914     applySort : function(){
8915         if(this.sortInfo && !this.remoteSort){
8916             var s = this.sortInfo, f = s.field;
8917             var st = this.fields.get(f).sortType;
8918             var fn = function(r1, r2){
8919                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
8920                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
8921             };
8922             this.data.sort(s.direction, fn);
8923             if(this.snapshot && this.snapshot != this.data){
8924                 this.snapshot.sort(s.direction, fn);
8925             }
8926         }
8927     },
8928
8929     /**
8930      * Sets the default sort column and order to be used by the next load operation.
8931      * @param {String} fieldName The name of the field to sort by.
8932      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
8933      */
8934     setDefaultSort : function(field, dir){
8935         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
8936     },
8937
8938     /**
8939      * Sort the Records.
8940      * If remote sorting is used, the sort is performed on the server, and the cache is
8941      * reloaded. If local sorting is used, the cache is sorted internally.
8942      * @param {String} fieldName The name of the field to sort by.
8943      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
8944      */
8945     sort : function(fieldName, dir){
8946         var f = this.fields.get(fieldName);
8947         if(!dir){
8948             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
8949             
8950             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
8951                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
8952             }else{
8953                 dir = f.sortDir;
8954             }
8955         }
8956         this.sortToggle[f.name] = dir;
8957         this.sortInfo = {field: f.name, direction: dir};
8958         if(!this.remoteSort){
8959             this.applySort();
8960             this.fireEvent("datachanged", this);
8961         }else{
8962             this.load(this.lastOptions);
8963         }
8964     },
8965
8966     /**
8967      * Calls the specified function for each of the Records in the cache.
8968      * @param {Function} fn The function to call. The Record is passed as the first parameter.
8969      * Returning <em>false</em> aborts and exits the iteration.
8970      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
8971      */
8972     each : function(fn, scope){
8973         this.data.each(fn, scope);
8974     },
8975
8976     /**
8977      * Gets all records modified since the last commit.  Modified records are persisted across load operations
8978      * (e.g., during paging).
8979      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
8980      */
8981     getModifiedRecords : function(){
8982         return this.modified;
8983     },
8984
8985     // private
8986     createFilterFn : function(property, value, anyMatch){
8987         if(!value.exec){ // not a regex
8988             value = String(value);
8989             if(value.length == 0){
8990                 return false;
8991             }
8992             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
8993         }
8994         return function(r){
8995             return value.test(r.data[property]);
8996         };
8997     },
8998
8999     /**
9000      * Sums the value of <i>property</i> for each record between start and end and returns the result.
9001      * @param {String} property A field on your records
9002      * @param {Number} start The record index to start at (defaults to 0)
9003      * @param {Number} end The last record index to include (defaults to length - 1)
9004      * @return {Number} The sum
9005      */
9006     sum : function(property, start, end){
9007         var rs = this.data.items, v = 0;
9008         start = start || 0;
9009         end = (end || end === 0) ? end : rs.length-1;
9010
9011         for(var i = start; i <= end; i++){
9012             v += (rs[i].data[property] || 0);
9013         }
9014         return v;
9015     },
9016
9017     /**
9018      * Filter the records by a specified property.
9019      * @param {String} field A field on your records
9020      * @param {String/RegExp} value Either a string that the field
9021      * should start with or a RegExp to test against the field
9022      * @param {Boolean} anyMatch True to match any part not just the beginning
9023      */
9024     filter : function(property, value, anyMatch){
9025         var fn = this.createFilterFn(property, value, anyMatch);
9026         return fn ? this.filterBy(fn) : this.clearFilter();
9027     },
9028
9029     /**
9030      * Filter by a function. The specified function will be called with each
9031      * record in this data source. If the function returns true the record is included,
9032      * otherwise it is filtered.
9033      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9034      * @param {Object} scope (optional) The scope of the function (defaults to this)
9035      */
9036     filterBy : function(fn, scope){
9037         this.snapshot = this.snapshot || this.data;
9038         this.data = this.queryBy(fn, scope||this);
9039         this.fireEvent("datachanged", this);
9040     },
9041
9042     /**
9043      * Query the records by a specified property.
9044      * @param {String} field A field on your records
9045      * @param {String/RegExp} value Either a string that the field
9046      * should start with or a RegExp to test against the field
9047      * @param {Boolean} anyMatch True to match any part not just the beginning
9048      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9049      */
9050     query : function(property, value, anyMatch){
9051         var fn = this.createFilterFn(property, value, anyMatch);
9052         return fn ? this.queryBy(fn) : this.data.clone();
9053     },
9054
9055     /**
9056      * Query by a function. The specified function will be called with each
9057      * record in this data source. If the function returns true the record is included
9058      * in the results.
9059      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9060      * @param {Object} scope (optional) The scope of the function (defaults to this)
9061       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9062      **/
9063     queryBy : function(fn, scope){
9064         var data = this.snapshot || this.data;
9065         return data.filterBy(fn, scope||this);
9066     },
9067
9068     /**
9069      * Collects unique values for a particular dataIndex from this store.
9070      * @param {String} dataIndex The property to collect
9071      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
9072      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
9073      * @return {Array} An array of the unique values
9074      **/
9075     collect : function(dataIndex, allowNull, bypassFilter){
9076         var d = (bypassFilter === true && this.snapshot) ?
9077                 this.snapshot.items : this.data.items;
9078         var v, sv, r = [], l = {};
9079         for(var i = 0, len = d.length; i < len; i++){
9080             v = d[i].data[dataIndex];
9081             sv = String(v);
9082             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
9083                 l[sv] = true;
9084                 r[r.length] = v;
9085             }
9086         }
9087         return r;
9088     },
9089
9090     /**
9091      * Revert to a view of the Record cache with no filtering applied.
9092      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
9093      */
9094     clearFilter : function(suppressEvent){
9095         if(this.snapshot && this.snapshot != this.data){
9096             this.data = this.snapshot;
9097             delete this.snapshot;
9098             if(suppressEvent !== true){
9099                 this.fireEvent("datachanged", this);
9100             }
9101         }
9102     },
9103
9104     // private
9105     afterEdit : function(record){
9106         if(this.modified.indexOf(record) == -1){
9107             this.modified.push(record);
9108         }
9109         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
9110     },
9111     
9112     // private
9113     afterReject : function(record){
9114         this.modified.remove(record);
9115         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
9116     },
9117
9118     // private
9119     afterCommit : function(record){
9120         this.modified.remove(record);
9121         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
9122     },
9123
9124     /**
9125      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
9126      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
9127      */
9128     commitChanges : function(){
9129         var m = this.modified.slice(0);
9130         this.modified = [];
9131         for(var i = 0, len = m.length; i < len; i++){
9132             m[i].commit();
9133         }
9134     },
9135
9136     /**
9137      * Cancel outstanding changes on all changed records.
9138      */
9139     rejectChanges : function(){
9140         var m = this.modified.slice(0);
9141         this.modified = [];
9142         for(var i = 0, len = m.length; i < len; i++){
9143             m[i].reject();
9144         }
9145     },
9146
9147     onMetaChange : function(meta, rtype, o){
9148         this.recordType = rtype;
9149         this.fields = rtype.prototype.fields;
9150         delete this.snapshot;
9151         this.sortInfo = meta.sortInfo || this.sortInfo;
9152         this.modified = [];
9153         this.fireEvent('metachange', this, this.reader.meta);
9154     },
9155     
9156     moveIndex : function(data, type)
9157     {
9158         var index = this.indexOf(data);
9159         
9160         var newIndex = index + type;
9161         
9162         this.remove(data);
9163         
9164         this.insert(newIndex, data);
9165         
9166     }
9167 });/*
9168  * Based on:
9169  * Ext JS Library 1.1.1
9170  * Copyright(c) 2006-2007, Ext JS, LLC.
9171  *
9172  * Originally Released Under LGPL - original licence link has changed is not relivant.
9173  *
9174  * Fork - LGPL
9175  * <script type="text/javascript">
9176  */
9177
9178 /**
9179  * @class Roo.data.SimpleStore
9180  * @extends Roo.data.Store
9181  * Small helper class to make creating Stores from Array data easier.
9182  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
9183  * @cfg {Array} fields An array of field definition objects, or field name strings.
9184  * @cfg {Array} data The multi-dimensional array of data
9185  * @constructor
9186  * @param {Object} config
9187  */
9188 Roo.data.SimpleStore = function(config){
9189     Roo.data.SimpleStore.superclass.constructor.call(this, {
9190         isLocal : true,
9191         reader: new Roo.data.ArrayReader({
9192                 id: config.id
9193             },
9194             Roo.data.Record.create(config.fields)
9195         ),
9196         proxy : new Roo.data.MemoryProxy(config.data)
9197     });
9198     this.load();
9199 };
9200 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
9201  * Based on:
9202  * Ext JS Library 1.1.1
9203  * Copyright(c) 2006-2007, Ext JS, LLC.
9204  *
9205  * Originally Released Under LGPL - original licence link has changed is not relivant.
9206  *
9207  * Fork - LGPL
9208  * <script type="text/javascript">
9209  */
9210
9211 /**
9212 /**
9213  * @extends Roo.data.Store
9214  * @class Roo.data.JsonStore
9215  * Small helper class to make creating Stores for JSON data easier. <br/>
9216 <pre><code>
9217 var store = new Roo.data.JsonStore({
9218     url: 'get-images.php',
9219     root: 'images',
9220     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
9221 });
9222 </code></pre>
9223  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
9224  * JsonReader and HttpProxy (unless inline data is provided).</b>
9225  * @cfg {Array} fields An array of field definition objects, or field name strings.
9226  * @constructor
9227  * @param {Object} config
9228  */
9229 Roo.data.JsonStore = function(c){
9230     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
9231         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
9232         reader: new Roo.data.JsonReader(c, c.fields)
9233     }));
9234 };
9235 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
9236  * Based on:
9237  * Ext JS Library 1.1.1
9238  * Copyright(c) 2006-2007, Ext JS, LLC.
9239  *
9240  * Originally Released Under LGPL - original licence link has changed is not relivant.
9241  *
9242  * Fork - LGPL
9243  * <script type="text/javascript">
9244  */
9245
9246  
9247 Roo.data.Field = function(config){
9248     if(typeof config == "string"){
9249         config = {name: config};
9250     }
9251     Roo.apply(this, config);
9252     
9253     if(!this.type){
9254         this.type = "auto";
9255     }
9256     
9257     var st = Roo.data.SortTypes;
9258     // named sortTypes are supported, here we look them up
9259     if(typeof this.sortType == "string"){
9260         this.sortType = st[this.sortType];
9261     }
9262     
9263     // set default sortType for strings and dates
9264     if(!this.sortType){
9265         switch(this.type){
9266             case "string":
9267                 this.sortType = st.asUCString;
9268                 break;
9269             case "date":
9270                 this.sortType = st.asDate;
9271                 break;
9272             default:
9273                 this.sortType = st.none;
9274         }
9275     }
9276
9277     // define once
9278     var stripRe = /[\$,%]/g;
9279
9280     // prebuilt conversion function for this field, instead of
9281     // switching every time we're reading a value
9282     if(!this.convert){
9283         var cv, dateFormat = this.dateFormat;
9284         switch(this.type){
9285             case "":
9286             case "auto":
9287             case undefined:
9288                 cv = function(v){ return v; };
9289                 break;
9290             case "string":
9291                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
9292                 break;
9293             case "int":
9294                 cv = function(v){
9295                     return v !== undefined && v !== null && v !== '' ?
9296                            parseInt(String(v).replace(stripRe, ""), 10) : '';
9297                     };
9298                 break;
9299             case "float":
9300                 cv = function(v){
9301                     return v !== undefined && v !== null && v !== '' ?
9302                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
9303                     };
9304                 break;
9305             case "bool":
9306             case "boolean":
9307                 cv = function(v){ return v === true || v === "true" || v == 1; };
9308                 break;
9309             case "date":
9310                 cv = function(v){
9311                     if(!v){
9312                         return '';
9313                     }
9314                     if(v instanceof Date){
9315                         return v;
9316                     }
9317                     if(dateFormat){
9318                         if(dateFormat == "timestamp"){
9319                             return new Date(v*1000);
9320                         }
9321                         return Date.parseDate(v, dateFormat);
9322                     }
9323                     var parsed = Date.parse(v);
9324                     return parsed ? new Date(parsed) : null;
9325                 };
9326              break;
9327             
9328         }
9329         this.convert = cv;
9330     }
9331 };
9332
9333 Roo.data.Field.prototype = {
9334     dateFormat: null,
9335     defaultValue: "",
9336     mapping: null,
9337     sortType : null,
9338     sortDir : "ASC"
9339 };/*
9340  * Based on:
9341  * Ext JS Library 1.1.1
9342  * Copyright(c) 2006-2007, Ext JS, LLC.
9343  *
9344  * Originally Released Under LGPL - original licence link has changed is not relivant.
9345  *
9346  * Fork - LGPL
9347  * <script type="text/javascript">
9348  */
9349  
9350 // Base class for reading structured data from a data source.  This class is intended to be
9351 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
9352
9353 /**
9354  * @class Roo.data.DataReader
9355  * Base class for reading structured data from a data source.  This class is intended to be
9356  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
9357  */
9358
9359 Roo.data.DataReader = function(meta, recordType){
9360     
9361     this.meta = meta;
9362     
9363     this.recordType = recordType instanceof Array ? 
9364         Roo.data.Record.create(recordType) : recordType;
9365 };
9366
9367 Roo.data.DataReader.prototype = {
9368      /**
9369      * Create an empty record
9370      * @param {Object} data (optional) - overlay some values
9371      * @return {Roo.data.Record} record created.
9372      */
9373     newRow :  function(d) {
9374         var da =  {};
9375         this.recordType.prototype.fields.each(function(c) {
9376             switch( c.type) {
9377                 case 'int' : da[c.name] = 0; break;
9378                 case 'date' : da[c.name] = new Date(); break;
9379                 case 'float' : da[c.name] = 0.0; break;
9380                 case 'boolean' : da[c.name] = false; break;
9381                 default : da[c.name] = ""; break;
9382             }
9383             
9384         });
9385         return new this.recordType(Roo.apply(da, d));
9386     }
9387     
9388 };/*
9389  * Based on:
9390  * Ext JS Library 1.1.1
9391  * Copyright(c) 2006-2007, Ext JS, LLC.
9392  *
9393  * Originally Released Under LGPL - original licence link has changed is not relivant.
9394  *
9395  * Fork - LGPL
9396  * <script type="text/javascript">
9397  */
9398
9399 /**
9400  * @class Roo.data.DataProxy
9401  * @extends Roo.data.Observable
9402  * This class is an abstract base class for implementations which provide retrieval of
9403  * unformatted data objects.<br>
9404  * <p>
9405  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
9406  * (of the appropriate type which knows how to parse the data object) to provide a block of
9407  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
9408  * <p>
9409  * Custom implementations must implement the load method as described in
9410  * {@link Roo.data.HttpProxy#load}.
9411  */
9412 Roo.data.DataProxy = function(){
9413     this.addEvents({
9414         /**
9415          * @event beforeload
9416          * Fires before a network request is made to retrieve a data object.
9417          * @param {Object} This DataProxy object.
9418          * @param {Object} params The params parameter to the load function.
9419          */
9420         beforeload : true,
9421         /**
9422          * @event load
9423          * Fires before the load method's callback is called.
9424          * @param {Object} This DataProxy object.
9425          * @param {Object} o The data object.
9426          * @param {Object} arg The callback argument object passed to the load function.
9427          */
9428         load : true,
9429         /**
9430          * @event loadexception
9431          * Fires if an Exception occurs during data retrieval.
9432          * @param {Object} This DataProxy object.
9433          * @param {Object} o The data object.
9434          * @param {Object} arg The callback argument object passed to the load function.
9435          * @param {Object} e The Exception.
9436          */
9437         loadexception : true
9438     });
9439     Roo.data.DataProxy.superclass.constructor.call(this);
9440 };
9441
9442 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
9443
9444     /**
9445      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
9446      */
9447 /*
9448  * Based on:
9449  * Ext JS Library 1.1.1
9450  * Copyright(c) 2006-2007, Ext JS, LLC.
9451  *
9452  * Originally Released Under LGPL - original licence link has changed is not relivant.
9453  *
9454  * Fork - LGPL
9455  * <script type="text/javascript">
9456  */
9457 /**
9458  * @class Roo.data.MemoryProxy
9459  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
9460  * to the Reader when its load method is called.
9461  * @constructor
9462  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
9463  */
9464 Roo.data.MemoryProxy = function(data){
9465     if (data.data) {
9466         data = data.data;
9467     }
9468     Roo.data.MemoryProxy.superclass.constructor.call(this);
9469     this.data = data;
9470 };
9471
9472 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
9473     /**
9474      * Load data from the requested source (in this case an in-memory
9475      * data object passed to the constructor), read the data object into
9476      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9477      * process that block using the passed callback.
9478      * @param {Object} params This parameter is not used by the MemoryProxy class.
9479      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9480      * object into a block of Roo.data.Records.
9481      * @param {Function} callback The function into which to pass the block of Roo.data.records.
9482      * The function must be passed <ul>
9483      * <li>The Record block object</li>
9484      * <li>The "arg" argument from the load function</li>
9485      * <li>A boolean success indicator</li>
9486      * </ul>
9487      * @param {Object} scope The scope in which to call the callback
9488      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9489      */
9490     load : function(params, reader, callback, scope, arg){
9491         params = params || {};
9492         var result;
9493         try {
9494             result = reader.readRecords(this.data);
9495         }catch(e){
9496             this.fireEvent("loadexception", this, arg, null, e);
9497             callback.call(scope, null, arg, false);
9498             return;
9499         }
9500         callback.call(scope, result, arg, true);
9501     },
9502     
9503     // private
9504     update : function(params, records){
9505         
9506     }
9507 });/*
9508  * Based on:
9509  * Ext JS Library 1.1.1
9510  * Copyright(c) 2006-2007, Ext JS, LLC.
9511  *
9512  * Originally Released Under LGPL - original licence link has changed is not relivant.
9513  *
9514  * Fork - LGPL
9515  * <script type="text/javascript">
9516  */
9517 /**
9518  * @class Roo.data.HttpProxy
9519  * @extends Roo.data.DataProxy
9520  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
9521  * configured to reference a certain URL.<br><br>
9522  * <p>
9523  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
9524  * from which the running page was served.<br><br>
9525  * <p>
9526  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
9527  * <p>
9528  * Be aware that to enable the browser to parse an XML document, the server must set
9529  * the Content-Type header in the HTTP response to "text/xml".
9530  * @constructor
9531  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
9532  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
9533  * will be used to make the request.
9534  */
9535 Roo.data.HttpProxy = function(conn){
9536     Roo.data.HttpProxy.superclass.constructor.call(this);
9537     // is conn a conn config or a real conn?
9538     this.conn = conn;
9539     this.useAjax = !conn || !conn.events;
9540   
9541 };
9542
9543 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
9544     // thse are take from connection...
9545     
9546     /**
9547      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
9548      */
9549     /**
9550      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
9551      * extra parameters to each request made by this object. (defaults to undefined)
9552      */
9553     /**
9554      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
9555      *  to each request made by this object. (defaults to undefined)
9556      */
9557     /**
9558      * @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)
9559      */
9560     /**
9561      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
9562      */
9563      /**
9564      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
9565      * @type Boolean
9566      */
9567   
9568
9569     /**
9570      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
9571      * @type Boolean
9572      */
9573     /**
9574      * Return the {@link Roo.data.Connection} object being used by this Proxy.
9575      * @return {Connection} The Connection object. This object may be used to subscribe to events on
9576      * a finer-grained basis than the DataProxy events.
9577      */
9578     getConnection : function(){
9579         return this.useAjax ? Roo.Ajax : this.conn;
9580     },
9581
9582     /**
9583      * Load data from the configured {@link Roo.data.Connection}, read the data object into
9584      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
9585      * process that block using the passed callback.
9586      * @param {Object} params An object containing properties which are to be used as HTTP parameters
9587      * for the request to the remote server.
9588      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9589      * object into a block of Roo.data.Records.
9590      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9591      * The function must be passed <ul>
9592      * <li>The Record block object</li>
9593      * <li>The "arg" argument from the load function</li>
9594      * <li>A boolean success indicator</li>
9595      * </ul>
9596      * @param {Object} scope The scope in which to call the callback
9597      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9598      */
9599     load : function(params, reader, callback, scope, arg){
9600         if(this.fireEvent("beforeload", this, params) !== false){
9601             var  o = {
9602                 params : params || {},
9603                 request: {
9604                     callback : callback,
9605                     scope : scope,
9606                     arg : arg
9607                 },
9608                 reader: reader,
9609                 callback : this.loadResponse,
9610                 scope: this
9611             };
9612             if(this.useAjax){
9613                 Roo.applyIf(o, this.conn);
9614                 if(this.activeRequest){
9615                     Roo.Ajax.abort(this.activeRequest);
9616                 }
9617                 this.activeRequest = Roo.Ajax.request(o);
9618             }else{
9619                 this.conn.request(o);
9620             }
9621         }else{
9622             callback.call(scope||this, null, arg, false);
9623         }
9624     },
9625
9626     // private
9627     loadResponse : function(o, success, response){
9628         delete this.activeRequest;
9629         if(!success){
9630             this.fireEvent("loadexception", this, o, response);
9631             o.request.callback.call(o.request.scope, null, o.request.arg, false);
9632             return;
9633         }
9634         var result;
9635         try {
9636             result = o.reader.read(response);
9637         }catch(e){
9638             this.fireEvent("loadexception", this, o, response, e);
9639             o.request.callback.call(o.request.scope, null, o.request.arg, false);
9640             return;
9641         }
9642         
9643         this.fireEvent("load", this, o, o.request.arg);
9644         o.request.callback.call(o.request.scope, result, o.request.arg, true);
9645     },
9646
9647     // private
9648     update : function(dataSet){
9649
9650     },
9651
9652     // private
9653     updateResponse : function(dataSet){
9654
9655     }
9656 });/*
9657  * Based on:
9658  * Ext JS Library 1.1.1
9659  * Copyright(c) 2006-2007, Ext JS, LLC.
9660  *
9661  * Originally Released Under LGPL - original licence link has changed is not relivant.
9662  *
9663  * Fork - LGPL
9664  * <script type="text/javascript">
9665  */
9666
9667 /**
9668  * @class Roo.data.ScriptTagProxy
9669  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
9670  * other than the originating domain of the running page.<br><br>
9671  * <p>
9672  * <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
9673  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
9674  * <p>
9675  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
9676  * source code that is used as the source inside a &lt;script> tag.<br><br>
9677  * <p>
9678  * In order for the browser to process the returned data, the server must wrap the data object
9679  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
9680  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
9681  * depending on whether the callback name was passed:
9682  * <p>
9683  * <pre><code>
9684 boolean scriptTag = false;
9685 String cb = request.getParameter("callback");
9686 if (cb != null) {
9687     scriptTag = true;
9688     response.setContentType("text/javascript");
9689 } else {
9690     response.setContentType("application/x-json");
9691 }
9692 Writer out = response.getWriter();
9693 if (scriptTag) {
9694     out.write(cb + "(");
9695 }
9696 out.print(dataBlock.toJsonString());
9697 if (scriptTag) {
9698     out.write(");");
9699 }
9700 </pre></code>
9701  *
9702  * @constructor
9703  * @param {Object} config A configuration object.
9704  */
9705 Roo.data.ScriptTagProxy = function(config){
9706     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
9707     Roo.apply(this, config);
9708     this.head = document.getElementsByTagName("head")[0];
9709 };
9710
9711 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
9712
9713 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
9714     /**
9715      * @cfg {String} url The URL from which to request the data object.
9716      */
9717     /**
9718      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
9719      */
9720     timeout : 30000,
9721     /**
9722      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
9723      * the server the name of the callback function set up by the load call to process the returned data object.
9724      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
9725      * javascript output which calls this named function passing the data object as its only parameter.
9726      */
9727     callbackParam : "callback",
9728     /**
9729      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
9730      * name to the request.
9731      */
9732     nocache : true,
9733
9734     /**
9735      * Load data from the configured URL, read the data object into
9736      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9737      * process that block using the passed callback.
9738      * @param {Object} params An object containing properties which are to be used as HTTP parameters
9739      * for the request to the remote server.
9740      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9741      * object into a block of Roo.data.Records.
9742      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9743      * The function must be passed <ul>
9744      * <li>The Record block object</li>
9745      * <li>The "arg" argument from the load function</li>
9746      * <li>A boolean success indicator</li>
9747      * </ul>
9748      * @param {Object} scope The scope in which to call the callback
9749      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9750      */
9751     load : function(params, reader, callback, scope, arg){
9752         if(this.fireEvent("beforeload", this, params) !== false){
9753
9754             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
9755
9756             var url = this.url;
9757             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
9758             if(this.nocache){
9759                 url += "&_dc=" + (new Date().getTime());
9760             }
9761             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
9762             var trans = {
9763                 id : transId,
9764                 cb : "stcCallback"+transId,
9765                 scriptId : "stcScript"+transId,
9766                 params : params,
9767                 arg : arg,
9768                 url : url,
9769                 callback : callback,
9770                 scope : scope,
9771                 reader : reader
9772             };
9773             var conn = this;
9774
9775             window[trans.cb] = function(o){
9776                 conn.handleResponse(o, trans);
9777             };
9778
9779             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
9780
9781             if(this.autoAbort !== false){
9782                 this.abort();
9783             }
9784
9785             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
9786
9787             var script = document.createElement("script");
9788             script.setAttribute("src", url);
9789             script.setAttribute("type", "text/javascript");
9790             script.setAttribute("id", trans.scriptId);
9791             this.head.appendChild(script);
9792
9793             this.trans = trans;
9794         }else{
9795             callback.call(scope||this, null, arg, false);
9796         }
9797     },
9798
9799     // private
9800     isLoading : function(){
9801         return this.trans ? true : false;
9802     },
9803
9804     /**
9805      * Abort the current server request.
9806      */
9807     abort : function(){
9808         if(this.isLoading()){
9809             this.destroyTrans(this.trans);
9810         }
9811     },
9812
9813     // private
9814     destroyTrans : function(trans, isLoaded){
9815         this.head.removeChild(document.getElementById(trans.scriptId));
9816         clearTimeout(trans.timeoutId);
9817         if(isLoaded){
9818             window[trans.cb] = undefined;
9819             try{
9820                 delete window[trans.cb];
9821             }catch(e){}
9822         }else{
9823             // if hasn't been loaded, wait for load to remove it to prevent script error
9824             window[trans.cb] = function(){
9825                 window[trans.cb] = undefined;
9826                 try{
9827                     delete window[trans.cb];
9828                 }catch(e){}
9829             };
9830         }
9831     },
9832
9833     // private
9834     handleResponse : function(o, trans){
9835         this.trans = false;
9836         this.destroyTrans(trans, true);
9837         var result;
9838         try {
9839             result = trans.reader.readRecords(o);
9840         }catch(e){
9841             this.fireEvent("loadexception", this, o, trans.arg, e);
9842             trans.callback.call(trans.scope||window, null, trans.arg, false);
9843             return;
9844         }
9845         this.fireEvent("load", this, o, trans.arg);
9846         trans.callback.call(trans.scope||window, result, trans.arg, true);
9847     },
9848
9849     // private
9850     handleFailure : function(trans){
9851         this.trans = false;
9852         this.destroyTrans(trans, false);
9853         this.fireEvent("loadexception", this, null, trans.arg);
9854         trans.callback.call(trans.scope||window, null, trans.arg, false);
9855     }
9856 });/*
9857  * Based on:
9858  * Ext JS Library 1.1.1
9859  * Copyright(c) 2006-2007, Ext JS, LLC.
9860  *
9861  * Originally Released Under LGPL - original licence link has changed is not relivant.
9862  *
9863  * Fork - LGPL
9864  * <script type="text/javascript">
9865  */
9866
9867 /**
9868  * @class Roo.data.JsonReader
9869  * @extends Roo.data.DataReader
9870  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
9871  * based on mappings in a provided Roo.data.Record constructor.
9872  * 
9873  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
9874  * in the reply previously. 
9875  * 
9876  * <p>
9877  * Example code:
9878  * <pre><code>
9879 var RecordDef = Roo.data.Record.create([
9880     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
9881     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
9882 ]);
9883 var myReader = new Roo.data.JsonReader({
9884     totalProperty: "results",    // The property which contains the total dataset size (optional)
9885     root: "rows",                // The property which contains an Array of row objects
9886     id: "id"                     // The property within each row object that provides an ID for the record (optional)
9887 }, RecordDef);
9888 </code></pre>
9889  * <p>
9890  * This would consume a JSON file like this:
9891  * <pre><code>
9892 { 'results': 2, 'rows': [
9893     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
9894     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
9895 }
9896 </code></pre>
9897  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
9898  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
9899  * paged from the remote server.
9900  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
9901  * @cfg {String} root name of the property which contains the Array of row objects.
9902  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
9903  * @constructor
9904  * Create a new JsonReader
9905  * @param {Object} meta Metadata configuration options
9906  * @param {Object} recordType Either an Array of field definition objects,
9907  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
9908  */
9909 Roo.data.JsonReader = function(meta, recordType){
9910     
9911     meta = meta || {};
9912     // set some defaults:
9913     Roo.applyIf(meta, {
9914         totalProperty: 'total',
9915         successProperty : 'success',
9916         root : 'data',
9917         id : 'id'
9918     });
9919     
9920     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
9921 };
9922 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
9923     
9924     /**
9925      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
9926      * Used by Store query builder to append _requestMeta to params.
9927      * 
9928      */
9929     metaFromRemote : false,
9930     /**
9931      * This method is only used by a DataProxy which has retrieved data from a remote server.
9932      * @param {Object} response The XHR object which contains the JSON data in its responseText.
9933      * @return {Object} data A data block which is used by an Roo.data.Store object as
9934      * a cache of Roo.data.Records.
9935      */
9936     read : function(response){
9937         var json = response.responseText;
9938        
9939         var o = /* eval:var:o */ eval("("+json+")");
9940         if(!o) {
9941             throw {message: "JsonReader.read: Json object not found"};
9942         }
9943         
9944         if(o.metaData){
9945             
9946             delete this.ef;
9947             this.metaFromRemote = true;
9948             this.meta = o.metaData;
9949             this.recordType = Roo.data.Record.create(o.metaData.fields);
9950             this.onMetaChange(this.meta, this.recordType, o);
9951         }
9952         return this.readRecords(o);
9953     },
9954
9955     // private function a store will implement
9956     onMetaChange : function(meta, recordType, o){
9957
9958     },
9959
9960     /**
9961          * @ignore
9962          */
9963     simpleAccess: function(obj, subsc) {
9964         return obj[subsc];
9965     },
9966
9967         /**
9968          * @ignore
9969          */
9970     getJsonAccessor: function(){
9971         var re = /[\[\.]/;
9972         return function(expr) {
9973             try {
9974                 return(re.test(expr))
9975                     ? new Function("obj", "return obj." + expr)
9976                     : function(obj){
9977                         return obj[expr];
9978                     };
9979             } catch(e){}
9980             return Roo.emptyFn;
9981         };
9982     }(),
9983
9984     /**
9985      * Create a data block containing Roo.data.Records from an XML document.
9986      * @param {Object} o An object which contains an Array of row objects in the property specified
9987      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
9988      * which contains the total size of the dataset.
9989      * @return {Object} data A data block which is used by an Roo.data.Store object as
9990      * a cache of Roo.data.Records.
9991      */
9992     readRecords : function(o){
9993         /**
9994          * After any data loads, the raw JSON data is available for further custom processing.
9995          * @type Object
9996          */
9997         this.o = o;
9998         var s = this.meta, Record = this.recordType,
9999             f = Record.prototype.fields, fi = f.items, fl = f.length;
10000
10001 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
10002         if (!this.ef) {
10003             if(s.totalProperty) {
10004                     this.getTotal = this.getJsonAccessor(s.totalProperty);
10005                 }
10006                 if(s.successProperty) {
10007                     this.getSuccess = this.getJsonAccessor(s.successProperty);
10008                 }
10009                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
10010                 if (s.id) {
10011                         var g = this.getJsonAccessor(s.id);
10012                         this.getId = function(rec) {
10013                                 var r = g(rec);
10014                                 return (r === undefined || r === "") ? null : r;
10015                         };
10016                 } else {
10017                         this.getId = function(){return null;};
10018                 }
10019             this.ef = [];
10020             for(var jj = 0; jj < fl; jj++){
10021                 f = fi[jj];
10022                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
10023                 this.ef[jj] = this.getJsonAccessor(map);
10024             }
10025         }
10026
10027         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
10028         if(s.totalProperty){
10029             var vt = parseInt(this.getTotal(o), 10);
10030             if(!isNaN(vt)){
10031                 totalRecords = vt;
10032             }
10033         }
10034         if(s.successProperty){
10035             var vs = this.getSuccess(o);
10036             if(vs === false || vs === 'false'){
10037                 success = false;
10038             }
10039         }
10040         var records = [];
10041             for(var i = 0; i < c; i++){
10042                     var n = root[i];
10043                 var values = {};
10044                 var id = this.getId(n);
10045                 for(var j = 0; j < fl; j++){
10046                     f = fi[j];
10047                 var v = this.ef[j](n);
10048                 if (!f.convert) {
10049                     Roo.log('missing convert for ' + f.name);
10050                     Roo.log(f);
10051                     continue;
10052                 }
10053                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
10054                 }
10055                 var record = new Record(values, id);
10056                 record.json = n;
10057                 records[i] = record;
10058             }
10059             return {
10060             raw : o,
10061                 success : success,
10062                 records : records,
10063                 totalRecords : totalRecords
10064             };
10065     }
10066 });/*
10067  * Based on:
10068  * Ext JS Library 1.1.1
10069  * Copyright(c) 2006-2007, Ext JS, LLC.
10070  *
10071  * Originally Released Under LGPL - original licence link has changed is not relivant.
10072  *
10073  * Fork - LGPL
10074  * <script type="text/javascript">
10075  */
10076
10077 /**
10078  * @class Roo.data.ArrayReader
10079  * @extends Roo.data.DataReader
10080  * Data reader class to create an Array of Roo.data.Record objects from an Array.
10081  * Each element of that Array represents a row of data fields. The
10082  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
10083  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
10084  * <p>
10085  * Example code:.
10086  * <pre><code>
10087 var RecordDef = Roo.data.Record.create([
10088     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
10089     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
10090 ]);
10091 var myReader = new Roo.data.ArrayReader({
10092     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
10093 }, RecordDef);
10094 </code></pre>
10095  * <p>
10096  * This would consume an Array like this:
10097  * <pre><code>
10098 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
10099   </code></pre>
10100  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
10101  * @constructor
10102  * Create a new JsonReader
10103  * @param {Object} meta Metadata configuration options.
10104  * @param {Object} recordType Either an Array of field definition objects
10105  * as specified to {@link Roo.data.Record#create},
10106  * or an {@link Roo.data.Record} object
10107  * created using {@link Roo.data.Record#create}.
10108  */
10109 Roo.data.ArrayReader = function(meta, recordType){
10110     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
10111 };
10112
10113 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
10114     /**
10115      * Create a data block containing Roo.data.Records from an XML document.
10116      * @param {Object} o An Array of row objects which represents the dataset.
10117      * @return {Object} data A data block which is used by an Roo.data.Store object as
10118      * a cache of Roo.data.Records.
10119      */
10120     readRecords : function(o){
10121         var sid = this.meta ? this.meta.id : null;
10122         var recordType = this.recordType, fields = recordType.prototype.fields;
10123         var records = [];
10124         var root = o;
10125             for(var i = 0; i < root.length; i++){
10126                     var n = root[i];
10127                 var values = {};
10128                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
10129                 for(var j = 0, jlen = fields.length; j < jlen; j++){
10130                 var f = fields.items[j];
10131                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
10132                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
10133                 v = f.convert(v);
10134                 values[f.name] = v;
10135             }
10136                 var record = new recordType(values, id);
10137                 record.json = n;
10138                 records[records.length] = record;
10139             }
10140             return {
10141                 records : records,
10142                 totalRecords : records.length
10143             };
10144     }
10145 });/*
10146  * - LGPL
10147  * * 
10148  */
10149
10150 /**
10151  * @class Roo.bootstrap.ComboBox
10152  * @extends Roo.bootstrap.TriggerField
10153  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
10154  * @cfg {Boolean} append (true|false) default false
10155  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
10156  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
10157  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
10158  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
10159  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
10160  * @constructor
10161  * Create a new ComboBox.
10162  * @param {Object} config Configuration options
10163  */
10164 Roo.bootstrap.ComboBox = function(config){
10165     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
10166     this.addEvents({
10167         /**
10168          * @event expand
10169          * Fires when the dropdown list is expanded
10170              * @param {Roo.bootstrap.ComboBox} combo This combo box
10171              */
10172         'expand' : true,
10173         /**
10174          * @event collapse
10175          * Fires when the dropdown list is collapsed
10176              * @param {Roo.bootstrap.ComboBox} combo This combo box
10177              */
10178         'collapse' : true,
10179         /**
10180          * @event beforeselect
10181          * Fires before a list item is selected. Return false to cancel the selection.
10182              * @param {Roo.bootstrap.ComboBox} combo This combo box
10183              * @param {Roo.data.Record} record The data record returned from the underlying store
10184              * @param {Number} index The index of the selected item in the dropdown list
10185              */
10186         'beforeselect' : true,
10187         /**
10188          * @event select
10189          * Fires when a list item is selected
10190              * @param {Roo.bootstrap.ComboBox} combo This combo box
10191              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
10192              * @param {Number} index The index of the selected item in the dropdown list
10193              */
10194         'select' : true,
10195         /**
10196          * @event beforequery
10197          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
10198          * The event object passed has these properties:
10199              * @param {Roo.bootstrap.ComboBox} combo This combo box
10200              * @param {String} query The query
10201              * @param {Boolean} forceAll true to force "all" query
10202              * @param {Boolean} cancel true to cancel the query
10203              * @param {Object} e The query event object
10204              */
10205         'beforequery': true,
10206          /**
10207          * @event add
10208          * Fires when the 'add' icon is pressed (add a listener to enable add button)
10209              * @param {Roo.bootstrap.ComboBox} combo This combo box
10210              */
10211         'add' : true,
10212         /**
10213          * @event edit
10214          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
10215              * @param {Roo.bootstrap.ComboBox} combo This combo box
10216              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
10217              */
10218         'edit' : true,
10219         /**
10220          * @event remove
10221          * Fires when the remove value from the combobox array
10222              * @param {Roo.bootstrap.ComboBox} combo This combo box
10223              */
10224         'remove' : true
10225         
10226     });
10227     
10228     this.item = [];
10229     this.tickItems = [];
10230     
10231     this.selectedIndex = -1;
10232     if(this.mode == 'local'){
10233         if(config.queryDelay === undefined){
10234             this.queryDelay = 10;
10235         }
10236         if(config.minChars === undefined){
10237             this.minChars = 0;
10238         }
10239     }
10240 };
10241
10242 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
10243      
10244     /**
10245      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
10246      * rendering into an Roo.Editor, defaults to false)
10247      */
10248     /**
10249      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
10250      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
10251      */
10252     /**
10253      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
10254      */
10255     /**
10256      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
10257      * the dropdown list (defaults to undefined, with no header element)
10258      */
10259
10260      /**
10261      * @cfg {String/Roo.Template} tpl The template to use to render the output
10262      */
10263      
10264      /**
10265      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
10266      */
10267     listWidth: undefined,
10268     /**
10269      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
10270      * mode = 'remote' or 'text' if mode = 'local')
10271      */
10272     displayField: undefined,
10273     /**
10274      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
10275      * mode = 'remote' or 'value' if mode = 'local'). 
10276      * Note: use of a valueField requires the user make a selection
10277      * in order for a value to be mapped.
10278      */
10279     valueField: undefined,
10280     
10281     
10282     /**
10283      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
10284      * field's data value (defaults to the underlying DOM element's name)
10285      */
10286     hiddenName: undefined,
10287     /**
10288      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
10289      */
10290     listClass: '',
10291     /**
10292      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
10293      */
10294     selectedClass: 'active',
10295     
10296     /**
10297      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
10298      */
10299     shadow:'sides',
10300     /**
10301      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
10302      * anchor positions (defaults to 'tl-bl')
10303      */
10304     listAlign: 'tl-bl?',
10305     /**
10306      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
10307      */
10308     maxHeight: 300,
10309     /**
10310      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
10311      * query specified by the allQuery config option (defaults to 'query')
10312      */
10313     triggerAction: 'query',
10314     /**
10315      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
10316      * (defaults to 4, does not apply if editable = false)
10317      */
10318     minChars : 4,
10319     /**
10320      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
10321      * delay (typeAheadDelay) if it matches a known value (defaults to false)
10322      */
10323     typeAhead: false,
10324     /**
10325      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
10326      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
10327      */
10328     queryDelay: 500,
10329     /**
10330      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
10331      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
10332      */
10333     pageSize: 0,
10334     /**
10335      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
10336      * when editable = true (defaults to false)
10337      */
10338     selectOnFocus:false,
10339     /**
10340      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
10341      */
10342     queryParam: 'query',
10343     /**
10344      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
10345      * when mode = 'remote' (defaults to 'Loading...')
10346      */
10347     loadingText: 'Loading...',
10348     /**
10349      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
10350      */
10351     resizable: false,
10352     /**
10353      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
10354      */
10355     handleHeight : 8,
10356     /**
10357      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
10358      * traditional select (defaults to true)
10359      */
10360     editable: true,
10361     /**
10362      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
10363      */
10364     allQuery: '',
10365     /**
10366      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
10367      */
10368     mode: 'remote',
10369     /**
10370      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
10371      * listWidth has a higher value)
10372      */
10373     minListWidth : 70,
10374     /**
10375      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
10376      * allow the user to set arbitrary text into the field (defaults to false)
10377      */
10378     forceSelection:false,
10379     /**
10380      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
10381      * if typeAhead = true (defaults to 250)
10382      */
10383     typeAheadDelay : 250,
10384     /**
10385      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
10386      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
10387      */
10388     valueNotFoundText : undefined,
10389     /**
10390      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
10391      */
10392     blockFocus : false,
10393     
10394     /**
10395      * @cfg {Boolean} disableClear Disable showing of clear button.
10396      */
10397     disableClear : false,
10398     /**
10399      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
10400      */
10401     alwaysQuery : false,
10402     
10403     /**
10404      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
10405      */
10406     multiple : false,
10407     
10408     //private
10409     addicon : false,
10410     editicon: false,
10411     
10412     page: 0,
10413     hasQuery: false,
10414     append: false,
10415     loadNext: false,
10416     autoFocus : true,
10417     tickable : false,
10418     btnPosition : 'right',
10419     triggerList : true,
10420     showToggleBtn : true,
10421     // element that contains real text value.. (when hidden is used..)
10422     
10423     getAutoCreate : function()
10424     {
10425         var cfg = false;
10426         
10427         /*
10428          *  Normal ComboBox
10429          */
10430         if(!this.tickable){
10431             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
10432             return cfg;
10433         }
10434         
10435         /*
10436          *  ComboBox with tickable selections
10437          */
10438              
10439         var align = this.labelAlign || this.parentLabelAlign();
10440         
10441         cfg = {
10442             cls : 'form-group roo-combobox-tickable' //input-group
10443         };
10444         
10445         
10446         var buttons = {
10447             tag : 'div',
10448             cls : 'tickable-buttons',
10449             cn : [
10450                 {
10451                     tag : 'button',
10452                     type : 'button',
10453                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
10454                     html : 'Edit'
10455                 },
10456                 {
10457                     tag : 'button',
10458                     type : 'button',
10459                     name : 'ok',
10460                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
10461                     html : 'Done'
10462                 },
10463                 {
10464                     tag : 'button',
10465                     type : 'button',
10466                     name : 'cancel',
10467                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
10468                     html : 'Cancel'
10469                 }
10470             ]
10471         };
10472         
10473         var _this = this;
10474         Roo.each(buttons.cn, function(c){
10475             if (_this.size) {
10476                 c.cls += ' btn-' + _this.size;
10477             }
10478
10479             if (_this.disabled) {
10480                 c.disabled = true;
10481             }
10482         });
10483         
10484         var box = {
10485             tag: 'div',
10486             cn: [
10487                 {
10488                     tag: 'input',
10489                     type : 'hidden',
10490                     cls: 'form-hidden-field'
10491                 },
10492                 {
10493                     tag: 'ul',
10494                     cls: 'select2-choices',
10495                     cn:[
10496                         {
10497                             tag: 'li',
10498                             cls: 'select2-search-field',
10499                             cn: [
10500
10501                                 buttons
10502                             ]
10503                         }
10504                     ]
10505                 }
10506             ]
10507         }
10508         
10509         var combobox = {
10510             cls: 'select2-container input-group select2-container-multi',
10511             cn: [
10512                 box
10513 //                {
10514 //                    tag: 'ul',
10515 //                    cls: 'typeahead typeahead-long dropdown-menu',
10516 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
10517 //                }
10518             ]
10519         };
10520         
10521         if (align ==='left' && this.fieldLabel.length) {
10522             
10523                 Roo.log("left and has label");
10524                 cfg.cn = [
10525                     
10526                     {
10527                         tag: 'label',
10528                         'for' :  id,
10529                         cls : 'control-label col-sm-' + this.labelWidth,
10530                         html : this.fieldLabel
10531                         
10532                     },
10533                     {
10534                         cls : "col-sm-" + (12 - this.labelWidth), 
10535                         cn: [
10536                             combobox
10537                         ]
10538                     }
10539                     
10540                 ];
10541         } else if ( this.fieldLabel.length) {
10542                 Roo.log(" label");
10543                  cfg.cn = [
10544                    
10545                     {
10546                         tag: 'label',
10547                         //cls : 'input-group-addon',
10548                         html : this.fieldLabel
10549                         
10550                     },
10551                     
10552                     combobox
10553                     
10554                 ];
10555
10556         } else {
10557             
10558                 Roo.log(" no label && no align");
10559                 cfg = combobox
10560                      
10561                 
10562         }
10563          
10564         var settings=this;
10565         ['xs','sm','md','lg'].map(function(size){
10566             if (settings[size]) {
10567                 cfg.cls += ' col-' + size + '-' + settings[size];
10568             }
10569         });
10570         
10571         return cfg;
10572         
10573     },
10574     
10575     // private
10576     initEvents: function()
10577     {
10578         
10579         if (!this.store) {
10580             throw "can not find store for combo";
10581         }
10582         this.store = Roo.factory(this.store, Roo.data);
10583         
10584         if(this.tickable){
10585             this.initTickableEvents();
10586             return;
10587         }
10588         
10589         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
10590         
10591         if(this.hiddenName){
10592             
10593             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10594             
10595             this.hiddenField.dom.value =
10596                 this.hiddenValue !== undefined ? this.hiddenValue :
10597                 this.value !== undefined ? this.value : '';
10598
10599             // prevent input submission
10600             this.el.dom.removeAttribute('name');
10601             this.hiddenField.dom.setAttribute('name', this.hiddenName);
10602              
10603              
10604         }
10605         //if(Roo.isGecko){
10606         //    this.el.dom.setAttribute('autocomplete', 'off');
10607         //}
10608         
10609         var cls = 'x-combo-list';
10610         
10611         //this.list = new Roo.Layer({
10612         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
10613         //});
10614         
10615         var _this = this;
10616         
10617         (function(){
10618             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
10619             _this.list.setWidth(lw);
10620         }).defer(100);
10621         
10622         this.list.on('mouseover', this.onViewOver, this);
10623         this.list.on('mousemove', this.onViewMove, this);
10624         
10625         this.list.on('scroll', this.onViewScroll, this);
10626         
10627         /*
10628         this.list.swallowEvent('mousewheel');
10629         this.assetHeight = 0;
10630
10631         if(this.title){
10632             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
10633             this.assetHeight += this.header.getHeight();
10634         }
10635
10636         this.innerList = this.list.createChild({cls:cls+'-inner'});
10637         this.innerList.on('mouseover', this.onViewOver, this);
10638         this.innerList.on('mousemove', this.onViewMove, this);
10639         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
10640         
10641         if(this.allowBlank && !this.pageSize && !this.disableClear){
10642             this.footer = this.list.createChild({cls:cls+'-ft'});
10643             this.pageTb = new Roo.Toolbar(this.footer);
10644            
10645         }
10646         if(this.pageSize){
10647             this.footer = this.list.createChild({cls:cls+'-ft'});
10648             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
10649                     {pageSize: this.pageSize});
10650             
10651         }
10652         
10653         if (this.pageTb && this.allowBlank && !this.disableClear) {
10654             var _this = this;
10655             this.pageTb.add(new Roo.Toolbar.Fill(), {
10656                 cls: 'x-btn-icon x-btn-clear',
10657                 text: '&#160;',
10658                 handler: function()
10659                 {
10660                     _this.collapse();
10661                     _this.clearValue();
10662                     _this.onSelect(false, -1);
10663                 }
10664             });
10665         }
10666         if (this.footer) {
10667             this.assetHeight += this.footer.getHeight();
10668         }
10669         */
10670             
10671         if(!this.tpl){
10672             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
10673         }
10674
10675         this.view = new Roo.View(this.list, this.tpl, {
10676             singleSelect:true, store: this.store, selectedClass: this.selectedClass
10677         });
10678         //this.view.wrapEl.setDisplayed(false);
10679         this.view.on('click', this.onViewClick, this);
10680         
10681         
10682         
10683         this.store.on('beforeload', this.onBeforeLoad, this);
10684         this.store.on('load', this.onLoad, this);
10685         this.store.on('loadexception', this.onLoadException, this);
10686         /*
10687         if(this.resizable){
10688             this.resizer = new Roo.Resizable(this.list,  {
10689                pinned:true, handles:'se'
10690             });
10691             this.resizer.on('resize', function(r, w, h){
10692                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
10693                 this.listWidth = w;
10694                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
10695                 this.restrictHeight();
10696             }, this);
10697             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
10698         }
10699         */
10700         if(!this.editable){
10701             this.editable = true;
10702             this.setEditable(false);
10703         }
10704         
10705         /*
10706         
10707         if (typeof(this.events.add.listeners) != 'undefined') {
10708             
10709             this.addicon = this.wrap.createChild(
10710                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
10711        
10712             this.addicon.on('click', function(e) {
10713                 this.fireEvent('add', this);
10714             }, this);
10715         }
10716         if (typeof(this.events.edit.listeners) != 'undefined') {
10717             
10718             this.editicon = this.wrap.createChild(
10719                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
10720             if (this.addicon) {
10721                 this.editicon.setStyle('margin-left', '40px');
10722             }
10723             this.editicon.on('click', function(e) {
10724                 
10725                 // we fire even  if inothing is selected..
10726                 this.fireEvent('edit', this, this.lastData );
10727                 
10728             }, this);
10729         }
10730         */
10731         
10732         this.keyNav = new Roo.KeyNav(this.inputEl(), {
10733             "up" : function(e){
10734                 this.inKeyMode = true;
10735                 this.selectPrev();
10736             },
10737
10738             "down" : function(e){
10739                 if(!this.isExpanded()){
10740                     this.onTriggerClick();
10741                 }else{
10742                     this.inKeyMode = true;
10743                     this.selectNext();
10744                 }
10745             },
10746
10747             "enter" : function(e){
10748 //                this.onViewClick();
10749                 //return true;
10750                 this.collapse();
10751                 
10752                 if(this.fireEvent("specialkey", this, e)){
10753                     this.onViewClick(false);
10754                 }
10755                 
10756                 return true;
10757             },
10758
10759             "esc" : function(e){
10760                 this.collapse();
10761             },
10762
10763             "tab" : function(e){
10764                 this.collapse();
10765                 
10766                 if(this.fireEvent("specialkey", this, e)){
10767                     this.onViewClick(false);
10768                 }
10769                 
10770                 return true;
10771             },
10772
10773             scope : this,
10774
10775             doRelay : function(foo, bar, hname){
10776                 if(hname == 'down' || this.scope.isExpanded()){
10777                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
10778                 }
10779                 return true;
10780             },
10781
10782             forceKeyDown: true
10783         });
10784         
10785         
10786         this.queryDelay = Math.max(this.queryDelay || 10,
10787                 this.mode == 'local' ? 10 : 250);
10788         
10789         
10790         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
10791         
10792         if(this.typeAhead){
10793             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
10794         }
10795         if(this.editable !== false){
10796             this.inputEl().on("keyup", this.onKeyUp, this);
10797         }
10798         if(this.forceSelection){
10799             this.inputEl().on('blur', this.doForce, this);
10800         }
10801         
10802         if(this.multiple){
10803             this.choices = this.el.select('ul.select2-choices', true).first();
10804             this.searchField = this.el.select('ul li.select2-search-field', true).first();
10805         }
10806     },
10807     
10808     initTickableEvents: function()
10809     {   
10810         this.createList();
10811         
10812         if(this.hiddenName){
10813             
10814             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10815             
10816             this.hiddenField.dom.value =
10817                 this.hiddenValue !== undefined ? this.hiddenValue :
10818                 this.value !== undefined ? this.value : '';
10819
10820             // prevent input submission
10821             this.el.dom.removeAttribute('name');
10822             this.hiddenField.dom.setAttribute('name', this.hiddenName);
10823              
10824              
10825         }
10826         
10827 //        this.list = this.el.select('ul.dropdown-menu',true).first();
10828         
10829         this.choices = this.el.select('ul.select2-choices', true).first();
10830         this.searchField = this.el.select('ul li.select2-search-field', true).first();
10831         if(this.triggerList){
10832             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
10833         }
10834          
10835         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
10836         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
10837         
10838         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
10839         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
10840         
10841         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
10842         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
10843         
10844         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
10845         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
10846         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
10847         
10848         this.okBtn.hide();
10849         this.cancelBtn.hide();
10850         
10851         var _this = this;
10852         
10853         (function(){
10854             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
10855             _this.list.setWidth(lw);
10856         }).defer(100);
10857         
10858         this.list.on('mouseover', this.onViewOver, this);
10859         this.list.on('mousemove', this.onViewMove, this);
10860         
10861         this.list.on('scroll', this.onViewScroll, this);
10862         
10863         if(!this.tpl){
10864             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>';
10865         }
10866
10867         this.view = new Roo.View(this.list, this.tpl, {
10868             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
10869         });
10870         
10871         //this.view.wrapEl.setDisplayed(false);
10872         this.view.on('click', this.onViewClick, this);
10873         
10874         
10875         
10876         this.store.on('beforeload', this.onBeforeLoad, this);
10877         this.store.on('load', this.onLoad, this);
10878         this.store.on('loadexception', this.onLoadException, this);
10879         
10880 //        this.keyNav = new Roo.KeyNav(this.inputEl(), {
10881 //            "up" : function(e){
10882 //                this.inKeyMode = true;
10883 //                this.selectPrev();
10884 //            },
10885 //
10886 //            "down" : function(e){
10887 //                if(!this.isExpanded()){
10888 //                    this.onTriggerClick();
10889 //                }else{
10890 //                    this.inKeyMode = true;
10891 //                    this.selectNext();
10892 //                }
10893 //            },
10894 //
10895 //            "enter" : function(e){
10896 ////                this.onViewClick();
10897 //                //return true;
10898 //                this.collapse();
10899 //                
10900 //                if(this.fireEvent("specialkey", this, e)){
10901 //                    this.onViewClick(false);
10902 //                }
10903 //                
10904 //                return true;
10905 //            },
10906 //
10907 //            "esc" : function(e){
10908 //                this.collapse();
10909 //            },
10910 //
10911 //            "tab" : function(e){
10912 //                this.collapse();
10913 //                
10914 //                if(this.fireEvent("specialkey", this, e)){
10915 //                    this.onViewClick(false);
10916 //                }
10917 //                
10918 //                return true;
10919 //            },
10920 //
10921 //            scope : this,
10922 //
10923 //            doRelay : function(foo, bar, hname){
10924 //                if(hname == 'down' || this.scope.isExpanded()){
10925 //                   return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
10926 //                }
10927 //                return true;
10928 //            },
10929 //
10930 //            forceKeyDown: true
10931 //        });
10932         
10933         
10934         this.queryDelay = Math.max(this.queryDelay || 10,
10935                 this.mode == 'local' ? 10 : 250);
10936         
10937         
10938         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
10939         
10940         if(this.typeAhead){
10941             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
10942         }
10943     },
10944
10945     onDestroy : function(){
10946         if(this.view){
10947             this.view.setStore(null);
10948             this.view.el.removeAllListeners();
10949             this.view.el.remove();
10950             this.view.purgeListeners();
10951         }
10952         if(this.list){
10953             this.list.dom.innerHTML  = '';
10954         }
10955         
10956         if(this.store){
10957             this.store.un('beforeload', this.onBeforeLoad, this);
10958             this.store.un('load', this.onLoad, this);
10959             this.store.un('loadexception', this.onLoadException, this);
10960         }
10961         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
10962     },
10963
10964     // private
10965     fireKey : function(e){
10966         if(e.isNavKeyPress() && !this.list.isVisible()){
10967             this.fireEvent("specialkey", this, e);
10968         }
10969     },
10970
10971     // private
10972     onResize: function(w, h){
10973 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
10974 //        
10975 //        if(typeof w != 'number'){
10976 //            // we do not handle it!?!?
10977 //            return;
10978 //        }
10979 //        var tw = this.trigger.getWidth();
10980 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
10981 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
10982 //        var x = w - tw;
10983 //        this.inputEl().setWidth( this.adjustWidth('input', x));
10984 //            
10985 //        //this.trigger.setStyle('left', x+'px');
10986 //        
10987 //        if(this.list && this.listWidth === undefined){
10988 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
10989 //            this.list.setWidth(lw);
10990 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
10991 //        }
10992         
10993     
10994         
10995     },
10996
10997     /**
10998      * Allow or prevent the user from directly editing the field text.  If false is passed,
10999      * the user will only be able to select from the items defined in the dropdown list.  This method
11000      * is the runtime equivalent of setting the 'editable' config option at config time.
11001      * @param {Boolean} value True to allow the user to directly edit the field text
11002      */
11003     setEditable : function(value){
11004         if(value == this.editable){
11005             return;
11006         }
11007         this.editable = value;
11008         if(!value){
11009             this.inputEl().dom.setAttribute('readOnly', true);
11010             this.inputEl().on('mousedown', this.onTriggerClick,  this);
11011             this.inputEl().addClass('x-combo-noedit');
11012         }else{
11013             this.inputEl().dom.setAttribute('readOnly', false);
11014             this.inputEl().un('mousedown', this.onTriggerClick,  this);
11015             this.inputEl().removeClass('x-combo-noedit');
11016         }
11017     },
11018
11019     // private
11020     
11021     onBeforeLoad : function(combo,opts){
11022         if(!this.hasFocus){
11023             return;
11024         }
11025          if (!opts.add) {
11026             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
11027          }
11028 //        this.restrictHeight();
11029         this.selectedIndex = -1;
11030     },
11031
11032     // private
11033     onLoad : function(){
11034         
11035         this.hasQuery = false;
11036         
11037         if(!this.hasFocus){
11038             return;
11039         }
11040         
11041         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11042             this.loading.hide();
11043         }
11044         
11045         if(this.store.getCount() > 0){
11046             this.expand();
11047 //            this.restrictHeight();
11048             if(this.lastQuery == this.allQuery){
11049                 if(this.editable && !this.tickable){
11050                     this.inputEl().dom.select();
11051                 }
11052                 
11053                 if(!this.selectByValue(this.value, true) && this.autoFocus && (typeof(this.store.lastOptions.add) == 'undefined' || this.store.lastOptions.add != true)){
11054                     this.select(0, true);
11055                 }
11056             }else{
11057                 if(this.autoFocus){
11058                     this.selectNext();
11059                 }
11060                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
11061                     this.taTask.delay(this.typeAheadDelay);
11062                 }
11063             }
11064         }else{
11065             this.onEmptyResults();
11066         }
11067         
11068         //this.el.focus();
11069     },
11070     // private
11071     onLoadException : function()
11072     {
11073         this.hasQuery = false;
11074         
11075         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11076             this.loading.hide();
11077         }
11078         
11079         this.collapse();
11080         Roo.log(this.store.reader.jsonData);
11081         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
11082             // fixme
11083             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
11084         }
11085         
11086         
11087     },
11088     // private
11089     onTypeAhead : function(){
11090         if(this.store.getCount() > 0){
11091             var r = this.store.getAt(0);
11092             var newValue = r.data[this.displayField];
11093             var len = newValue.length;
11094             var selStart = this.getRawValue().length;
11095             
11096             if(selStart != len){
11097                 this.setRawValue(newValue);
11098                 this.selectText(selStart, newValue.length);
11099             }
11100         }
11101     },
11102
11103     // private
11104     onSelect : function(record, index){
11105         
11106         if(this.fireEvent('beforeselect', this, record, index) !== false){
11107         
11108             this.setFromData(index > -1 ? record.data : false);
11109             
11110             this.collapse();
11111             this.fireEvent('select', this, record, index);
11112         }
11113     },
11114
11115     /**
11116      * Returns the currently selected field value or empty string if no value is set.
11117      * @return {String} value The selected value
11118      */
11119     getValue : function(){
11120         
11121         if(this.multiple){
11122             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
11123         }
11124         
11125         if(this.valueField){
11126             return typeof this.value != 'undefined' ? this.value : '';
11127         }else{
11128             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
11129         }
11130     },
11131
11132     /**
11133      * Clears any text/value currently set in the field
11134      */
11135     clearValue : function(){
11136         if(this.hiddenField){
11137             this.hiddenField.dom.value = '';
11138         }
11139         this.value = '';
11140         this.setRawValue('');
11141         this.lastSelectionText = '';
11142         
11143     },
11144
11145     /**
11146      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
11147      * will be displayed in the field.  If the value does not match the data value of an existing item,
11148      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
11149      * Otherwise the field will be blank (although the value will still be set).
11150      * @param {String} value The value to match
11151      */
11152     setValue : function(v){
11153         if(this.multiple){
11154             this.syncValue();
11155             return;
11156         }
11157         
11158         var text = v;
11159         if(this.valueField){
11160             var r = this.findRecord(this.valueField, v);
11161             if(r){
11162                 text = r.data[this.displayField];
11163             }else if(this.valueNotFoundText !== undefined){
11164                 text = this.valueNotFoundText;
11165             }
11166         }
11167         this.lastSelectionText = text;
11168         if(this.hiddenField){
11169             this.hiddenField.dom.value = v;
11170         }
11171         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
11172         this.value = v;
11173     },
11174     /**
11175      * @property {Object} the last set data for the element
11176      */
11177     
11178     lastData : false,
11179     /**
11180      * Sets the value of the field based on a object which is related to the record format for the store.
11181      * @param {Object} value the value to set as. or false on reset?
11182      */
11183     setFromData : function(o){
11184         
11185         if(this.multiple){
11186             if(typeof o.display_name !== 'string'){
11187                 for(var i=0;i<o.display_name.length;i++){
11188                     this.addItem({'id':o.id[i],'display_name':o.display_name[i]});
11189                 }
11190                 return;
11191             }
11192             this.addItem(o);
11193             return;
11194         }
11195             
11196         var dv = ''; // display value
11197         var vv = ''; // value value..
11198         this.lastData = o;
11199         if (this.displayField) {
11200             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11201         } else {
11202             // this is an error condition!!!
11203             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
11204         }
11205         
11206         if(this.valueField){
11207             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
11208         }
11209         
11210         if(this.hiddenField){
11211             this.hiddenField.dom.value = vv;
11212             
11213             this.lastSelectionText = dv;
11214             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11215             this.value = vv;
11216             return;
11217         }
11218         // no hidden field.. - we store the value in 'value', but still display
11219         // display field!!!!
11220         this.lastSelectionText = dv;
11221         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11222         this.value = vv;
11223         
11224         
11225     },
11226     // private
11227     reset : function(){
11228         // overridden so that last data is reset..
11229         this.setValue(this.originalValue);
11230         this.clearInvalid();
11231         this.lastData = false;
11232         if (this.view) {
11233             this.view.clearSelections();
11234         }
11235     },
11236     // private
11237     findRecord : function(prop, value){
11238         var record;
11239         if(this.store.getCount() > 0){
11240             this.store.each(function(r){
11241                 if(r.data[prop] == value){
11242                     record = r;
11243                     return false;
11244                 }
11245                 return true;
11246             });
11247         }
11248         return record;
11249     },
11250     
11251     getName: function()
11252     {
11253         // returns hidden if it's set..
11254         if (!this.rendered) {return ''};
11255         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
11256         
11257     },
11258     // private
11259     onViewMove : function(e, t){
11260         this.inKeyMode = false;
11261     },
11262
11263     // private
11264     onViewOver : function(e, t){
11265         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
11266             return;
11267         }
11268         var item = this.view.findItemFromChild(t);
11269         
11270         if(item){
11271             var index = this.view.indexOf(item);
11272             this.select(index, false);
11273         }
11274     },
11275
11276     // private
11277     onViewClick : function(view, doFocus, el, e)
11278     {
11279         var index = this.view.getSelectedIndexes()[0];
11280         
11281         var r = this.store.getAt(index);
11282         
11283         if(this.tickable){
11284             
11285             if(e.getTarget().nodeName.toLowerCase() != 'input'){
11286                 return;
11287             }
11288             
11289             var rm = false;
11290             var _this = this;
11291             
11292             Roo.each(this.tickItems, function(v,k){
11293                 
11294                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
11295                     _this.tickItems.splice(k, 1);
11296                     rm = true;
11297                     return;
11298                 }
11299             })
11300             
11301             if(rm){
11302                 return;
11303             }
11304             
11305             this.tickItems.push(r.data);
11306             return;
11307         }
11308         
11309         if(r){
11310             this.onSelect(r, index);
11311         }
11312         if(doFocus !== false && !this.blockFocus){
11313             this.inputEl().focus();
11314         }
11315     },
11316
11317     // private
11318     restrictHeight : function(){
11319         //this.innerList.dom.style.height = '';
11320         //var inner = this.innerList.dom;
11321         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
11322         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
11323         //this.list.beginUpdate();
11324         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
11325         this.list.alignTo(this.inputEl(), this.listAlign);
11326         this.list.alignTo(this.inputEl(), this.listAlign);
11327         //this.list.endUpdate();
11328     },
11329
11330     // private
11331     onEmptyResults : function(){
11332         this.collapse();
11333     },
11334
11335     /**
11336      * Returns true if the dropdown list is expanded, else false.
11337      */
11338     isExpanded : function(){
11339         return this.list.isVisible();
11340     },
11341
11342     /**
11343      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
11344      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11345      * @param {String} value The data value of the item to select
11346      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11347      * selected item if it is not currently in view (defaults to true)
11348      * @return {Boolean} True if the value matched an item in the list, else false
11349      */
11350     selectByValue : function(v, scrollIntoView){
11351         if(v !== undefined && v !== null){
11352             var r = this.findRecord(this.valueField || this.displayField, v);
11353             if(r){
11354                 this.select(this.store.indexOf(r), scrollIntoView);
11355                 return true;
11356             }
11357         }
11358         return false;
11359     },
11360
11361     /**
11362      * Select an item in the dropdown list by its numeric index in the list. 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 {Number} index The zero-based index of the list 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      */
11368     select : function(index, scrollIntoView){
11369         this.selectedIndex = index;
11370         this.view.select(index);
11371         if(scrollIntoView !== false){
11372             var el = this.view.getNode(index);
11373             if(el && !this.multiple && !this.tickable){
11374                 this.list.scrollChildIntoView(el, false);
11375             }
11376         }
11377     },
11378
11379     // private
11380     selectNext : function(){
11381         var ct = this.store.getCount();
11382         if(ct > 0){
11383             if(this.selectedIndex == -1){
11384                 this.select(0);
11385             }else if(this.selectedIndex < ct-1){
11386                 this.select(this.selectedIndex+1);
11387             }
11388         }
11389     },
11390
11391     // private
11392     selectPrev : function(){
11393         var ct = this.store.getCount();
11394         if(ct > 0){
11395             if(this.selectedIndex == -1){
11396                 this.select(0);
11397             }else if(this.selectedIndex != 0){
11398                 this.select(this.selectedIndex-1);
11399             }
11400         }
11401     },
11402
11403     // private
11404     onKeyUp : function(e){
11405         if(this.editable !== false && !e.isSpecialKey()){
11406             this.lastKey = e.getKey();
11407             this.dqTask.delay(this.queryDelay);
11408         }
11409     },
11410
11411     // private
11412     validateBlur : function(){
11413         return !this.list || !this.list.isVisible();   
11414     },
11415
11416     // private
11417     initQuery : function(){
11418         this.doQuery(this.getRawValue());
11419     },
11420
11421     // private
11422     doForce : function(){
11423         if(this.inputEl().dom.value.length > 0){
11424             this.inputEl().dom.value =
11425                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
11426              
11427         }
11428     },
11429
11430     /**
11431      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
11432      * query allowing the query action to be canceled if needed.
11433      * @param {String} query The SQL query to execute
11434      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
11435      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
11436      * saved in the current store (defaults to false)
11437      */
11438     doQuery : function(q, forceAll){
11439         
11440         if(q === undefined || q === null){
11441             q = '';
11442         }
11443         var qe = {
11444             query: q,
11445             forceAll: forceAll,
11446             combo: this,
11447             cancel:false
11448         };
11449         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
11450             return false;
11451         }
11452         q = qe.query;
11453         
11454         forceAll = qe.forceAll;
11455         if(forceAll === true || (q.length >= this.minChars)){
11456             
11457             this.hasQuery = true;
11458             
11459             if(this.lastQuery != q || this.alwaysQuery){
11460                 this.lastQuery = q;
11461                 if(this.mode == 'local'){
11462                     this.selectedIndex = -1;
11463                     if(forceAll){
11464                         this.store.clearFilter();
11465                     }else{
11466                         this.store.filter(this.displayField, q);
11467                     }
11468                     this.onLoad();
11469                 }else{
11470                     this.store.baseParams[this.queryParam] = q;
11471                     
11472                     var options = {params : this.getParams(q)};
11473                     
11474                     if(this.loadNext){
11475                         options.add = true;
11476                         options.params.start = this.page * this.pageSize;
11477                     }
11478                     
11479                     this.store.load(options);
11480                     /*
11481                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
11482                      *  we should expand the list on onLoad
11483                      *  so command out it
11484                      */
11485 //                    this.expand();
11486                 }
11487             }else{
11488                 this.selectedIndex = -1;
11489                 this.onLoad();   
11490             }
11491         }
11492         
11493         this.loadNext = false;
11494     },
11495
11496     // private
11497     getParams : function(q){
11498         var p = {};
11499         //p[this.queryParam] = q;
11500         
11501         if(this.pageSize){
11502             p.start = 0;
11503             p.limit = this.pageSize;
11504         }
11505         return p;
11506     },
11507
11508     /**
11509      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
11510      */
11511     collapse : function(){
11512         if(!this.isExpanded()){
11513             return;
11514         }
11515         
11516         this.list.hide();
11517         
11518         if(this.tickable){
11519             this.okBtn.hide();
11520             this.cancelBtn.hide();
11521             this.trigger.show();
11522         }
11523         
11524         Roo.get(document).un('mousedown', this.collapseIf, this);
11525         Roo.get(document).un('mousewheel', this.collapseIf, this);
11526         if (!this.editable) {
11527             Roo.get(document).un('keydown', this.listKeyPress, this);
11528         }
11529         this.fireEvent('collapse', this);
11530     },
11531
11532     // private
11533     collapseIf : function(e){
11534         var in_combo  = e.within(this.el);
11535         var in_list =  e.within(this.list);
11536         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
11537         
11538         if (in_combo || in_list || is_list) {
11539             //e.stopPropagation();
11540             return;
11541         }
11542         
11543         if(this.tickable){
11544             this.onTickableFooterButtonClick(e, false, false);
11545         }
11546
11547         this.collapse();
11548         
11549     },
11550
11551     /**
11552      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
11553      */
11554     expand : function(){
11555        
11556         if(this.isExpanded() || !this.hasFocus){
11557             return;
11558         }
11559         
11560         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
11561         this.list.setWidth(lw);
11562         
11563         
11564          Roo.log('expand');
11565         
11566         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
11567         this.list.setWidth(lw);
11568             
11569         this.list.show();
11570         
11571         this.restrictHeight();
11572         
11573         if(this.tickable){
11574             
11575             this.tickItems = Roo.apply([], this.item);
11576             
11577             this.okBtn.show();
11578             this.cancelBtn.show();
11579             this.trigger.hide();
11580             
11581         }
11582         
11583         Roo.get(document).on('mousedown', this.collapseIf, this);
11584         Roo.get(document).on('mousewheel', this.collapseIf, this);
11585         if (!this.editable) {
11586             Roo.get(document).on('keydown', this.listKeyPress, this);
11587         }
11588         
11589         this.fireEvent('expand', this);
11590     },
11591
11592     // private
11593     // Implements the default empty TriggerField.onTriggerClick function
11594     onTriggerClick : function(e)
11595     {
11596         Roo.log('trigger click');
11597         
11598         if(this.disabled || !this.triggerList){
11599             return;
11600         }
11601         
11602         this.page = 0;
11603         this.loadNext = false;
11604         
11605         if(this.isExpanded()){
11606             this.collapse();
11607             if (!this.blockFocus) {
11608                 this.inputEl().focus();
11609             }
11610             
11611         }else {
11612             this.hasFocus = true;
11613             if(this.triggerAction == 'all') {
11614                 this.doQuery(this.allQuery, true);
11615             } else {
11616                 this.doQuery(this.getRawValue());
11617             }
11618             if (!this.blockFocus) {
11619                 this.inputEl().focus();
11620             }
11621         }
11622     },
11623     
11624     onTickableTriggerClick : function(e)
11625     {
11626         if(this.disabled){
11627             return;
11628         }
11629         
11630         this.page = 0;
11631         this.loadNext = false;
11632         this.hasFocus = true;
11633         
11634         if(this.triggerAction == 'all') {
11635             this.doQuery(this.allQuery, true);
11636         } else {
11637             this.doQuery(this.getRawValue());
11638         }
11639     },
11640     
11641     onSearchFieldClick : function(e)
11642     {
11643         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
11644             return;
11645         }
11646         
11647         this.page = 0;
11648         this.loadNext = false;
11649         this.hasFocus = true;
11650         
11651         if(this.triggerAction == 'all') {
11652             this.doQuery(this.allQuery, true);
11653         } else {
11654             this.doQuery(this.getRawValue());
11655         }
11656     },
11657     
11658     listKeyPress : function(e)
11659     {
11660         //Roo.log('listkeypress');
11661         // scroll to first matching element based on key pres..
11662         if (e.isSpecialKey()) {
11663             return false;
11664         }
11665         var k = String.fromCharCode(e.getKey()).toUpperCase();
11666         //Roo.log(k);
11667         var match  = false;
11668         var csel = this.view.getSelectedNodes();
11669         var cselitem = false;
11670         if (csel.length) {
11671             var ix = this.view.indexOf(csel[0]);
11672             cselitem  = this.store.getAt(ix);
11673             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
11674                 cselitem = false;
11675             }
11676             
11677         }
11678         
11679         this.store.each(function(v) { 
11680             if (cselitem) {
11681                 // start at existing selection.
11682                 if (cselitem.id == v.id) {
11683                     cselitem = false;
11684                 }
11685                 return true;
11686             }
11687                 
11688             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
11689                 match = this.store.indexOf(v);
11690                 return false;
11691             }
11692             return true;
11693         }, this);
11694         
11695         if (match === false) {
11696             return true; // no more action?
11697         }
11698         // scroll to?
11699         this.view.select(match);
11700         var sn = Roo.get(this.view.getSelectedNodes()[0])
11701         //sn.scrollIntoView(sn.dom.parentNode, false);
11702     },
11703     
11704     onViewScroll : function(e, t){
11705         
11706         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){
11707             return;
11708         }
11709         
11710         this.hasQuery = true;
11711         
11712         this.loading = this.list.select('.loading', true).first();
11713         
11714         if(this.loading === null){
11715             this.list.createChild({
11716                 tag: 'div',
11717                 cls: 'loading select2-more-results select2-active',
11718                 html: 'Loading more results...'
11719             })
11720             
11721             this.loading = this.list.select('.loading', true).first();
11722             
11723             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
11724             
11725             this.loading.hide();
11726         }
11727         
11728         this.loading.show();
11729         
11730         var _combo = this;
11731         
11732         this.page++;
11733         this.loadNext = true;
11734         
11735         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
11736         
11737         return;
11738     },
11739     
11740     addItem : function(o)
11741     {   
11742         var dv = ''; // display value
11743         
11744         if (this.displayField) {
11745             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11746         } else {
11747             // this is an error condition!!!
11748             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
11749         }
11750         
11751         if(!dv.length){
11752             return;
11753         }
11754         
11755         var choice = this.choices.createChild({
11756             tag: 'li',
11757             cls: 'select2-search-choice',
11758             cn: [
11759                 {
11760                     tag: 'div',
11761                     html: dv
11762                 },
11763                 {
11764                     tag: 'a',
11765                     href: '#',
11766                     cls: 'select2-search-choice-close',
11767                     tabindex: '-1'
11768                 }
11769             ]
11770             
11771         }, this.searchField);
11772         
11773         var close = choice.select('a.select2-search-choice-close', true).first()
11774         
11775         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
11776         
11777         this.item.push(o);
11778         
11779         this.lastData = o;
11780         
11781         this.syncValue();
11782         
11783         this.inputEl().dom.value = '';
11784         
11785     },
11786     
11787     onRemoveItem : function(e, _self, o)
11788     {
11789         e.preventDefault();
11790         var index = this.item.indexOf(o.data) * 1;
11791         
11792         if( index < 0){
11793             Roo.log('not this item?!');
11794             return;
11795         }
11796         
11797         this.item.splice(index, 1);
11798         o.item.remove();
11799         
11800         this.syncValue();
11801         
11802         this.fireEvent('remove', this, e);
11803         
11804     },
11805     
11806     syncValue : function()
11807     {
11808         if(!this.item.length){
11809             this.clearValue();
11810             return;
11811         }
11812             
11813         var value = [];
11814         var _this = this;
11815         Roo.each(this.item, function(i){
11816             if(_this.valueField){
11817                 value.push(i[_this.valueField]);
11818                 return;
11819             }
11820
11821             value.push(i);
11822         });
11823
11824         this.value = value.join(',');
11825
11826         if(this.hiddenField){
11827             this.hiddenField.dom.value = this.value;
11828         }
11829     },
11830     
11831     clearItem : function()
11832     {
11833         if(!this.multiple){
11834             return;
11835         }
11836         
11837         this.item = [];
11838         
11839         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
11840            c.remove();
11841         });
11842         
11843         this.syncValue();
11844     },
11845     
11846     inputEl: function ()
11847     {
11848         if(this.tickable){
11849             return this.searchField;
11850         }
11851         return this.el.select('input.form-control',true).first();
11852     },
11853     
11854     
11855     onTickableFooterButtonClick : function(e, btn, el)
11856     {
11857         e.preventDefault();
11858         
11859         if(btn && btn.name == 'cancel'){
11860             this.tickItems = Roo.apply([], this.item);
11861             this.collapse();
11862             return;
11863         }
11864         
11865         this.clearItem();
11866         
11867         var _this = this;
11868         
11869         Roo.each(this.tickItems, function(o){
11870             _this.addItem(o);
11871         });
11872         
11873         this.collapse();
11874         
11875     }
11876     
11877     
11878
11879     /** 
11880     * @cfg {Boolean} grow 
11881     * @hide 
11882     */
11883     /** 
11884     * @cfg {Number} growMin 
11885     * @hide 
11886     */
11887     /** 
11888     * @cfg {Number} growMax 
11889     * @hide 
11890     */
11891     /**
11892      * @hide
11893      * @method autoSize
11894      */
11895 });
11896 /*
11897  * Based on:
11898  * Ext JS Library 1.1.1
11899  * Copyright(c) 2006-2007, Ext JS, LLC.
11900  *
11901  * Originally Released Under LGPL - original licence link has changed is not relivant.
11902  *
11903  * Fork - LGPL
11904  * <script type="text/javascript">
11905  */
11906
11907 /**
11908  * @class Roo.View
11909  * @extends Roo.util.Observable
11910  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
11911  * This class also supports single and multi selection modes. <br>
11912  * Create a data model bound view:
11913  <pre><code>
11914  var store = new Roo.data.Store(...);
11915
11916  var view = new Roo.View({
11917     el : "my-element",
11918     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
11919  
11920     singleSelect: true,
11921     selectedClass: "ydataview-selected",
11922     store: store
11923  });
11924
11925  // listen for node click?
11926  view.on("click", function(vw, index, node, e){
11927  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
11928  });
11929
11930  // load XML data
11931  dataModel.load("foobar.xml");
11932  </code></pre>
11933  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
11934  * <br><br>
11935  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
11936  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
11937  * 
11938  * Note: old style constructor is still suported (container, template, config)
11939  * 
11940  * @constructor
11941  * Create a new View
11942  * @param {Object} config The config object
11943  * 
11944  */
11945 Roo.View = function(config, depreciated_tpl, depreciated_config){
11946     
11947     this.parent = false;
11948     
11949     if (typeof(depreciated_tpl) == 'undefined') {
11950         // new way.. - universal constructor.
11951         Roo.apply(this, config);
11952         this.el  = Roo.get(this.el);
11953     } else {
11954         // old format..
11955         this.el  = Roo.get(config);
11956         this.tpl = depreciated_tpl;
11957         Roo.apply(this, depreciated_config);
11958     }
11959     this.wrapEl  = this.el.wrap().wrap();
11960     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
11961     
11962     
11963     if(typeof(this.tpl) == "string"){
11964         this.tpl = new Roo.Template(this.tpl);
11965     } else {
11966         // support xtype ctors..
11967         this.tpl = new Roo.factory(this.tpl, Roo);
11968     }
11969     
11970     
11971     this.tpl.compile();
11972     
11973     /** @private */
11974     this.addEvents({
11975         /**
11976          * @event beforeclick
11977          * Fires before a click is processed. Returns false to cancel the default action.
11978          * @param {Roo.View} this
11979          * @param {Number} index The index of the target node
11980          * @param {HTMLElement} node The target node
11981          * @param {Roo.EventObject} e The raw event object
11982          */
11983             "beforeclick" : true,
11984         /**
11985          * @event click
11986          * Fires when a template node is clicked.
11987          * @param {Roo.View} this
11988          * @param {Number} index The index of the target node
11989          * @param {HTMLElement} node The target node
11990          * @param {Roo.EventObject} e The raw event object
11991          */
11992             "click" : true,
11993         /**
11994          * @event dblclick
11995          * Fires when a template node is double clicked.
11996          * @param {Roo.View} this
11997          * @param {Number} index The index of the target node
11998          * @param {HTMLElement} node The target node
11999          * @param {Roo.EventObject} e The raw event object
12000          */
12001             "dblclick" : true,
12002         /**
12003          * @event contextmenu
12004          * Fires when a template node is right clicked.
12005          * @param {Roo.View} this
12006          * @param {Number} index The index of the target node
12007          * @param {HTMLElement} node The target node
12008          * @param {Roo.EventObject} e The raw event object
12009          */
12010             "contextmenu" : true,
12011         /**
12012          * @event selectionchange
12013          * Fires when the selected nodes change.
12014          * @param {Roo.View} this
12015          * @param {Array} selections Array of the selected nodes
12016          */
12017             "selectionchange" : true,
12018     
12019         /**
12020          * @event beforeselect
12021          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
12022          * @param {Roo.View} this
12023          * @param {HTMLElement} node The node to be selected
12024          * @param {Array} selections Array of currently selected nodes
12025          */
12026             "beforeselect" : true,
12027         /**
12028          * @event preparedata
12029          * Fires on every row to render, to allow you to change the data.
12030          * @param {Roo.View} this
12031          * @param {Object} data to be rendered (change this)
12032          */
12033           "preparedata" : true
12034           
12035           
12036         });
12037
12038
12039
12040     this.el.on({
12041         "click": this.onClick,
12042         "dblclick": this.onDblClick,
12043         "contextmenu": this.onContextMenu,
12044         scope:this
12045     });
12046
12047     this.selections = [];
12048     this.nodes = [];
12049     this.cmp = new Roo.CompositeElementLite([]);
12050     if(this.store){
12051         this.store = Roo.factory(this.store, Roo.data);
12052         this.setStore(this.store, true);
12053     }
12054     
12055     if ( this.footer && this.footer.xtype) {
12056            
12057          var fctr = this.wrapEl.appendChild(document.createElement("div"));
12058         
12059         this.footer.dataSource = this.store
12060         this.footer.container = fctr;
12061         this.footer = Roo.factory(this.footer, Roo);
12062         fctr.insertFirst(this.el);
12063         
12064         // this is a bit insane - as the paging toolbar seems to detach the el..
12065 //        dom.parentNode.parentNode.parentNode
12066          // they get detached?
12067     }
12068     
12069     
12070     Roo.View.superclass.constructor.call(this);
12071     
12072     
12073 };
12074
12075 Roo.extend(Roo.View, Roo.util.Observable, {
12076     
12077      /**
12078      * @cfg {Roo.data.Store} store Data store to load data from.
12079      */
12080     store : false,
12081     
12082     /**
12083      * @cfg {String|Roo.Element} el The container element.
12084      */
12085     el : '',
12086     
12087     /**
12088      * @cfg {String|Roo.Template} tpl The template used by this View 
12089      */
12090     tpl : false,
12091     /**
12092      * @cfg {String} dataName the named area of the template to use as the data area
12093      *                          Works with domtemplates roo-name="name"
12094      */
12095     dataName: false,
12096     /**
12097      * @cfg {String} selectedClass The css class to add to selected nodes
12098      */
12099     selectedClass : "x-view-selected",
12100      /**
12101      * @cfg {String} emptyText The empty text to show when nothing is loaded.
12102      */
12103     emptyText : "",
12104     
12105     /**
12106      * @cfg {String} text to display on mask (default Loading)
12107      */
12108     mask : false,
12109     /**
12110      * @cfg {Boolean} multiSelect Allow multiple selection
12111      */
12112     multiSelect : false,
12113     /**
12114      * @cfg {Boolean} singleSelect Allow single selection
12115      */
12116     singleSelect:  false,
12117     
12118     /**
12119      * @cfg {Boolean} toggleSelect - selecting 
12120      */
12121     toggleSelect : false,
12122     
12123     /**
12124      * @cfg {Boolean} tickable - selecting 
12125      */
12126     tickable : false,
12127     
12128     /**
12129      * Returns the element this view is bound to.
12130      * @return {Roo.Element}
12131      */
12132     getEl : function(){
12133         return this.wrapEl;
12134     },
12135     
12136     
12137
12138     /**
12139      * Refreshes the view. - called by datachanged on the store. - do not call directly.
12140      */
12141     refresh : function(){
12142         Roo.log('refresh');
12143         var t = this.tpl;
12144         
12145         // if we are using something like 'domtemplate', then
12146         // the what gets used is:
12147         // t.applySubtemplate(NAME, data, wrapping data..)
12148         // the outer template then get' applied with
12149         //     the store 'extra data'
12150         // and the body get's added to the
12151         //      roo-name="data" node?
12152         //      <span class='roo-tpl-{name}'></span> ?????
12153         
12154         
12155         
12156         this.clearSelections();
12157         this.el.update("");
12158         var html = [];
12159         var records = this.store.getRange();
12160         if(records.length < 1) {
12161             
12162             // is this valid??  = should it render a template??
12163             
12164             this.el.update(this.emptyText);
12165             return;
12166         }
12167         var el = this.el;
12168         if (this.dataName) {
12169             this.el.update(t.apply(this.store.meta)); //????
12170             el = this.el.child('.roo-tpl-' + this.dataName);
12171         }
12172         
12173         for(var i = 0, len = records.length; i < len; i++){
12174             var data = this.prepareData(records[i].data, i, records[i]);
12175             this.fireEvent("preparedata", this, data, i, records[i]);
12176             
12177             var d = Roo.apply({}, data);
12178             
12179             if(this.tickable){
12180                 Roo.apply(d, {'roo-id' : Roo.id()});
12181                 
12182                 var _this = this;
12183             
12184                 Roo.each(this.parent.item, function(item){
12185                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
12186                         return;
12187                     }
12188                     Roo.apply(d, {'roo-data-checked' : 'checked'});
12189                 });
12190             }
12191             
12192             html[html.length] = Roo.util.Format.trim(
12193                 this.dataName ?
12194                     t.applySubtemplate(this.dataName, d, this.store.meta) :
12195                     t.apply(d)
12196             );
12197         }
12198         
12199         
12200         
12201         el.update(html.join(""));
12202         this.nodes = el.dom.childNodes;
12203         this.updateIndexes(0);
12204     },
12205     
12206
12207     /**
12208      * Function to override to reformat the data that is sent to
12209      * the template for each node.
12210      * DEPRICATED - use the preparedata event handler.
12211      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
12212      * a JSON object for an UpdateManager bound view).
12213      */
12214     prepareData : function(data, index, record)
12215     {
12216         this.fireEvent("preparedata", this, data, index, record);
12217         return data;
12218     },
12219
12220     onUpdate : function(ds, record){
12221          Roo.log('on update');   
12222         this.clearSelections();
12223         var index = this.store.indexOf(record);
12224         var n = this.nodes[index];
12225         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
12226         n.parentNode.removeChild(n);
12227         this.updateIndexes(index, index);
12228     },
12229
12230     
12231     
12232 // --------- FIXME     
12233     onAdd : function(ds, records, index)
12234     {
12235         Roo.log(['on Add', ds, records, index] );        
12236         this.clearSelections();
12237         if(this.nodes.length == 0){
12238             this.refresh();
12239             return;
12240         }
12241         var n = this.nodes[index];
12242         for(var i = 0, len = records.length; i < len; i++){
12243             var d = this.prepareData(records[i].data, i, records[i]);
12244             if(n){
12245                 this.tpl.insertBefore(n, d);
12246             }else{
12247                 
12248                 this.tpl.append(this.el, d);
12249             }
12250         }
12251         this.updateIndexes(index);
12252     },
12253
12254     onRemove : function(ds, record, index){
12255         Roo.log('onRemove');
12256         this.clearSelections();
12257         var el = this.dataName  ?
12258             this.el.child('.roo-tpl-' + this.dataName) :
12259             this.el; 
12260         
12261         el.dom.removeChild(this.nodes[index]);
12262         this.updateIndexes(index);
12263     },
12264
12265     /**
12266      * Refresh an individual node.
12267      * @param {Number} index
12268      */
12269     refreshNode : function(index){
12270         this.onUpdate(this.store, this.store.getAt(index));
12271     },
12272
12273     updateIndexes : function(startIndex, endIndex){
12274         var ns = this.nodes;
12275         startIndex = startIndex || 0;
12276         endIndex = endIndex || ns.length - 1;
12277         for(var i = startIndex; i <= endIndex; i++){
12278             ns[i].nodeIndex = i;
12279         }
12280     },
12281
12282     /**
12283      * Changes the data store this view uses and refresh the view.
12284      * @param {Store} store
12285      */
12286     setStore : function(store, initial){
12287         if(!initial && this.store){
12288             this.store.un("datachanged", this.refresh);
12289             this.store.un("add", this.onAdd);
12290             this.store.un("remove", this.onRemove);
12291             this.store.un("update", this.onUpdate);
12292             this.store.un("clear", this.refresh);
12293             this.store.un("beforeload", this.onBeforeLoad);
12294             this.store.un("load", this.onLoad);
12295             this.store.un("loadexception", this.onLoad);
12296         }
12297         if(store){
12298           
12299             store.on("datachanged", this.refresh, this);
12300             store.on("add", this.onAdd, this);
12301             store.on("remove", this.onRemove, this);
12302             store.on("update", this.onUpdate, this);
12303             store.on("clear", this.refresh, this);
12304             store.on("beforeload", this.onBeforeLoad, this);
12305             store.on("load", this.onLoad, this);
12306             store.on("loadexception", this.onLoad, this);
12307         }
12308         
12309         if(store){
12310             this.refresh();
12311         }
12312     },
12313     /**
12314      * onbeforeLoad - masks the loading area.
12315      *
12316      */
12317     onBeforeLoad : function(store,opts)
12318     {
12319          Roo.log('onBeforeLoad');   
12320         if (!opts.add) {
12321             this.el.update("");
12322         }
12323         this.el.mask(this.mask ? this.mask : "Loading" ); 
12324     },
12325     onLoad : function ()
12326     {
12327         this.el.unmask();
12328     },
12329     
12330
12331     /**
12332      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
12333      * @param {HTMLElement} node
12334      * @return {HTMLElement} The template node
12335      */
12336     findItemFromChild : function(node){
12337         var el = this.dataName  ?
12338             this.el.child('.roo-tpl-' + this.dataName,true) :
12339             this.el.dom; 
12340         
12341         if(!node || node.parentNode == el){
12342                     return node;
12343             }
12344             var p = node.parentNode;
12345             while(p && p != el){
12346             if(p.parentNode == el){
12347                 return p;
12348             }
12349             p = p.parentNode;
12350         }
12351             return null;
12352     },
12353
12354     /** @ignore */
12355     onClick : function(e){
12356         var item = this.findItemFromChild(e.getTarget());
12357         if(item){
12358             var index = this.indexOf(item);
12359             if(this.onItemClick(item, index, e) !== false){
12360                 this.fireEvent("click", this, index, item, e);
12361             }
12362         }else{
12363             this.clearSelections();
12364         }
12365     },
12366
12367     /** @ignore */
12368     onContextMenu : function(e){
12369         var item = this.findItemFromChild(e.getTarget());
12370         if(item){
12371             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
12372         }
12373     },
12374
12375     /** @ignore */
12376     onDblClick : function(e){
12377         var item = this.findItemFromChild(e.getTarget());
12378         if(item){
12379             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
12380         }
12381     },
12382
12383     onItemClick : function(item, index, e)
12384     {
12385         if(this.fireEvent("beforeclick", this, index, item, e) === false){
12386             return false;
12387         }
12388         if (this.toggleSelect) {
12389             var m = this.isSelected(item) ? 'unselect' : 'select';
12390             Roo.log(m);
12391             var _t = this;
12392             _t[m](item, true, false);
12393             return true;
12394         }
12395         if(this.multiSelect || this.singleSelect){
12396             if(this.multiSelect && e.shiftKey && this.lastSelection){
12397                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
12398             }else{
12399                 this.select(item, this.multiSelect && e.ctrlKey);
12400                 this.lastSelection = item;
12401             }
12402             
12403             if(!this.tickable){
12404                 e.preventDefault();
12405             }
12406             
12407         }
12408         return true;
12409     },
12410
12411     /**
12412      * Get the number of selected nodes.
12413      * @return {Number}
12414      */
12415     getSelectionCount : function(){
12416         return this.selections.length;
12417     },
12418
12419     /**
12420      * Get the currently selected nodes.
12421      * @return {Array} An array of HTMLElements
12422      */
12423     getSelectedNodes : function(){
12424         return this.selections;
12425     },
12426
12427     /**
12428      * Get the indexes of the selected nodes.
12429      * @return {Array}
12430      */
12431     getSelectedIndexes : function(){
12432         var indexes = [], s = this.selections;
12433         for(var i = 0, len = s.length; i < len; i++){
12434             indexes.push(s[i].nodeIndex);
12435         }
12436         return indexes;
12437     },
12438
12439     /**
12440      * Clear all selections
12441      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
12442      */
12443     clearSelections : function(suppressEvent){
12444         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
12445             this.cmp.elements = this.selections;
12446             this.cmp.removeClass(this.selectedClass);
12447             this.selections = [];
12448             if(!suppressEvent){
12449                 this.fireEvent("selectionchange", this, this.selections);
12450             }
12451         }
12452     },
12453
12454     /**
12455      * Returns true if the passed node is selected
12456      * @param {HTMLElement/Number} node The node or node index
12457      * @return {Boolean}
12458      */
12459     isSelected : function(node){
12460         var s = this.selections;
12461         if(s.length < 1){
12462             return false;
12463         }
12464         node = this.getNode(node);
12465         return s.indexOf(node) !== -1;
12466     },
12467
12468     /**
12469      * Selects nodes.
12470      * @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
12471      * @param {Boolean} keepExisting (optional) true to keep existing selections
12472      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12473      */
12474     select : function(nodeInfo, keepExisting, suppressEvent){
12475         if(nodeInfo instanceof Array){
12476             if(!keepExisting){
12477                 this.clearSelections(true);
12478             }
12479             for(var i = 0, len = nodeInfo.length; i < len; i++){
12480                 this.select(nodeInfo[i], true, true);
12481             }
12482             return;
12483         } 
12484         var node = this.getNode(nodeInfo);
12485         if(!node || this.isSelected(node)){
12486             return; // already selected.
12487         }
12488         if(!keepExisting){
12489             this.clearSelections(true);
12490         }
12491         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
12492             Roo.fly(node).addClass(this.selectedClass);
12493             this.selections.push(node);
12494             if(!suppressEvent){
12495                 this.fireEvent("selectionchange", this, this.selections);
12496             }
12497         }
12498         
12499         
12500     },
12501       /**
12502      * Unselects nodes.
12503      * @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
12504      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
12505      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12506      */
12507     unselect : function(nodeInfo, keepExisting, suppressEvent)
12508     {
12509         if(nodeInfo instanceof Array){
12510             Roo.each(this.selections, function(s) {
12511                 this.unselect(s, nodeInfo);
12512             }, this);
12513             return;
12514         }
12515         var node = this.getNode(nodeInfo);
12516         if(!node || !this.isSelected(node)){
12517             Roo.log("not selected");
12518             return; // not selected.
12519         }
12520         // fireevent???
12521         var ns = [];
12522         Roo.each(this.selections, function(s) {
12523             if (s == node ) {
12524                 Roo.fly(node).removeClass(this.selectedClass);
12525
12526                 return;
12527             }
12528             ns.push(s);
12529         },this);
12530         
12531         this.selections= ns;
12532         this.fireEvent("selectionchange", this, this.selections);
12533     },
12534
12535     /**
12536      * Gets a template node.
12537      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12538      * @return {HTMLElement} The node or null if it wasn't found
12539      */
12540     getNode : function(nodeInfo){
12541         if(typeof nodeInfo == "string"){
12542             return document.getElementById(nodeInfo);
12543         }else if(typeof nodeInfo == "number"){
12544             return this.nodes[nodeInfo];
12545         }
12546         return nodeInfo;
12547     },
12548
12549     /**
12550      * Gets a range template nodes.
12551      * @param {Number} startIndex
12552      * @param {Number} endIndex
12553      * @return {Array} An array of nodes
12554      */
12555     getNodes : function(start, end){
12556         var ns = this.nodes;
12557         start = start || 0;
12558         end = typeof end == "undefined" ? ns.length - 1 : end;
12559         var nodes = [];
12560         if(start <= end){
12561             for(var i = start; i <= end; i++){
12562                 nodes.push(ns[i]);
12563             }
12564         } else{
12565             for(var i = start; i >= end; i--){
12566                 nodes.push(ns[i]);
12567             }
12568         }
12569         return nodes;
12570     },
12571
12572     /**
12573      * Finds the index of the passed node
12574      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12575      * @return {Number} The index of the node or -1
12576      */
12577     indexOf : function(node){
12578         node = this.getNode(node);
12579         if(typeof node.nodeIndex == "number"){
12580             return node.nodeIndex;
12581         }
12582         var ns = this.nodes;
12583         for(var i = 0, len = ns.length; i < len; i++){
12584             if(ns[i] == node){
12585                 return i;
12586             }
12587         }
12588         return -1;
12589     }
12590 });
12591 /*
12592  * - LGPL
12593  *
12594  * based on jquery fullcalendar
12595  * 
12596  */
12597
12598 Roo.bootstrap = Roo.bootstrap || {};
12599 /**
12600  * @class Roo.bootstrap.Calendar
12601  * @extends Roo.bootstrap.Component
12602  * Bootstrap Calendar class
12603  * @cfg {Boolean} loadMask (true|false) default false
12604  * @cfg {Object} header generate the user specific header of the calendar, default false
12605
12606  * @constructor
12607  * Create a new Container
12608  * @param {Object} config The config object
12609  */
12610
12611
12612
12613 Roo.bootstrap.Calendar = function(config){
12614     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
12615      this.addEvents({
12616         /**
12617              * @event select
12618              * Fires when a date is selected
12619              * @param {DatePicker} this
12620              * @param {Date} date The selected date
12621              */
12622         'select': true,
12623         /**
12624              * @event monthchange
12625              * Fires when the displayed month changes 
12626              * @param {DatePicker} this
12627              * @param {Date} date The selected month
12628              */
12629         'monthchange': true,
12630         /**
12631              * @event evententer
12632              * Fires when mouse over an event
12633              * @param {Calendar} this
12634              * @param {event} Event
12635              */
12636         'evententer': true,
12637         /**
12638              * @event eventleave
12639              * Fires when the mouse leaves an
12640              * @param {Calendar} this
12641              * @param {event}
12642              */
12643         'eventleave': true,
12644         /**
12645              * @event eventclick
12646              * Fires when the mouse click an
12647              * @param {Calendar} this
12648              * @param {event}
12649              */
12650         'eventclick': true
12651         
12652     });
12653
12654 };
12655
12656 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
12657     
12658      /**
12659      * @cfg {Number} startDay
12660      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
12661      */
12662     startDay : 0,
12663     
12664     loadMask : false,
12665     
12666     header : false,
12667       
12668     getAutoCreate : function(){
12669         
12670         
12671         var fc_button = function(name, corner, style, content ) {
12672             return Roo.apply({},{
12673                 tag : 'span',
12674                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
12675                          (corner.length ?
12676                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
12677                             ''
12678                         ),
12679                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
12680                 unselectable: 'on'
12681             });
12682         };
12683         
12684         var header = {};
12685         
12686         if(!this.header){
12687             header = {
12688                 tag : 'table',
12689                 cls : 'fc-header',
12690                 style : 'width:100%',
12691                 cn : [
12692                     {
12693                         tag: 'tr',
12694                         cn : [
12695                             {
12696                                 tag : 'td',
12697                                 cls : 'fc-header-left',
12698                                 cn : [
12699                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
12700                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
12701                                     { tag: 'span', cls: 'fc-header-space' },
12702                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
12703
12704
12705                                 ]
12706                             },
12707
12708                             {
12709                                 tag : 'td',
12710                                 cls : 'fc-header-center',
12711                                 cn : [
12712                                     {
12713                                         tag: 'span',
12714                                         cls: 'fc-header-title',
12715                                         cn : {
12716                                             tag: 'H2',
12717                                             html : 'month / year'
12718                                         }
12719                                     }
12720
12721                                 ]
12722                             },
12723                             {
12724                                 tag : 'td',
12725                                 cls : 'fc-header-right',
12726                                 cn : [
12727                               /*      fc_button('month', 'left', '', 'month' ),
12728                                     fc_button('week', '', '', 'week' ),
12729                                     fc_button('day', 'right', '', 'day' )
12730                                 */    
12731
12732                                 ]
12733                             }
12734
12735                         ]
12736                     }
12737                 ]
12738             };
12739         }
12740         
12741         header = this.header;
12742         
12743        
12744         var cal_heads = function() {
12745             var ret = [];
12746             // fixme - handle this.
12747             
12748             for (var i =0; i < Date.dayNames.length; i++) {
12749                 var d = Date.dayNames[i];
12750                 ret.push({
12751                     tag: 'th',
12752                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
12753                     html : d.substring(0,3)
12754                 });
12755                 
12756             }
12757             ret[0].cls += ' fc-first';
12758             ret[6].cls += ' fc-last';
12759             return ret;
12760         };
12761         var cal_cell = function(n) {
12762             return  {
12763                 tag: 'td',
12764                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
12765                 cn : [
12766                     {
12767                         cn : [
12768                             {
12769                                 cls: 'fc-day-number',
12770                                 html: 'D'
12771                             },
12772                             {
12773                                 cls: 'fc-day-content',
12774                              
12775                                 cn : [
12776                                      {
12777                                         style: 'position: relative;' // height: 17px;
12778                                     }
12779                                 ]
12780                             }
12781                             
12782                             
12783                         ]
12784                     }
12785                 ]
12786                 
12787             }
12788         };
12789         var cal_rows = function() {
12790             
12791             var ret = []
12792             for (var r = 0; r < 6; r++) {
12793                 var row= {
12794                     tag : 'tr',
12795                     cls : 'fc-week',
12796                     cn : []
12797                 };
12798                 
12799                 for (var i =0; i < Date.dayNames.length; i++) {
12800                     var d = Date.dayNames[i];
12801                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
12802
12803                 }
12804                 row.cn[0].cls+=' fc-first';
12805                 row.cn[0].cn[0].style = 'min-height:90px';
12806                 row.cn[6].cls+=' fc-last';
12807                 ret.push(row);
12808                 
12809             }
12810             ret[0].cls += ' fc-first';
12811             ret[4].cls += ' fc-prev-last';
12812             ret[5].cls += ' fc-last';
12813             return ret;
12814             
12815         };
12816         
12817         var cal_table = {
12818             tag: 'table',
12819             cls: 'fc-border-separate',
12820             style : 'width:100%',
12821             cellspacing  : 0,
12822             cn : [
12823                 { 
12824                     tag: 'thead',
12825                     cn : [
12826                         { 
12827                             tag: 'tr',
12828                             cls : 'fc-first fc-last',
12829                             cn : cal_heads()
12830                         }
12831                     ]
12832                 },
12833                 { 
12834                     tag: 'tbody',
12835                     cn : cal_rows()
12836                 }
12837                   
12838             ]
12839         };
12840          
12841          var cfg = {
12842             cls : 'fc fc-ltr',
12843             cn : [
12844                 header,
12845                 {
12846                     cls : 'fc-content',
12847                     style : "position: relative;",
12848                     cn : [
12849                         {
12850                             cls : 'fc-view fc-view-month fc-grid',
12851                             style : 'position: relative',
12852                             unselectable : 'on',
12853                             cn : [
12854                                 {
12855                                     cls : 'fc-event-container',
12856                                     style : 'position:absolute;z-index:8;top:0;left:0;'
12857                                 },
12858                                 cal_table
12859                             ]
12860                         }
12861                     ]
12862     
12863                 }
12864            ] 
12865             
12866         };
12867         
12868          
12869         
12870         return cfg;
12871     },
12872     
12873     
12874     initEvents : function()
12875     {
12876         if(!this.store){
12877             throw "can not find store for calendar";
12878         }
12879         
12880         var mark = {
12881             tag: "div",
12882             cls:"x-dlg-mask",
12883             style: "text-align:center",
12884             cn: [
12885                 {
12886                     tag: "div",
12887                     style: "background-color:white;width:50%;margin:250 auto",
12888                     cn: [
12889                         {
12890                             tag: "img",
12891                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
12892                         },
12893                         {
12894                             tag: "span",
12895                             html: "Loading"
12896                         }
12897                         
12898                     ]
12899                 }
12900             ]
12901         }
12902         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
12903         
12904         var size = this.el.select('.fc-content', true).first().getSize();
12905         this.maskEl.setSize(size.width, size.height);
12906         this.maskEl.enableDisplayMode("block");
12907         if(!this.loadMask){
12908             this.maskEl.hide();
12909         }
12910         
12911         this.store = Roo.factory(this.store, Roo.data);
12912         this.store.on('load', this.onLoad, this);
12913         this.store.on('beforeload', this.onBeforeLoad, this);
12914         
12915         this.resize();
12916         
12917         this.cells = this.el.select('.fc-day',true);
12918         //Roo.log(this.cells);
12919         this.textNodes = this.el.query('.fc-day-number');
12920         this.cells.addClassOnOver('fc-state-hover');
12921         
12922         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
12923         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
12924         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
12925         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
12926         
12927         this.on('monthchange', this.onMonthChange, this);
12928         
12929         this.update(new Date().clearTime());
12930     },
12931     
12932     resize : function() {
12933         var sz  = this.el.getSize();
12934         
12935         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
12936         this.el.select('.fc-day-content div',true).setHeight(34);
12937     },
12938     
12939     
12940     // private
12941     showPrevMonth : function(e){
12942         this.update(this.activeDate.add("mo", -1));
12943     },
12944     showToday : function(e){
12945         this.update(new Date().clearTime());
12946     },
12947     // private
12948     showNextMonth : function(e){
12949         this.update(this.activeDate.add("mo", 1));
12950     },
12951
12952     // private
12953     showPrevYear : function(){
12954         this.update(this.activeDate.add("y", -1));
12955     },
12956
12957     // private
12958     showNextYear : function(){
12959         this.update(this.activeDate.add("y", 1));
12960     },
12961
12962     
12963    // private
12964     update : function(date)
12965     {
12966         var vd = this.activeDate;
12967         this.activeDate = date;
12968 //        if(vd && this.el){
12969 //            var t = date.getTime();
12970 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
12971 //                Roo.log('using add remove');
12972 //                
12973 //                this.fireEvent('monthchange', this, date);
12974 //                
12975 //                this.cells.removeClass("fc-state-highlight");
12976 //                this.cells.each(function(c){
12977 //                   if(c.dateValue == t){
12978 //                       c.addClass("fc-state-highlight");
12979 //                       setTimeout(function(){
12980 //                            try{c.dom.firstChild.focus();}catch(e){}
12981 //                       }, 50);
12982 //                       return false;
12983 //                   }
12984 //                   return true;
12985 //                });
12986 //                return;
12987 //            }
12988 //        }
12989         
12990         var days = date.getDaysInMonth();
12991         
12992         var firstOfMonth = date.getFirstDateOfMonth();
12993         var startingPos = firstOfMonth.getDay()-this.startDay;
12994         
12995         if(startingPos < this.startDay){
12996             startingPos += 7;
12997         }
12998         
12999         var pm = date.add(Date.MONTH, -1);
13000         var prevStart = pm.getDaysInMonth()-startingPos;
13001 //        
13002         this.cells = this.el.select('.fc-day',true);
13003         this.textNodes = this.el.query('.fc-day-number');
13004         this.cells.addClassOnOver('fc-state-hover');
13005         
13006         var cells = this.cells.elements;
13007         var textEls = this.textNodes;
13008         
13009         Roo.each(cells, function(cell){
13010             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
13011         });
13012         
13013         days += startingPos;
13014
13015         // convert everything to numbers so it's fast
13016         var day = 86400000;
13017         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
13018         //Roo.log(d);
13019         //Roo.log(pm);
13020         //Roo.log(prevStart);
13021         
13022         var today = new Date().clearTime().getTime();
13023         var sel = date.clearTime().getTime();
13024         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
13025         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
13026         var ddMatch = this.disabledDatesRE;
13027         var ddText = this.disabledDatesText;
13028         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
13029         var ddaysText = this.disabledDaysText;
13030         var format = this.format;
13031         
13032         var setCellClass = function(cal, cell){
13033             cell.row = 0;
13034             cell.events = [];
13035             cell.more = [];
13036             //Roo.log('set Cell Class');
13037             cell.title = "";
13038             var t = d.getTime();
13039             
13040             //Roo.log(d);
13041             
13042             cell.dateValue = t;
13043             if(t == today){
13044                 cell.className += " fc-today";
13045                 cell.className += " fc-state-highlight";
13046                 cell.title = cal.todayText;
13047             }
13048             if(t == sel){
13049                 // disable highlight in other month..
13050                 //cell.className += " fc-state-highlight";
13051                 
13052             }
13053             // disabling
13054             if(t < min) {
13055                 cell.className = " fc-state-disabled";
13056                 cell.title = cal.minText;
13057                 return;
13058             }
13059             if(t > max) {
13060                 cell.className = " fc-state-disabled";
13061                 cell.title = cal.maxText;
13062                 return;
13063             }
13064             if(ddays){
13065                 if(ddays.indexOf(d.getDay()) != -1){
13066                     cell.title = ddaysText;
13067                     cell.className = " fc-state-disabled";
13068                 }
13069             }
13070             if(ddMatch && format){
13071                 var fvalue = d.dateFormat(format);
13072                 if(ddMatch.test(fvalue)){
13073                     cell.title = ddText.replace("%0", fvalue);
13074                     cell.className = " fc-state-disabled";
13075                 }
13076             }
13077             
13078             if (!cell.initialClassName) {
13079                 cell.initialClassName = cell.dom.className;
13080             }
13081             
13082             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
13083         };
13084
13085         var i = 0;
13086         
13087         for(; i < startingPos; i++) {
13088             textEls[i].innerHTML = (++prevStart);
13089             d.setDate(d.getDate()+1);
13090             
13091             cells[i].className = "fc-past fc-other-month";
13092             setCellClass(this, cells[i]);
13093         }
13094         
13095         var intDay = 0;
13096         
13097         for(; i < days; i++){
13098             intDay = i - startingPos + 1;
13099             textEls[i].innerHTML = (intDay);
13100             d.setDate(d.getDate()+1);
13101             
13102             cells[i].className = ''; // "x-date-active";
13103             setCellClass(this, cells[i]);
13104         }
13105         var extraDays = 0;
13106         
13107         for(; i < 42; i++) {
13108             textEls[i].innerHTML = (++extraDays);
13109             d.setDate(d.getDate()+1);
13110             
13111             cells[i].className = "fc-future fc-other-month";
13112             setCellClass(this, cells[i]);
13113         }
13114         
13115         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
13116         
13117         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
13118         
13119         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
13120         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
13121         
13122         if(totalRows != 6){
13123             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
13124             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
13125         }
13126         
13127         this.fireEvent('monthchange', this, date);
13128         
13129         
13130         /*
13131         if(!this.internalRender){
13132             var main = this.el.dom.firstChild;
13133             var w = main.offsetWidth;
13134             this.el.setWidth(w + this.el.getBorderWidth("lr"));
13135             Roo.fly(main).setWidth(w);
13136             this.internalRender = true;
13137             // opera does not respect the auto grow header center column
13138             // then, after it gets a width opera refuses to recalculate
13139             // without a second pass
13140             if(Roo.isOpera && !this.secondPass){
13141                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
13142                 this.secondPass = true;
13143                 this.update.defer(10, this, [date]);
13144             }
13145         }
13146         */
13147         
13148     },
13149     
13150     findCell : function(dt) {
13151         dt = dt.clearTime().getTime();
13152         var ret = false;
13153         this.cells.each(function(c){
13154             //Roo.log("check " +c.dateValue + '?=' + dt);
13155             if(c.dateValue == dt){
13156                 ret = c;
13157                 return false;
13158             }
13159             return true;
13160         });
13161         
13162         return ret;
13163     },
13164     
13165     findCells : function(ev) {
13166         var s = ev.start.clone().clearTime().getTime();
13167        // Roo.log(s);
13168         var e= ev.end.clone().clearTime().getTime();
13169        // Roo.log(e);
13170         var ret = [];
13171         this.cells.each(function(c){
13172              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
13173             
13174             if(c.dateValue > e){
13175                 return ;
13176             }
13177             if(c.dateValue < s){
13178                 return ;
13179             }
13180             ret.push(c);
13181         });
13182         
13183         return ret;    
13184     },
13185     
13186 //    findBestRow: function(cells)
13187 //    {
13188 //        var ret = 0;
13189 //        
13190 //        for (var i =0 ; i < cells.length;i++) {
13191 //            ret  = Math.max(cells[i].rows || 0,ret);
13192 //        }
13193 //        return ret;
13194 //        
13195 //    },
13196     
13197     
13198     addItem : function(ev)
13199     {
13200         // look for vertical location slot in
13201         var cells = this.findCells(ev);
13202         
13203 //        ev.row = this.findBestRow(cells);
13204         
13205         // work out the location.
13206         
13207         var crow = false;
13208         var rows = [];
13209         for(var i =0; i < cells.length; i++) {
13210             
13211             cells[i].row = cells[0].row;
13212             
13213             if(i == 0){
13214                 cells[i].row = cells[i].row + 1;
13215             }
13216             
13217             if (!crow) {
13218                 crow = {
13219                     start : cells[i],
13220                     end :  cells[i]
13221                 };
13222                 continue;
13223             }
13224             if (crow.start.getY() == cells[i].getY()) {
13225                 // on same row.
13226                 crow.end = cells[i];
13227                 continue;
13228             }
13229             // different row.
13230             rows.push(crow);
13231             crow = {
13232                 start: cells[i],
13233                 end : cells[i]
13234             };
13235             
13236         }
13237         
13238         rows.push(crow);
13239         ev.els = [];
13240         ev.rows = rows;
13241         ev.cells = cells;
13242         
13243         cells[0].events.push(ev);
13244         
13245         this.calevents.push(ev);
13246     },
13247     
13248     clearEvents: function() {
13249         
13250         if(!this.calevents){
13251             return;
13252         }
13253         
13254         Roo.each(this.cells.elements, function(c){
13255             c.row = 0;
13256             c.events = [];
13257             c.more = [];
13258         });
13259         
13260         Roo.each(this.calevents, function(e) {
13261             Roo.each(e.els, function(el) {
13262                 el.un('mouseenter' ,this.onEventEnter, this);
13263                 el.un('mouseleave' ,this.onEventLeave, this);
13264                 el.remove();
13265             },this);
13266         },this);
13267         
13268         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
13269             e.remove();
13270         });
13271         
13272     },
13273     
13274     renderEvents: function()
13275     {   
13276         var _this = this;
13277         
13278         this.cells.each(function(c) {
13279             
13280             if(c.row < 5){
13281                 return;
13282             }
13283             
13284             var ev = c.events;
13285             
13286             var r = 4;
13287             if(c.row != c.events.length){
13288                 r = 4 - (4 - (c.row - c.events.length));
13289             }
13290             
13291             c.events = ev.slice(0, r);
13292             c.more = ev.slice(r);
13293             
13294             if(c.more.length && c.more.length == 1){
13295                 c.events.push(c.more.pop());
13296             }
13297             
13298             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
13299             
13300         });
13301             
13302         this.cells.each(function(c) {
13303             
13304             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
13305             
13306             
13307             for (var e = 0; e < c.events.length; e++){
13308                 var ev = c.events[e];
13309                 var rows = ev.rows;
13310                 
13311                 for(var i = 0; i < rows.length; i++) {
13312                 
13313                     // how many rows should it span..
13314
13315                     var  cfg = {
13316                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
13317                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
13318
13319                         unselectable : "on",
13320                         cn : [
13321                             {
13322                                 cls: 'fc-event-inner',
13323                                 cn : [
13324     //                                {
13325     //                                  tag:'span',
13326     //                                  cls: 'fc-event-time',
13327     //                                  html : cells.length > 1 ? '' : ev.time
13328     //                                },
13329                                     {
13330                                       tag:'span',
13331                                       cls: 'fc-event-title',
13332                                       html : String.format('{0}', ev.title)
13333                                     }
13334
13335
13336                                 ]
13337                             },
13338                             {
13339                                 cls: 'ui-resizable-handle ui-resizable-e',
13340                                 html : '&nbsp;&nbsp;&nbsp'
13341                             }
13342
13343                         ]
13344                     };
13345
13346                     if (i == 0) {
13347                         cfg.cls += ' fc-event-start';
13348                     }
13349                     if ((i+1) == rows.length) {
13350                         cfg.cls += ' fc-event-end';
13351                     }
13352
13353                     var ctr = _this.el.select('.fc-event-container',true).first();
13354                     var cg = ctr.createChild(cfg);
13355
13356                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
13357                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
13358
13359                     var r = (c.more.length) ? 1 : 0;
13360                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
13361                     cg.setWidth(ebox.right - sbox.x -2);
13362
13363                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
13364                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
13365                     cg.on('click', _this.onEventClick, _this, ev);
13366
13367                     ev.els.push(cg);
13368                     
13369                 }
13370                 
13371             }
13372             
13373             
13374             if(c.more.length){
13375                 var  cfg = {
13376                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
13377                     style : 'position: absolute',
13378                     unselectable : "on",
13379                     cn : [
13380                         {
13381                             cls: 'fc-event-inner',
13382                             cn : [
13383                                 {
13384                                   tag:'span',
13385                                   cls: 'fc-event-title',
13386                                   html : 'More'
13387                                 }
13388
13389
13390                             ]
13391                         },
13392                         {
13393                             cls: 'ui-resizable-handle ui-resizable-e',
13394                             html : '&nbsp;&nbsp;&nbsp'
13395                         }
13396
13397                     ]
13398                 };
13399
13400                 var ctr = _this.el.select('.fc-event-container',true).first();
13401                 var cg = ctr.createChild(cfg);
13402
13403                 var sbox = c.select('.fc-day-content',true).first().getBox();
13404                 var ebox = c.select('.fc-day-content',true).first().getBox();
13405                 //Roo.log(cg);
13406                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
13407                 cg.setWidth(ebox.right - sbox.x -2);
13408
13409                 cg.on('click', _this.onMoreEventClick, _this, c.more);
13410                 
13411             }
13412             
13413         });
13414         
13415         
13416         
13417     },
13418     
13419     onEventEnter: function (e, el,event,d) {
13420         this.fireEvent('evententer', this, el, event);
13421     },
13422     
13423     onEventLeave: function (e, el,event,d) {
13424         this.fireEvent('eventleave', this, el, event);
13425     },
13426     
13427     onEventClick: function (e, el,event,d) {
13428         this.fireEvent('eventclick', this, el, event);
13429     },
13430     
13431     onMonthChange: function () {
13432         this.store.load();
13433     },
13434     
13435     onMoreEventClick: function(e, el, more)
13436     {
13437         var _this = this;
13438         
13439         this.calpopover.placement = 'right';
13440         this.calpopover.setTitle('More');
13441         
13442         this.calpopover.setContent('');
13443         
13444         var ctr = this.calpopover.el.select('.popover-content', true).first();
13445         
13446         Roo.each(more, function(m){
13447             var cfg = {
13448                 cls : 'fc-event-hori fc-event-draggable',
13449                 html : m.title
13450             }
13451             var cg = ctr.createChild(cfg);
13452             
13453             cg.on('click', _this.onEventClick, _this, m);
13454         });
13455         
13456         this.calpopover.show(el);
13457         
13458         
13459     },
13460     
13461     onLoad: function () 
13462     {   
13463         this.calevents = [];
13464         var cal = this;
13465         
13466         if(this.store.getCount() > 0){
13467             this.store.data.each(function(d){
13468                cal.addItem({
13469                     id : d.data.id,
13470                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
13471                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
13472                     time : d.data.start_time,
13473                     title : d.data.title,
13474                     description : d.data.description,
13475                     venue : d.data.venue
13476                 });
13477             });
13478         }
13479         
13480         this.renderEvents();
13481         
13482         if(this.calevents.length && this.loadMask){
13483             this.maskEl.hide();
13484         }
13485     },
13486     
13487     onBeforeLoad: function()
13488     {
13489         this.clearEvents();
13490         if(this.loadMask){
13491             this.maskEl.show();
13492         }
13493     }
13494 });
13495
13496  
13497  /*
13498  * - LGPL
13499  *
13500  * element
13501  * 
13502  */
13503
13504 /**
13505  * @class Roo.bootstrap.Popover
13506  * @extends Roo.bootstrap.Component
13507  * Bootstrap Popover class
13508  * @cfg {String} html contents of the popover   (or false to use children..)
13509  * @cfg {String} title of popover (or false to hide)
13510  * @cfg {String} placement how it is placed
13511  * @cfg {String} trigger click || hover (or false to trigger manually)
13512  * @cfg {String} over what (parent or false to trigger manually.)
13513  * 
13514  * @constructor
13515  * Create a new Popover
13516  * @param {Object} config The config object
13517  */
13518
13519 Roo.bootstrap.Popover = function(config){
13520     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
13521 };
13522
13523 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
13524     
13525     title: 'Fill in a title',
13526     html: false,
13527     
13528     placement : 'right',
13529     trigger : 'hover', // hover
13530     
13531     over: 'parent',
13532     
13533     can_build_overlaid : false,
13534     
13535     getChildContainer : function()
13536     {
13537         return this.el.select('.popover-content',true).first();
13538     },
13539     
13540     getAutoCreate : function(){
13541          Roo.log('make popover?');
13542         var cfg = {
13543            cls : 'popover roo-dynamic',
13544            style: 'display:block',
13545            cn : [
13546                 {
13547                     cls : 'arrow'
13548                 },
13549                 {
13550                     cls : 'popover-inner',
13551                     cn : [
13552                         {
13553                             tag: 'h3',
13554                             cls: 'popover-title',
13555                             html : this.title
13556                         },
13557                         {
13558                             cls : 'popover-content',
13559                             html : this.html
13560                         }
13561                     ]
13562                     
13563                 }
13564            ]
13565         };
13566         
13567         return cfg;
13568     },
13569     setTitle: function(str)
13570     {
13571         this.el.select('.popover-title',true).first().dom.innerHTML = str;
13572     },
13573     setContent: function(str)
13574     {
13575         this.el.select('.popover-content',true).first().dom.innerHTML = str;
13576     },
13577     // as it get's added to the bottom of the page.
13578     onRender : function(ct, position)
13579     {
13580         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
13581         if(!this.el){
13582             var cfg = Roo.apply({},  this.getAutoCreate());
13583             cfg.id = Roo.id();
13584             
13585             if (this.cls) {
13586                 cfg.cls += ' ' + this.cls;
13587             }
13588             if (this.style) {
13589                 cfg.style = this.style;
13590             }
13591             Roo.log("adding to ")
13592             this.el = Roo.get(document.body).createChild(cfg, position);
13593             Roo.log(this.el);
13594         }
13595         this.initEvents();
13596     },
13597     
13598     initEvents : function()
13599     {
13600         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
13601         this.el.enableDisplayMode('block');
13602         this.el.hide();
13603         if (this.over === false) {
13604             return; 
13605         }
13606         if (this.triggers === false) {
13607             return;
13608         }
13609         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13610         var triggers = this.trigger ? this.trigger.split(' ') : [];
13611         Roo.each(triggers, function(trigger) {
13612         
13613             if (trigger == 'click') {
13614                 on_el.on('click', this.toggle, this);
13615             } else if (trigger != 'manual') {
13616                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
13617                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
13618       
13619                 on_el.on(eventIn  ,this.enter, this);
13620                 on_el.on(eventOut, this.leave, this);
13621             }
13622         }, this);
13623         
13624     },
13625     
13626     
13627     // private
13628     timeout : null,
13629     hoverState : null,
13630     
13631     toggle : function () {
13632         this.hoverState == 'in' ? this.leave() : this.enter();
13633     },
13634     
13635     enter : function () {
13636        
13637     
13638         clearTimeout(this.timeout);
13639     
13640         this.hoverState = 'in'
13641     
13642         if (!this.delay || !this.delay.show) {
13643             this.show();
13644             return 
13645         }
13646         var _t = this;
13647         this.timeout = setTimeout(function () {
13648             if (_t.hoverState == 'in') {
13649                 _t.show();
13650             }
13651         }, this.delay.show)
13652     },
13653     leave : function() {
13654         clearTimeout(this.timeout);
13655     
13656         this.hoverState = 'out'
13657     
13658         if (!this.delay || !this.delay.hide) {
13659             this.hide();
13660             return 
13661         }
13662         var _t = this;
13663         this.timeout = setTimeout(function () {
13664             if (_t.hoverState == 'out') {
13665                 _t.hide();
13666             }
13667         }, this.delay.hide)
13668     },
13669     
13670     show : function (on_el)
13671     {
13672         if (!on_el) {
13673             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13674         }
13675         // set content.
13676         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
13677         if (this.html !== false) {
13678             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
13679         }
13680         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
13681         if (!this.title.length) {
13682             this.el.select('.popover-title',true).hide();
13683         }
13684         
13685         var placement = typeof this.placement == 'function' ?
13686             this.placement.call(this, this.el, on_el) :
13687             this.placement;
13688             
13689         var autoToken = /\s?auto?\s?/i;
13690         var autoPlace = autoToken.test(placement);
13691         if (autoPlace) {
13692             placement = placement.replace(autoToken, '') || 'top';
13693         }
13694         
13695         //this.el.detach()
13696         //this.el.setXY([0,0]);
13697         this.el.show();
13698         this.el.dom.style.display='block';
13699         this.el.addClass(placement);
13700         
13701         //this.el.appendTo(on_el);
13702         
13703         var p = this.getPosition();
13704         var box = this.el.getBox();
13705         
13706         if (autoPlace) {
13707             // fixme..
13708         }
13709         var align = Roo.bootstrap.Popover.alignment[placement]
13710         this.el.alignTo(on_el, align[0],align[1]);
13711         //var arrow = this.el.select('.arrow',true).first();
13712         //arrow.set(align[2], 
13713         
13714         this.el.addClass('in');
13715         this.hoverState = null;
13716         
13717         if (this.el.hasClass('fade')) {
13718             // fade it?
13719         }
13720         
13721     },
13722     hide : function()
13723     {
13724         this.el.setXY([0,0]);
13725         this.el.removeClass('in');
13726         this.el.hide();
13727         
13728     }
13729     
13730 });
13731
13732 Roo.bootstrap.Popover.alignment = {
13733     'left' : ['r-l', [-10,0], 'right'],
13734     'right' : ['l-r', [10,0], 'left'],
13735     'bottom' : ['t-b', [0,10], 'top'],
13736     'top' : [ 'b-t', [0,-10], 'bottom']
13737 };
13738
13739  /*
13740  * - LGPL
13741  *
13742  * Progress
13743  * 
13744  */
13745
13746 /**
13747  * @class Roo.bootstrap.Progress
13748  * @extends Roo.bootstrap.Component
13749  * Bootstrap Progress class
13750  * @cfg {Boolean} striped striped of the progress bar
13751  * @cfg {Boolean} active animated of the progress bar
13752  * 
13753  * 
13754  * @constructor
13755  * Create a new Progress
13756  * @param {Object} config The config object
13757  */
13758
13759 Roo.bootstrap.Progress = function(config){
13760     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
13761 };
13762
13763 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
13764     
13765     striped : false,
13766     active: false,
13767     
13768     getAutoCreate : function(){
13769         var cfg = {
13770             tag: 'div',
13771             cls: 'progress'
13772         };
13773         
13774         
13775         if(this.striped){
13776             cfg.cls += ' progress-striped';
13777         }
13778       
13779         if(this.active){
13780             cfg.cls += ' active';
13781         }
13782         
13783         
13784         return cfg;
13785     }
13786    
13787 });
13788
13789  
13790
13791  /*
13792  * - LGPL
13793  *
13794  * ProgressBar
13795  * 
13796  */
13797
13798 /**
13799  * @class Roo.bootstrap.ProgressBar
13800  * @extends Roo.bootstrap.Component
13801  * Bootstrap ProgressBar class
13802  * @cfg {Number} aria_valuenow aria-value now
13803  * @cfg {Number} aria_valuemin aria-value min
13804  * @cfg {Number} aria_valuemax aria-value max
13805  * @cfg {String} label label for the progress bar
13806  * @cfg {String} panel (success | info | warning | danger )
13807  * @cfg {String} role role of the progress bar
13808  * @cfg {String} sr_only text
13809  * 
13810  * 
13811  * @constructor
13812  * Create a new ProgressBar
13813  * @param {Object} config The config object
13814  */
13815
13816 Roo.bootstrap.ProgressBar = function(config){
13817     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
13818 };
13819
13820 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
13821     
13822     aria_valuenow : 0,
13823     aria_valuemin : 0,
13824     aria_valuemax : 100,
13825     label : false,
13826     panel : false,
13827     role : false,
13828     sr_only: false,
13829     
13830     getAutoCreate : function()
13831     {
13832         
13833         var cfg = {
13834             tag: 'div',
13835             cls: 'progress-bar',
13836             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
13837         };
13838         
13839         if(this.sr_only){
13840             cfg.cn = {
13841                 tag: 'span',
13842                 cls: 'sr-only',
13843                 html: this.sr_only
13844             }
13845         }
13846         
13847         if(this.role){
13848             cfg.role = this.role;
13849         }
13850         
13851         if(this.aria_valuenow){
13852             cfg['aria-valuenow'] = this.aria_valuenow;
13853         }
13854         
13855         if(this.aria_valuemin){
13856             cfg['aria-valuemin'] = this.aria_valuemin;
13857         }
13858         
13859         if(this.aria_valuemax){
13860             cfg['aria-valuemax'] = this.aria_valuemax;
13861         }
13862         
13863         if(this.label && !this.sr_only){
13864             cfg.html = this.label;
13865         }
13866         
13867         if(this.panel){
13868             cfg.cls += ' progress-bar-' + this.panel;
13869         }
13870         
13871         return cfg;
13872     },
13873     
13874     update : function(aria_valuenow)
13875     {
13876         this.aria_valuenow = aria_valuenow;
13877         
13878         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
13879     }
13880    
13881 });
13882
13883  
13884
13885  /*
13886  * - LGPL
13887  *
13888  * column
13889  * 
13890  */
13891
13892 /**
13893  * @class Roo.bootstrap.TabGroup
13894  * @extends Roo.bootstrap.Column
13895  * Bootstrap Column class
13896  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
13897  * @cfg {Boolean} carousel true to make the group behave like a carousel
13898  * 
13899  * @constructor
13900  * Create a new TabGroup
13901  * @param {Object} config The config object
13902  */
13903
13904 Roo.bootstrap.TabGroup = function(config){
13905     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
13906     if (!this.navId) {
13907         this.navId = Roo.id();
13908     }
13909     this.tabs = [];
13910     Roo.bootstrap.TabGroup.register(this);
13911     
13912 };
13913
13914 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
13915     
13916     carousel : false,
13917     transition : false,
13918      
13919     getAutoCreate : function()
13920     {
13921         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
13922         
13923         cfg.cls += ' tab-content';
13924         
13925         if (this.carousel) {
13926             cfg.cls += ' carousel slide';
13927             cfg.cn = [{
13928                cls : 'carousel-inner'
13929             }]
13930         }
13931         
13932         
13933         return cfg;
13934     },
13935     getChildContainer : function()
13936     {
13937         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
13938     },
13939     
13940     /**
13941     * register a Navigation item
13942     * @param {Roo.bootstrap.NavItem} the navitem to add
13943     */
13944     register : function(item)
13945     {
13946         this.tabs.push( item);
13947         item.navId = this.navId; // not really needed..
13948     
13949     },
13950     
13951     getActivePanel : function()
13952     {
13953         var r = false;
13954         Roo.each(this.tabs, function(t) {
13955             if (t.active) {
13956                 r = t;
13957                 return false;
13958             }
13959             return null;
13960         });
13961         return r;
13962         
13963     },
13964     getPanelByName : function(n)
13965     {
13966         var r = false;
13967         Roo.each(this.tabs, function(t) {
13968             if (t.tabId == n) {
13969                 r = t;
13970                 return false;
13971             }
13972             return null;
13973         });
13974         return r;
13975     },
13976     indexOfPanel : function(p)
13977     {
13978         var r = false;
13979         Roo.each(this.tabs, function(t,i) {
13980             if (t.tabId == p.tabId) {
13981                 r = i;
13982                 return false;
13983             }
13984             return null;
13985         });
13986         return r;
13987     },
13988     /**
13989      * show a specific panel
13990      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
13991      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
13992      */
13993     showPanel : function (pan)
13994     {
13995         
13996         if (typeof(pan) == 'number') {
13997             pan = this.tabs[pan];
13998         }
13999         if (typeof(pan) == 'string') {
14000             pan = this.getPanelByName(pan);
14001         }
14002         if (pan.tabId == this.getActivePanel().tabId) {
14003             return true;
14004         }
14005         var cur = this.getActivePanel();
14006         
14007         if (false === cur.fireEvent('beforedeactivate')) {
14008             return false;
14009         }
14010         
14011         if (this.carousel) {
14012             this.transition = true;
14013             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
14014             var lr = dir == 'next' ? 'left' : 'right';
14015             pan.el.addClass(dir); // or prev
14016             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
14017             cur.el.addClass(lr); // or right
14018             pan.el.addClass(lr);
14019             
14020             var _this = this;
14021             cur.el.on('transitionend', function() {
14022                 Roo.log("trans end?");
14023                 
14024                 pan.el.removeClass([lr,dir]);
14025                 pan.setActive(true);
14026                 
14027                 cur.el.removeClass([lr]);
14028                 cur.setActive(false);
14029                 
14030                 _this.transition = false;
14031                 
14032             }, this, { single:  true } );
14033             return true;
14034         }
14035         
14036         cur.setActive(false);
14037         pan.setActive(true);
14038         return true;
14039         
14040     },
14041     showPanelNext : function()
14042     {
14043         var i = this.indexOfPanel(this.getActivePanel());
14044         if (i > this.tabs.length) {
14045             return;
14046         }
14047         this.showPanel(this.tabs[i+1]);
14048     },
14049     showPanelPrev : function()
14050     {
14051         var i = this.indexOfPanel(this.getActivePanel());
14052         if (i  < 1) {
14053             return;
14054         }
14055         this.showPanel(this.tabs[i-1]);
14056     }
14057     
14058     
14059   
14060 });
14061
14062  
14063
14064  
14065  
14066 Roo.apply(Roo.bootstrap.TabGroup, {
14067     
14068     groups: {},
14069      /**
14070     * register a Navigation Group
14071     * @param {Roo.bootstrap.NavGroup} the navgroup to add
14072     */
14073     register : function(navgrp)
14074     {
14075         this.groups[navgrp.navId] = navgrp;
14076         
14077     },
14078     /**
14079     * fetch a Navigation Group based on the navigation ID
14080     * if one does not exist , it will get created.
14081     * @param {string} the navgroup to add
14082     * @returns {Roo.bootstrap.NavGroup} the navgroup 
14083     */
14084     get: function(navId) {
14085         if (typeof(this.groups[navId]) == 'undefined') {
14086             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
14087         }
14088         return this.groups[navId] ;
14089     }
14090     
14091     
14092     
14093 });
14094
14095  /*
14096  * - LGPL
14097  *
14098  * TabPanel
14099  * 
14100  */
14101
14102 /**
14103  * @class Roo.bootstrap.TabPanel
14104  * @extends Roo.bootstrap.Component
14105  * Bootstrap TabPanel class
14106  * @cfg {Boolean} active panel active
14107  * @cfg {String} html panel content
14108  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
14109  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
14110  * 
14111  * 
14112  * @constructor
14113  * Create a new TabPanel
14114  * @param {Object} config The config object
14115  */
14116
14117 Roo.bootstrap.TabPanel = function(config){
14118     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
14119     this.addEvents({
14120         /**
14121              * @event changed
14122              * Fires when the active status changes
14123              * @param {Roo.bootstrap.TabPanel} this
14124              * @param {Boolean} state the new state
14125             
14126          */
14127         'changed': true,
14128         /**
14129              * @event beforedeactivate
14130              * Fires before a tab is de-activated - can be used to do validation on a form.
14131              * @param {Roo.bootstrap.TabPanel} this
14132              * @return {Boolean} false if there is an error
14133             
14134          */
14135         'beforedeactivate': true
14136      });
14137     
14138     this.tabId = this.tabId || Roo.id();
14139   
14140 };
14141
14142 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
14143     
14144     active: false,
14145     html: false,
14146     tabId: false,
14147     navId : false,
14148     
14149     getAutoCreate : function(){
14150         var cfg = {
14151             tag: 'div',
14152             // item is needed for carousel - not sure if it has any effect otherwise
14153             cls: 'tab-pane item',
14154             html: this.html || ''
14155         };
14156         
14157         if(this.active){
14158             cfg.cls += ' active';
14159         }
14160         
14161         if(this.tabId){
14162             cfg.tabId = this.tabId;
14163         }
14164         
14165         
14166         return cfg;
14167     },
14168     
14169     initEvents:  function()
14170     {
14171         Roo.log('-------- init events on tab panel ---------');
14172         
14173         var p = this.parent();
14174         this.navId = this.navId || p.navId;
14175         
14176         if (typeof(this.navId) != 'undefined') {
14177             // not really needed.. but just in case.. parent should be a NavGroup.
14178             var tg = Roo.bootstrap.TabGroup.get(this.navId);
14179             Roo.log(['register', tg, this]);
14180             tg.register(this);
14181         }
14182     },
14183     
14184     
14185     onRender : function(ct, position)
14186     {
14187        // Roo.log("Call onRender: " + this.xtype);
14188         
14189         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
14190         
14191         
14192         
14193         
14194         
14195     },
14196     
14197     setActive: function(state)
14198     {
14199         Roo.log("panel - set active " + this.tabId + "=" + state);
14200         
14201         this.active = state;
14202         if (!state) {
14203             this.el.removeClass('active');
14204             
14205         } else  if (!this.el.hasClass('active')) {
14206             this.el.addClass('active');
14207         }
14208         this.fireEvent('changed', this, state);
14209     }
14210     
14211     
14212 });
14213  
14214
14215  
14216
14217  /*
14218  * - LGPL
14219  *
14220  * DateField
14221  * 
14222  */
14223
14224 /**
14225  * @class Roo.bootstrap.DateField
14226  * @extends Roo.bootstrap.Input
14227  * Bootstrap DateField class
14228  * @cfg {Number} weekStart default 0
14229  * @cfg {Number} weekStart default 0
14230  * @cfg {Number} viewMode default empty, (months|years)
14231  * @cfg {Number} minViewMode default empty, (months|years)
14232  * @cfg {Number} startDate default -Infinity
14233  * @cfg {Number} endDate default Infinity
14234  * @cfg {Boolean} todayHighlight default false
14235  * @cfg {Boolean} todayBtn default false
14236  * @cfg {Boolean} calendarWeeks default false
14237  * @cfg {Object} daysOfWeekDisabled default empty
14238  * 
14239  * @cfg {Boolean} keyboardNavigation default true
14240  * @cfg {String} language default en
14241  * 
14242  * @constructor
14243  * Create a new DateField
14244  * @param {Object} config The config object
14245  */
14246
14247 Roo.bootstrap.DateField = function(config){
14248     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
14249      this.addEvents({
14250             /**
14251              * @event show
14252              * Fires when this field show.
14253              * @param {Roo.bootstrap.DateField} this
14254              * @param {Mixed} date The date value
14255              */
14256             show : true,
14257             /**
14258              * @event show
14259              * Fires when this field hide.
14260              * @param {Roo.bootstrap.DateField} this
14261              * @param {Mixed} date The date value
14262              */
14263             hide : true,
14264             /**
14265              * @event select
14266              * Fires when select a date.
14267              * @param {Roo.bootstrap.DateField} this
14268              * @param {Mixed} date The date value
14269              */
14270             select : true
14271         });
14272 };
14273
14274 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
14275     
14276     /**
14277      * @cfg {String} format
14278      * The default date format string which can be overriden for localization support.  The format must be
14279      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
14280      */
14281     format : "m/d/y",
14282     /**
14283      * @cfg {String} altFormats
14284      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
14285      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
14286      */
14287     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
14288     
14289     weekStart : 0,
14290     
14291     viewMode : '',
14292     
14293     minViewMode : '',
14294     
14295     todayHighlight : false,
14296     
14297     todayBtn: false,
14298     
14299     language: 'en',
14300     
14301     keyboardNavigation: true,
14302     
14303     calendarWeeks: false,
14304     
14305     startDate: -Infinity,
14306     
14307     endDate: Infinity,
14308     
14309     daysOfWeekDisabled: [],
14310     
14311     _events: [],
14312     
14313     UTCDate: function()
14314     {
14315         return new Date(Date.UTC.apply(Date, arguments));
14316     },
14317     
14318     UTCToday: function()
14319     {
14320         var today = new Date();
14321         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
14322     },
14323     
14324     getDate: function() {
14325             var d = this.getUTCDate();
14326             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
14327     },
14328     
14329     getUTCDate: function() {
14330             return this.date;
14331     },
14332     
14333     setDate: function(d) {
14334             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
14335     },
14336     
14337     setUTCDate: function(d) {
14338             this.date = d;
14339             this.setValue(this.formatDate(this.date));
14340     },
14341         
14342     onRender: function(ct, position)
14343     {
14344         
14345         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
14346         
14347         this.language = this.language || 'en';
14348         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
14349         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
14350         
14351         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
14352         this.format = this.format || 'm/d/y';
14353         this.isInline = false;
14354         this.isInput = true;
14355         this.component = this.el.select('.add-on', true).first() || false;
14356         this.component = (this.component && this.component.length === 0) ? false : this.component;
14357         this.hasInput = this.component && this.inputEL().length;
14358         
14359         if (typeof(this.minViewMode === 'string')) {
14360             switch (this.minViewMode) {
14361                 case 'months':
14362                     this.minViewMode = 1;
14363                     break;
14364                 case 'years':
14365                     this.minViewMode = 2;
14366                     break;
14367                 default:
14368                     this.minViewMode = 0;
14369                     break;
14370             }
14371         }
14372         
14373         if (typeof(this.viewMode === 'string')) {
14374             switch (this.viewMode) {
14375                 case 'months':
14376                     this.viewMode = 1;
14377                     break;
14378                 case 'years':
14379                     this.viewMode = 2;
14380                     break;
14381                 default:
14382                     this.viewMode = 0;
14383                     break;
14384             }
14385         }
14386                 
14387         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
14388         
14389 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
14390         
14391         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14392         
14393         this.picker().on('mousedown', this.onMousedown, this);
14394         this.picker().on('click', this.onClick, this);
14395         
14396         this.picker().addClass('datepicker-dropdown');
14397         
14398         this.startViewMode = this.viewMode;
14399         
14400         
14401         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
14402             if(!this.calendarWeeks){
14403                 v.remove();
14404                 return;
14405             };
14406             
14407             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
14408             v.attr('colspan', function(i, val){
14409                 return parseInt(val) + 1;
14410             });
14411         })
14412                         
14413         
14414         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
14415         
14416         this.setStartDate(this.startDate);
14417         this.setEndDate(this.endDate);
14418         
14419         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
14420         
14421         this.fillDow();
14422         this.fillMonths();
14423         this.update();
14424         this.showMode();
14425         
14426         if(this.isInline) {
14427             this.show();
14428         }
14429     },
14430     
14431     picker : function()
14432     {
14433         return this.pickerEl;
14434 //        return this.el.select('.datepicker', true).first();
14435     },
14436     
14437     fillDow: function()
14438     {
14439         var dowCnt = this.weekStart;
14440         
14441         var dow = {
14442             tag: 'tr',
14443             cn: [
14444                 
14445             ]
14446         };
14447         
14448         if(this.calendarWeeks){
14449             dow.cn.push({
14450                 tag: 'th',
14451                 cls: 'cw',
14452                 html: '&nbsp;'
14453             })
14454         }
14455         
14456         while (dowCnt < this.weekStart + 7) {
14457             dow.cn.push({
14458                 tag: 'th',
14459                 cls: 'dow',
14460                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
14461             });
14462         }
14463         
14464         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
14465     },
14466     
14467     fillMonths: function()
14468     {    
14469         var i = 0
14470         var months = this.picker().select('>.datepicker-months td', true).first();
14471         
14472         months.dom.innerHTML = '';
14473         
14474         while (i < 12) {
14475             var month = {
14476                 tag: 'span',
14477                 cls: 'month',
14478                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
14479             }
14480             
14481             months.createChild(month);
14482         }
14483         
14484     },
14485     
14486     update: function()
14487     {
14488         
14489         this.date = (typeof(this.date) === 'undefined' || !this.date.length) ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
14490         
14491         if (this.date < this.startDate) {
14492             this.viewDate = new Date(this.startDate);
14493         } else if (this.date > this.endDate) {
14494             this.viewDate = new Date(this.endDate);
14495         } else {
14496             this.viewDate = new Date(this.date);
14497         }
14498         
14499         this.fill();
14500     },
14501     
14502     fill: function() 
14503     {
14504         var d = new Date(this.viewDate),
14505                 year = d.getUTCFullYear(),
14506                 month = d.getUTCMonth(),
14507                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
14508                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
14509                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
14510                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
14511                 currentDate = this.date && this.date.valueOf(),
14512                 today = this.UTCToday();
14513         
14514         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
14515         
14516 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
14517         
14518 //        this.picker.select('>tfoot th.today').
14519 //                                              .text(dates[this.language].today)
14520 //                                              .toggle(this.todayBtn !== false);
14521     
14522         this.updateNavArrows();
14523         this.fillMonths();
14524                                                 
14525         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
14526         
14527         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
14528          
14529         prevMonth.setUTCDate(day);
14530         
14531         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
14532         
14533         var nextMonth = new Date(prevMonth);
14534         
14535         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
14536         
14537         nextMonth = nextMonth.valueOf();
14538         
14539         var fillMonths = false;
14540         
14541         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
14542         
14543         while(prevMonth.valueOf() < nextMonth) {
14544             var clsName = '';
14545             
14546             if (prevMonth.getUTCDay() === this.weekStart) {
14547                 if(fillMonths){
14548                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
14549                 }
14550                     
14551                 fillMonths = {
14552                     tag: 'tr',
14553                     cn: []
14554                 };
14555                 
14556                 if(this.calendarWeeks){
14557                     // ISO 8601: First week contains first thursday.
14558                     // ISO also states week starts on Monday, but we can be more abstract here.
14559                     var
14560                     // Start of current week: based on weekstart/current date
14561                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
14562                     // Thursday of this week
14563                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
14564                     // First Thursday of year, year from thursday
14565                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
14566                     // Calendar week: ms between thursdays, div ms per day, div 7 days
14567                     calWeek =  (th - yth) / 864e5 / 7 + 1;
14568                     
14569                     fillMonths.cn.push({
14570                         tag: 'td',
14571                         cls: 'cw',
14572                         html: calWeek
14573                     });
14574                 }
14575             }
14576             
14577             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
14578                 clsName += ' old';
14579             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
14580                 clsName += ' new';
14581             }
14582             if (this.todayHighlight &&
14583                 prevMonth.getUTCFullYear() == today.getFullYear() &&
14584                 prevMonth.getUTCMonth() == today.getMonth() &&
14585                 prevMonth.getUTCDate() == today.getDate()) {
14586                 clsName += ' today';
14587             }
14588             
14589             if (currentDate && prevMonth.valueOf() === currentDate) {
14590                 clsName += ' active';
14591             }
14592             
14593             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
14594                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
14595                     clsName += ' disabled';
14596             }
14597             
14598             fillMonths.cn.push({
14599                 tag: 'td',
14600                 cls: 'day ' + clsName,
14601                 html: prevMonth.getDate()
14602             })
14603             
14604             prevMonth.setDate(prevMonth.getDate()+1);
14605         }
14606           
14607         var currentYear = this.date && this.date.getUTCFullYear();
14608         var currentMonth = this.date && this.date.getUTCMonth();
14609         
14610         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
14611         
14612         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
14613             v.removeClass('active');
14614             
14615             if(currentYear === year && k === currentMonth){
14616                 v.addClass('active');
14617             }
14618             
14619             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
14620                 v.addClass('disabled');
14621             }
14622             
14623         });
14624         
14625         
14626         year = parseInt(year/10, 10) * 10;
14627         
14628         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
14629         
14630         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
14631         
14632         year -= 1;
14633         for (var i = -1; i < 11; i++) {
14634             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
14635                 tag: 'span',
14636                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
14637                 html: year
14638             })
14639             
14640             year += 1;
14641         }
14642     },
14643     
14644     showMode: function(dir) 
14645     {
14646         if (dir) {
14647             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
14648         }
14649         Roo.each(this.picker().select('>div',true).elements, function(v){
14650             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14651             v.hide();
14652         });
14653         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
14654     },
14655     
14656     place: function()
14657     {
14658         if(this.isInline) return;
14659         
14660         this.picker().removeClass(['bottom', 'top']);
14661         
14662         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
14663             /*
14664              * place to the top of element!
14665              *
14666              */
14667             
14668             this.picker().addClass('top');
14669             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
14670             
14671             return;
14672         }
14673         
14674         this.picker().addClass('bottom');
14675         
14676         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
14677     },
14678     
14679     parseDate : function(value)
14680     {
14681         if(!value || value instanceof Date){
14682             return value;
14683         }
14684         var v = Date.parseDate(value, this.format);
14685         if (!v && this.useIso) {
14686             v = Date.parseDate(value, 'Y-m-d');
14687         }
14688         if(!v && this.altFormats){
14689             if(!this.altFormatsArray){
14690                 this.altFormatsArray = this.altFormats.split("|");
14691             }
14692             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
14693                 v = Date.parseDate(value, this.altFormatsArray[i]);
14694             }
14695         }
14696         return v;
14697     },
14698     
14699     formatDate : function(date, fmt)
14700     {
14701         return (!date || !(date instanceof Date)) ?
14702         date : date.dateFormat(fmt || this.format);
14703     },
14704     
14705     onFocus : function()
14706     {
14707         Roo.bootstrap.DateField.superclass.onFocus.call(this);
14708         this.show();
14709     },
14710     
14711     onBlur : function()
14712     {
14713         Roo.bootstrap.DateField.superclass.onBlur.call(this);
14714         
14715         var d = this.inputEl().getValue();
14716         
14717         this.setValue(d);
14718                 
14719         this.hide();
14720     },
14721     
14722     show : function()
14723     {
14724         this.picker().show();
14725         this.update();
14726         this.place();
14727         
14728         this.fireEvent('show', this, this.date);
14729     },
14730     
14731     hide : function()
14732     {
14733         if(this.isInline) return;
14734         this.picker().hide();
14735         this.viewMode = this.startViewMode;
14736         this.showMode();
14737         
14738         this.fireEvent('hide', this, this.date);
14739         
14740     },
14741     
14742     onMousedown: function(e)
14743     {
14744         e.stopPropagation();
14745         e.preventDefault();
14746     },
14747     
14748     keyup: function(e)
14749     {
14750         Roo.bootstrap.DateField.superclass.keyup.call(this);
14751         this.update();
14752     },
14753
14754     setValue: function(v)
14755     {
14756         var d = new Date(v);
14757         
14758         if(isNaN(d.getTime())){
14759             this.date = '';
14760             return;
14761         }
14762         
14763         v = this.formatDate(d);
14764         
14765         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
14766         
14767         this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
14768
14769         this.update();
14770
14771         this.fireEvent('select', this, this.date);
14772         
14773     },
14774     
14775     getValue: function()
14776     {
14777         return this.formatDate(this.date);
14778     },
14779     
14780     fireKey: function(e)
14781     {
14782         if (!this.picker().isVisible()){
14783             if (e.keyCode == 27) // allow escape to hide and re-show picker
14784                 this.show();
14785             return;
14786         }
14787         
14788         var dateChanged = false,
14789         dir, day, month,
14790         newDate, newViewDate;
14791         
14792         switch(e.keyCode){
14793             case 27: // escape
14794                 this.hide();
14795                 e.preventDefault();
14796                 break;
14797             case 37: // left
14798             case 39: // right
14799                 if (!this.keyboardNavigation) break;
14800                 dir = e.keyCode == 37 ? -1 : 1;
14801                 
14802                 if (e.ctrlKey){
14803                     newDate = this.moveYear(this.date, dir);
14804                     newViewDate = this.moveYear(this.viewDate, dir);
14805                 } else if (e.shiftKey){
14806                     newDate = this.moveMonth(this.date, dir);
14807                     newViewDate = this.moveMonth(this.viewDate, dir);
14808                 } else {
14809                     newDate = new Date(this.date);
14810                     newDate.setUTCDate(this.date.getUTCDate() + dir);
14811                     newViewDate = new Date(this.viewDate);
14812                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
14813                 }
14814                 if (this.dateWithinRange(newDate)){
14815                     this.date = newDate;
14816                     this.viewDate = newViewDate;
14817                     this.setValue(this.formatDate(this.date));
14818 //                    this.update();
14819                     e.preventDefault();
14820                     dateChanged = true;
14821                 }
14822                 break;
14823             case 38: // up
14824             case 40: // down
14825                 if (!this.keyboardNavigation) break;
14826                 dir = e.keyCode == 38 ? -1 : 1;
14827                 if (e.ctrlKey){
14828                     newDate = this.moveYear(this.date, dir);
14829                     newViewDate = this.moveYear(this.viewDate, dir);
14830                 } else if (e.shiftKey){
14831                     newDate = this.moveMonth(this.date, dir);
14832                     newViewDate = this.moveMonth(this.viewDate, dir);
14833                 } else {
14834                     newDate = new Date(this.date);
14835                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
14836                     newViewDate = new Date(this.viewDate);
14837                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
14838                 }
14839                 if (this.dateWithinRange(newDate)){
14840                     this.date = newDate;
14841                     this.viewDate = newViewDate;
14842                     this.setValue(this.formatDate(this.date));
14843 //                    this.update();
14844                     e.preventDefault();
14845                     dateChanged = true;
14846                 }
14847                 break;
14848             case 13: // enter
14849                 this.setValue(this.formatDate(this.date));
14850                 this.hide();
14851                 e.preventDefault();
14852                 break;
14853             case 9: // tab
14854                 this.setValue(this.formatDate(this.date));
14855                 this.hide();
14856                 break;
14857             case 16: // shift
14858             case 17: // ctrl
14859             case 18: // alt
14860                 break;
14861             default :
14862                 this.hide();
14863                 
14864         }
14865     },
14866     
14867     
14868     onClick: function(e) 
14869     {
14870         e.stopPropagation();
14871         e.preventDefault();
14872         
14873         var target = e.getTarget();
14874         
14875         if(target.nodeName.toLowerCase() === 'i'){
14876             target = Roo.get(target).dom.parentNode;
14877         }
14878         
14879         var nodeName = target.nodeName;
14880         var className = target.className;
14881         var html = target.innerHTML;
14882         
14883         switch(nodeName.toLowerCase()) {
14884             case 'th':
14885                 switch(className) {
14886                     case 'switch':
14887                         this.showMode(1);
14888                         break;
14889                     case 'prev':
14890                     case 'next':
14891                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
14892                         switch(this.viewMode){
14893                                 case 0:
14894                                         this.viewDate = this.moveMonth(this.viewDate, dir);
14895                                         break;
14896                                 case 1:
14897                                 case 2:
14898                                         this.viewDate = this.moveYear(this.viewDate, dir);
14899                                         break;
14900                         }
14901                         this.fill();
14902                         break;
14903                     case 'today':
14904                         var date = new Date();
14905                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
14906 //                        this.fill()
14907                         this.setValue(this.formatDate(this.date));
14908                         
14909                         this.hide();
14910                         break;
14911                 }
14912                 break;
14913             case 'span':
14914                 if (className.indexOf('disabled') === -1) {
14915                     this.viewDate.setUTCDate(1);
14916                     if (className.indexOf('month') !== -1) {
14917                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
14918                     } else {
14919                         var year = parseInt(html, 10) || 0;
14920                         this.viewDate.setUTCFullYear(year);
14921                         
14922                     }
14923                     this.showMode(-1);
14924                     this.fill();
14925                 }
14926                 break;
14927                 
14928             case 'td':
14929                 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
14930                     var day = parseInt(html, 10) || 1;
14931                     var year = this.viewDate.getUTCFullYear(),
14932                         month = this.viewDate.getUTCMonth();
14933
14934                     if (className.indexOf('old') !== -1) {
14935                         if(month === 0 ){
14936                             month = 11;
14937                             year -= 1;
14938                         }else{
14939                             month -= 1;
14940                         }
14941                     } else if (className.indexOf('new') !== -1) {
14942                         if (month == 11) {
14943                             month = 0;
14944                             year += 1;
14945                         } else {
14946                             month += 1;
14947                         }
14948                     }
14949                     this.date = this.UTCDate(year, month, day,0,0,0,0);
14950                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
14951 //                    this.fill();
14952                     this.setValue(this.formatDate(this.date));
14953                     this.hide();
14954                 }
14955                 break;
14956         }
14957     },
14958     
14959     setStartDate: function(startDate)
14960     {
14961         this.startDate = startDate || -Infinity;
14962         if (this.startDate !== -Infinity) {
14963             this.startDate = this.parseDate(this.startDate);
14964         }
14965         this.update();
14966         this.updateNavArrows();
14967     },
14968
14969     setEndDate: function(endDate)
14970     {
14971         this.endDate = endDate || Infinity;
14972         if (this.endDate !== Infinity) {
14973             this.endDate = this.parseDate(this.endDate);
14974         }
14975         this.update();
14976         this.updateNavArrows();
14977     },
14978     
14979     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
14980     {
14981         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
14982         if (typeof(this.daysOfWeekDisabled) !== 'object') {
14983             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
14984         }
14985         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
14986             return parseInt(d, 10);
14987         });
14988         this.update();
14989         this.updateNavArrows();
14990     },
14991     
14992     updateNavArrows: function() 
14993     {
14994         var d = new Date(this.viewDate),
14995         year = d.getUTCFullYear(),
14996         month = d.getUTCMonth();
14997         
14998         Roo.each(this.picker().select('.prev', true).elements, function(v){
14999             v.show();
15000             switch (this.viewMode) {
15001                 case 0:
15002
15003                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
15004                         v.hide();
15005                     }
15006                     break;
15007                 case 1:
15008                 case 2:
15009                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
15010                         v.hide();
15011                     }
15012                     break;
15013             }
15014         });
15015         
15016         Roo.each(this.picker().select('.next', true).elements, function(v){
15017             v.show();
15018             switch (this.viewMode) {
15019                 case 0:
15020
15021                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
15022                         v.hide();
15023                     }
15024                     break;
15025                 case 1:
15026                 case 2:
15027                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
15028                         v.hide();
15029                     }
15030                     break;
15031             }
15032         })
15033     },
15034     
15035     moveMonth: function(date, dir)
15036     {
15037         if (!dir) return date;
15038         var new_date = new Date(date.valueOf()),
15039         day = new_date.getUTCDate(),
15040         month = new_date.getUTCMonth(),
15041         mag = Math.abs(dir),
15042         new_month, test;
15043         dir = dir > 0 ? 1 : -1;
15044         if (mag == 1){
15045             test = dir == -1
15046             // If going back one month, make sure month is not current month
15047             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
15048             ? function(){
15049                 return new_date.getUTCMonth() == month;
15050             }
15051             // If going forward one month, make sure month is as expected
15052             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
15053             : function(){
15054                 return new_date.getUTCMonth() != new_month;
15055             };
15056             new_month = month + dir;
15057             new_date.setUTCMonth(new_month);
15058             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
15059             if (new_month < 0 || new_month > 11)
15060                 new_month = (new_month + 12) % 12;
15061         } else {
15062             // For magnitudes >1, move one month at a time...
15063             for (var i=0; i<mag; i++)
15064                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
15065                 new_date = this.moveMonth(new_date, dir);
15066             // ...then reset the day, keeping it in the new month
15067             new_month = new_date.getUTCMonth();
15068             new_date.setUTCDate(day);
15069             test = function(){
15070                 return new_month != new_date.getUTCMonth();
15071             };
15072         }
15073         // Common date-resetting loop -- if date is beyond end of month, make it
15074         // end of month
15075         while (test()){
15076             new_date.setUTCDate(--day);
15077             new_date.setUTCMonth(new_month);
15078         }
15079         return new_date;
15080     },
15081
15082     moveYear: function(date, dir)
15083     {
15084         return this.moveMonth(date, dir*12);
15085     },
15086
15087     dateWithinRange: function(date)
15088     {
15089         return date >= this.startDate && date <= this.endDate;
15090     },
15091
15092     
15093     remove: function() 
15094     {
15095         this.picker().remove();
15096     }
15097    
15098 });
15099
15100 Roo.apply(Roo.bootstrap.DateField,  {
15101     
15102     head : {
15103         tag: 'thead',
15104         cn: [
15105         {
15106             tag: 'tr',
15107             cn: [
15108             {
15109                 tag: 'th',
15110                 cls: 'prev',
15111                 html: '<i class="fa fa-arrow-left"/>'
15112             },
15113             {
15114                 tag: 'th',
15115                 cls: 'switch',
15116                 colspan: '5'
15117             },
15118             {
15119                 tag: 'th',
15120                 cls: 'next',
15121                 html: '<i class="fa fa-arrow-right"/>'
15122             }
15123
15124             ]
15125         }
15126         ]
15127     },
15128     
15129     content : {
15130         tag: 'tbody',
15131         cn: [
15132         {
15133             tag: 'tr',
15134             cn: [
15135             {
15136                 tag: 'td',
15137                 colspan: '7'
15138             }
15139             ]
15140         }
15141         ]
15142     },
15143     
15144     footer : {
15145         tag: 'tfoot',
15146         cn: [
15147         {
15148             tag: 'tr',
15149             cn: [
15150             {
15151                 tag: 'th',
15152                 colspan: '7',
15153                 cls: 'today'
15154             }
15155                     
15156             ]
15157         }
15158         ]
15159     },
15160     
15161     dates:{
15162         en: {
15163             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
15164             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
15165             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
15166             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
15167             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
15168             today: "Today"
15169         }
15170     },
15171     
15172     modes: [
15173     {
15174         clsName: 'days',
15175         navFnc: 'Month',
15176         navStep: 1
15177     },
15178     {
15179         clsName: 'months',
15180         navFnc: 'FullYear',
15181         navStep: 1
15182     },
15183     {
15184         clsName: 'years',
15185         navFnc: 'FullYear',
15186         navStep: 10
15187     }]
15188 });
15189
15190 Roo.apply(Roo.bootstrap.DateField,  {
15191   
15192     template : {
15193         tag: 'div',
15194         cls: 'datepicker dropdown-menu',
15195         cn: [
15196         {
15197             tag: 'div',
15198             cls: 'datepicker-days',
15199             cn: [
15200             {
15201                 tag: 'table',
15202                 cls: 'table-condensed',
15203                 cn:[
15204                 Roo.bootstrap.DateField.head,
15205                 {
15206                     tag: 'tbody'
15207                 },
15208                 Roo.bootstrap.DateField.footer
15209                 ]
15210             }
15211             ]
15212         },
15213         {
15214             tag: 'div',
15215             cls: 'datepicker-months',
15216             cn: [
15217             {
15218                 tag: 'table',
15219                 cls: 'table-condensed',
15220                 cn:[
15221                 Roo.bootstrap.DateField.head,
15222                 Roo.bootstrap.DateField.content,
15223                 Roo.bootstrap.DateField.footer
15224                 ]
15225             }
15226             ]
15227         },
15228         {
15229             tag: 'div',
15230             cls: 'datepicker-years',
15231             cn: [
15232             {
15233                 tag: 'table',
15234                 cls: 'table-condensed',
15235                 cn:[
15236                 Roo.bootstrap.DateField.head,
15237                 Roo.bootstrap.DateField.content,
15238                 Roo.bootstrap.DateField.footer
15239                 ]
15240             }
15241             ]
15242         }
15243         ]
15244     }
15245 });
15246
15247  
15248
15249  /*
15250  * - LGPL
15251  *
15252  * TimeField
15253  * 
15254  */
15255
15256 /**
15257  * @class Roo.bootstrap.TimeField
15258  * @extends Roo.bootstrap.Input
15259  * Bootstrap DateField class
15260  * 
15261  * 
15262  * @constructor
15263  * Create a new TimeField
15264  * @param {Object} config The config object
15265  */
15266
15267 Roo.bootstrap.TimeField = function(config){
15268     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
15269     this.addEvents({
15270             /**
15271              * @event show
15272              * Fires when this field show.
15273              * @param {Roo.bootstrap.DateField} this
15274              * @param {Mixed} date The date value
15275              */
15276             show : true,
15277             /**
15278              * @event show
15279              * Fires when this field hide.
15280              * @param {Roo.bootstrap.DateField} this
15281              * @param {Mixed} date The date value
15282              */
15283             hide : true,
15284             /**
15285              * @event select
15286              * Fires when select a date.
15287              * @param {Roo.bootstrap.DateField} this
15288              * @param {Mixed} date The date value
15289              */
15290             select : true
15291         });
15292 };
15293
15294 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
15295     
15296     /**
15297      * @cfg {String} format
15298      * The default time format string which can be overriden for localization support.  The format must be
15299      * valid according to {@link Date#parseDate} (defaults to 'H:i').
15300      */
15301     format : "H:i",
15302        
15303     onRender: function(ct, position)
15304     {
15305         
15306         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
15307                 
15308         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
15309         
15310         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15311         
15312         this.pop = this.picker().select('>.datepicker-time',true).first();
15313         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block' 
15314         
15315         this.picker().on('mousedown', this.onMousedown, this);
15316         this.picker().on('click', this.onClick, this);
15317         
15318         this.picker().addClass('datepicker-dropdown');
15319     
15320         this.fillTime();
15321         this.update();
15322             
15323         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
15324         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
15325         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
15326         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
15327         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
15328         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
15329
15330     },
15331     
15332     fireKey: function(e){
15333         if (!this.picker().isVisible()){
15334             if (e.keyCode == 27) // allow escape to hide and re-show picker
15335                 this.show();
15336             return;
15337         }
15338
15339         e.preventDefault();
15340         
15341         switch(e.keyCode){
15342             case 27: // escape
15343                 this.hide();
15344                 break;
15345             case 37: // left
15346             case 39: // right
15347                 this.onTogglePeriod();
15348                 break;
15349             case 38: // up
15350                 this.onIncrementMinutes();
15351                 break;
15352             case 40: // down
15353                 this.onDecrementMinutes();
15354                 break;
15355             case 13: // enter
15356             case 9: // tab
15357                 this.setTime();
15358                 break;
15359         }
15360     },
15361     
15362     onClick: function(e) {
15363         e.stopPropagation();
15364         e.preventDefault();
15365     },
15366     
15367     picker : function()
15368     {
15369         return this.el.select('.datepicker', true).first();
15370     },
15371     
15372     fillTime: function()
15373     {    
15374         var time = this.pop.select('tbody', true).first();
15375         
15376         time.dom.innerHTML = '';
15377         
15378         time.createChild({
15379             tag: 'tr',
15380             cn: [
15381                 {
15382                     tag: 'td',
15383                     cn: [
15384                         {
15385                             tag: 'a',
15386                             href: '#',
15387                             cls: 'btn',
15388                             cn: [
15389                                 {
15390                                     tag: 'span',
15391                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
15392                                 }
15393                             ]
15394                         } 
15395                     ]
15396                 },
15397                 {
15398                     tag: 'td',
15399                     cls: 'separator'
15400                 },
15401                 {
15402                     tag: 'td',
15403                     cn: [
15404                         {
15405                             tag: 'a',
15406                             href: '#',
15407                             cls: 'btn',
15408                             cn: [
15409                                 {
15410                                     tag: 'span',
15411                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
15412                                 }
15413                             ]
15414                         }
15415                     ]
15416                 },
15417                 {
15418                     tag: 'td',
15419                     cls: 'separator'
15420                 }
15421             ]
15422         });
15423         
15424         time.createChild({
15425             tag: 'tr',
15426             cn: [
15427                 {
15428                     tag: 'td',
15429                     cn: [
15430                         {
15431                             tag: 'span',
15432                             cls: 'timepicker-hour',
15433                             html: '00'
15434                         }  
15435                     ]
15436                 },
15437                 {
15438                     tag: 'td',
15439                     cls: 'separator',
15440                     html: ':'
15441                 },
15442                 {
15443                     tag: 'td',
15444                     cn: [
15445                         {
15446                             tag: 'span',
15447                             cls: 'timepicker-minute',
15448                             html: '00'
15449                         }  
15450                     ]
15451                 },
15452                 {
15453                     tag: 'td',
15454                     cls: 'separator'
15455                 },
15456                 {
15457                     tag: 'td',
15458                     cn: [
15459                         {
15460                             tag: 'button',
15461                             type: 'button',
15462                             cls: 'btn btn-primary period',
15463                             html: 'AM'
15464                             
15465                         }
15466                     ]
15467                 }
15468             ]
15469         });
15470         
15471         time.createChild({
15472             tag: 'tr',
15473             cn: [
15474                 {
15475                     tag: 'td',
15476                     cn: [
15477                         {
15478                             tag: 'a',
15479                             href: '#',
15480                             cls: 'btn',
15481                             cn: [
15482                                 {
15483                                     tag: 'span',
15484                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
15485                                 }
15486                             ]
15487                         }
15488                     ]
15489                 },
15490                 {
15491                     tag: 'td',
15492                     cls: 'separator'
15493                 },
15494                 {
15495                     tag: 'td',
15496                     cn: [
15497                         {
15498                             tag: 'a',
15499                             href: '#',
15500                             cls: 'btn',
15501                             cn: [
15502                                 {
15503                                     tag: 'span',
15504                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
15505                                 }
15506                             ]
15507                         }
15508                     ]
15509                 },
15510                 {
15511                     tag: 'td',
15512                     cls: 'separator'
15513                 }
15514             ]
15515         });
15516         
15517     },
15518     
15519     update: function()
15520     {
15521         
15522         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
15523         
15524         this.fill();
15525     },
15526     
15527     fill: function() 
15528     {
15529         var hours = this.time.getHours();
15530         var minutes = this.time.getMinutes();
15531         var period = 'AM';
15532         
15533         if(hours > 11){
15534             period = 'PM';
15535         }
15536         
15537         if(hours == 0){
15538             hours = 12;
15539         }
15540         
15541         
15542         if(hours > 12){
15543             hours = hours - 12;
15544         }
15545         
15546         if(hours < 10){
15547             hours = '0' + hours;
15548         }
15549         
15550         if(minutes < 10){
15551             minutes = '0' + minutes;
15552         }
15553         
15554         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
15555         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
15556         this.pop.select('button', true).first().dom.innerHTML = period;
15557         
15558     },
15559     
15560     place: function()
15561     {   
15562         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
15563         
15564         var cls = ['bottom'];
15565         
15566         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
15567             cls.pop();
15568             cls.push('top');
15569         }
15570         
15571         cls.push('right');
15572         
15573         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
15574             cls.pop();
15575             cls.push('left');
15576         }
15577         
15578         this.picker().addClass(cls.join('-'));
15579         
15580         var _this = this;
15581         
15582         Roo.each(cls, function(c){
15583             if(c == 'bottom'){
15584                 _this.picker().setTop(_this.inputEl().getHeight());
15585                 return;
15586             }
15587             if(c == 'top'){
15588                 _this.picker().setTop(0 - _this.picker().getHeight());
15589                 return;
15590             }
15591             
15592             if(c == 'left'){
15593                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
15594                 return;
15595             }
15596             if(c == 'right'){
15597                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
15598                 return;
15599             }
15600         });
15601         
15602     },
15603   
15604     onFocus : function()
15605     {
15606         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
15607         this.show();
15608     },
15609     
15610     onBlur : function()
15611     {
15612         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
15613         this.hide();
15614     },
15615     
15616     show : function()
15617     {
15618         this.picker().show();
15619         this.pop.show();
15620         this.update();
15621         this.place();
15622         
15623         this.fireEvent('show', this, this.date);
15624     },
15625     
15626     hide : function()
15627     {
15628         this.picker().hide();
15629         this.pop.hide();
15630         
15631         this.fireEvent('hide', this, this.date);
15632     },
15633     
15634     setTime : function()
15635     {
15636         this.hide();
15637         this.setValue(this.time.format(this.format));
15638         
15639         this.fireEvent('select', this, this.date);
15640         
15641         
15642     },
15643     
15644     onMousedown: function(e){
15645         e.stopPropagation();
15646         e.preventDefault();
15647     },
15648     
15649     onIncrementHours: function()
15650     {
15651         Roo.log('onIncrementHours');
15652         this.time = this.time.add(Date.HOUR, 1);
15653         this.update();
15654         
15655     },
15656     
15657     onDecrementHours: function()
15658     {
15659         Roo.log('onDecrementHours');
15660         this.time = this.time.add(Date.HOUR, -1);
15661         this.update();
15662     },
15663     
15664     onIncrementMinutes: function()
15665     {
15666         Roo.log('onIncrementMinutes');
15667         this.time = this.time.add(Date.MINUTE, 1);
15668         this.update();
15669     },
15670     
15671     onDecrementMinutes: function()
15672     {
15673         Roo.log('onDecrementMinutes');
15674         this.time = this.time.add(Date.MINUTE, -1);
15675         this.update();
15676     },
15677     
15678     onTogglePeriod: function()
15679     {
15680         Roo.log('onTogglePeriod');
15681         this.time = this.time.add(Date.HOUR, 12);
15682         this.update();
15683     }
15684     
15685    
15686 });
15687
15688 Roo.apply(Roo.bootstrap.TimeField,  {
15689     
15690     content : {
15691         tag: 'tbody',
15692         cn: [
15693             {
15694                 tag: 'tr',
15695                 cn: [
15696                 {
15697                     tag: 'td',
15698                     colspan: '7'
15699                 }
15700                 ]
15701             }
15702         ]
15703     },
15704     
15705     footer : {
15706         tag: 'tfoot',
15707         cn: [
15708             {
15709                 tag: 'tr',
15710                 cn: [
15711                 {
15712                     tag: 'th',
15713                     colspan: '7',
15714                     cls: '',
15715                     cn: [
15716                         {
15717                             tag: 'button',
15718                             cls: 'btn btn-info ok',
15719                             html: 'OK'
15720                         }
15721                     ]
15722                 }
15723
15724                 ]
15725             }
15726         ]
15727     }
15728 });
15729
15730 Roo.apply(Roo.bootstrap.TimeField,  {
15731   
15732     template : {
15733         tag: 'div',
15734         cls: 'datepicker dropdown-menu',
15735         cn: [
15736             {
15737                 tag: 'div',
15738                 cls: 'datepicker-time',
15739                 cn: [
15740                 {
15741                     tag: 'table',
15742                     cls: 'table-condensed',
15743                     cn:[
15744                     Roo.bootstrap.TimeField.content,
15745                     Roo.bootstrap.TimeField.footer
15746                     ]
15747                 }
15748                 ]
15749             }
15750         ]
15751     }
15752 });
15753
15754  
15755
15756  /*
15757  * - LGPL
15758  *
15759  * CheckBox
15760  * 
15761  */
15762
15763 /**
15764  * @class Roo.bootstrap.CheckBox
15765  * @extends Roo.bootstrap.Input
15766  * Bootstrap CheckBox class
15767  * 
15768  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
15769  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
15770  * @cfg {String} boxLabel The text that appears beside the checkbox
15771  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
15772  * @cfg {Boolean} checked initnal the element
15773  * 
15774  * 
15775  * @constructor
15776  * Create a new CheckBox
15777  * @param {Object} config The config object
15778  */
15779
15780 Roo.bootstrap.CheckBox = function(config){
15781     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
15782    
15783         this.addEvents({
15784             /**
15785             * @event check
15786             * Fires when the element is checked or unchecked.
15787             * @param {Roo.bootstrap.CheckBox} this This input
15788             * @param {Boolean} checked The new checked value
15789             */
15790            check : true
15791         });
15792 };
15793
15794 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
15795     
15796     inputType: 'checkbox',
15797     inputValue: 1,
15798     valueOff: 0,
15799     boxLabel: false,
15800     checked: false,
15801     weight : false,
15802     
15803     getAutoCreate : function()
15804     {
15805         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
15806         
15807         var id = Roo.id();
15808         
15809         var cfg = {};
15810         
15811         cfg.cls = 'form-group checkbox' //input-group
15812         
15813         
15814         
15815         
15816         var input =  {
15817             tag: 'input',
15818             id : id,
15819             type : this.inputType,
15820             value : (!this.checked) ? this.valueOff : this.inputValue,
15821             cls : 'roo-checkbox', //'form-box',
15822             placeholder : this.placeholder || ''
15823             
15824         };
15825         
15826         if (this.weight) { // Validity check?
15827             cfg.cls += " checkbox-" + this.weight;
15828         }
15829         
15830         if (this.disabled) {
15831             input.disabled=true;
15832         }
15833         
15834         if(this.checked){
15835             input.checked = this.checked;
15836         }
15837         
15838         if (this.name) {
15839             input.name = this.name;
15840         }
15841         
15842         if (this.size) {
15843             input.cls += ' input-' + this.size;
15844         }
15845         
15846         var settings=this;
15847         ['xs','sm','md','lg'].map(function(size){
15848             if (settings[size]) {
15849                 cfg.cls += ' col-' + size + '-' + settings[size];
15850             }
15851         });
15852         
15853        
15854         
15855         var inputblock = input;
15856         
15857         
15858         
15859         
15860         if (this.before || this.after) {
15861             
15862             inputblock = {
15863                 cls : 'input-group',
15864                 cn :  [] 
15865             };
15866             if (this.before) {
15867                 inputblock.cn.push({
15868                     tag :'span',
15869                     cls : 'input-group-addon',
15870                     html : this.before
15871                 });
15872             }
15873             inputblock.cn.push(input);
15874             if (this.after) {
15875                 inputblock.cn.push({
15876                     tag :'span',
15877                     cls : 'input-group-addon',
15878                     html : this.after
15879                 });
15880             }
15881             
15882         };
15883         
15884         if (align ==='left' && this.fieldLabel.length) {
15885                 Roo.log("left and has label");
15886                 cfg.cn = [
15887                     
15888                     {
15889                         tag: 'label',
15890                         'for' :  id,
15891                         cls : 'control-label col-md-' + this.labelWidth,
15892                         html : this.fieldLabel
15893                         
15894                     },
15895                     {
15896                         cls : "col-md-" + (12 - this.labelWidth), 
15897                         cn: [
15898                             inputblock
15899                         ]
15900                     }
15901                     
15902                 ];
15903         } else if ( this.fieldLabel.length) {
15904                 Roo.log(" label");
15905                 cfg.cn = [
15906                    
15907                     {
15908                         tag: this.boxLabel ? 'span' : 'label',
15909                         'for': id,
15910                         cls: 'control-label box-input-label',
15911                         //cls : 'input-group-addon',
15912                         html : this.fieldLabel
15913                         
15914                     },
15915                     
15916                     inputblock
15917                     
15918                 ];
15919
15920         } else {
15921             
15922                 Roo.log(" no label && no align");
15923                 cfg.cn = [  inputblock ] ;
15924                 
15925                 
15926         };
15927          if(this.boxLabel){
15928             cfg.cn.push( {
15929                 tag: 'label',
15930                 'for': id,
15931                 cls: 'box-label',
15932                 html: this.boxLabel
15933                 
15934             });
15935         }
15936         
15937         
15938        
15939         return cfg;
15940         
15941     },
15942     
15943     /**
15944      * return the real input element.
15945      */
15946     inputEl: function ()
15947     {
15948         return this.el.select('input.roo-checkbox',true).first();
15949     },
15950     
15951     label: function()
15952     {
15953         return this.el.select('label.control-label',true).first();
15954     },
15955     
15956     initEvents : function()
15957     {
15958 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
15959         
15960         this.inputEl().on('click', this.onClick,  this);
15961         
15962     },
15963     
15964     onClick : function()
15965     {   
15966         this.setChecked(!this.checked);
15967     },
15968     
15969     setChecked : function(state,suppressEvent)
15970     {
15971         this.checked = state;
15972         
15973         this.inputEl().dom.checked = state;
15974         
15975         if(suppressEvent !== true){
15976             this.fireEvent('check', this, state);
15977         }
15978         
15979         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
15980         
15981     },
15982     
15983     setValue : function(v,suppressEvent)
15984     {
15985         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
15986     }
15987     
15988 });
15989
15990  
15991 /*
15992  * - LGPL
15993  *
15994  * Radio
15995  * 
15996  */
15997
15998 /**
15999  * @class Roo.bootstrap.Radio
16000  * @extends Roo.bootstrap.CheckBox
16001  * Bootstrap Radio class
16002
16003  * @constructor
16004  * Create a new Radio
16005  * @param {Object} config The config object
16006  */
16007
16008 Roo.bootstrap.Radio = function(config){
16009     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
16010    
16011 };
16012
16013 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
16014     
16015     inputType: 'radio',
16016     inputValue: '',
16017     valueOff: '',
16018     
16019     getAutoCreate : function()
16020     {
16021         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
16022         
16023         var id = Roo.id();
16024         
16025         var cfg = {};
16026         
16027         cfg.cls = 'form-group radio' //input-group
16028         
16029         var input =  {
16030             tag: 'input',
16031             id : id,
16032             type : this.inputType,
16033             value : (!this.checked) ? this.valueOff : this.inputValue,
16034             cls : 'roo-radio',
16035             placeholder : this.placeholder || ''
16036             
16037         };
16038           if (this.weight) { // Validity check?
16039             cfg.cls += " radio-" + this.weight;
16040         }
16041         if (this.disabled) {
16042             input.disabled=true;
16043         }
16044         
16045         if(this.checked){
16046             input.checked = this.checked;
16047         }
16048         
16049         if (this.name) {
16050             input.name = this.name;
16051         }
16052         
16053         if (this.size) {
16054             input.cls += ' input-' + this.size;
16055         }
16056         
16057         var settings=this;
16058         ['xs','sm','md','lg'].map(function(size){
16059             if (settings[size]) {
16060                 cfg.cls += ' col-' + size + '-' + settings[size];
16061             }
16062         });
16063         
16064         var inputblock = input;
16065         
16066         if (this.before || this.after) {
16067             
16068             inputblock = {
16069                 cls : 'input-group',
16070                 cn :  [] 
16071             };
16072             if (this.before) {
16073                 inputblock.cn.push({
16074                     tag :'span',
16075                     cls : 'input-group-addon',
16076                     html : this.before
16077                 });
16078             }
16079             inputblock.cn.push(input);
16080             if (this.after) {
16081                 inputblock.cn.push({
16082                     tag :'span',
16083                     cls : 'input-group-addon',
16084                     html : this.after
16085                 });
16086             }
16087             
16088         };
16089         
16090         if (align ==='left' && this.fieldLabel.length) {
16091                 Roo.log("left and has label");
16092                 cfg.cn = [
16093                     
16094                     {
16095                         tag: 'label',
16096                         'for' :  id,
16097                         cls : 'control-label col-md-' + this.labelWidth,
16098                         html : this.fieldLabel
16099                         
16100                     },
16101                     {
16102                         cls : "col-md-" + (12 - this.labelWidth), 
16103                         cn: [
16104                             inputblock
16105                         ]
16106                     }
16107                     
16108                 ];
16109         } else if ( this.fieldLabel.length) {
16110                 Roo.log(" label");
16111                  cfg.cn = [
16112                    
16113                     {
16114                         tag: 'label',
16115                         'for': id,
16116                         cls: 'control-label box-input-label',
16117                         //cls : 'input-group-addon',
16118                         html : this.fieldLabel
16119                         
16120                     },
16121                     
16122                     inputblock
16123                     
16124                 ];
16125
16126         } else {
16127             
16128                    Roo.log(" no label && no align");
16129                 cfg.cn = [
16130                     
16131                         inputblock
16132                     
16133                 ];
16134                 
16135                 
16136         };
16137         
16138         if(this.boxLabel){
16139             cfg.cn.push({
16140                 tag: 'label',
16141                 'for': id,
16142                 cls: 'box-label',
16143                 html: this.boxLabel
16144             })
16145         }
16146         
16147         return cfg;
16148         
16149     },
16150     inputEl: function ()
16151     {
16152         return this.el.select('input.roo-radio',true).first();
16153     },
16154     onClick : function()
16155     {   
16156         this.setChecked(true);
16157     },
16158     
16159     setChecked : function(state,suppressEvent)
16160     {
16161         if(state){
16162             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
16163                 v.dom.checked = false;
16164             });
16165         }
16166         
16167         this.checked = state;
16168         this.inputEl().dom.checked = state;
16169         
16170         if(suppressEvent !== true){
16171             this.fireEvent('check', this, state);
16172         }
16173         
16174         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
16175         
16176     },
16177     
16178     getGroupValue : function()
16179     {
16180         var value = ''
16181         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
16182             if(v.dom.checked == true){
16183                 value = v.dom.value;
16184             }
16185         });
16186         
16187         return value;
16188     },
16189     
16190     /**
16191      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
16192      * @return {Mixed} value The field value
16193      */
16194     getValue : function(){
16195         return this.getGroupValue();
16196     }
16197     
16198 });
16199
16200  
16201 //<script type="text/javascript">
16202
16203 /*
16204  * Based  Ext JS Library 1.1.1
16205  * Copyright(c) 2006-2007, Ext JS, LLC.
16206  * LGPL
16207  *
16208  */
16209  
16210 /**
16211  * @class Roo.HtmlEditorCore
16212  * @extends Roo.Component
16213  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
16214  *
16215  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
16216  */
16217
16218 Roo.HtmlEditorCore = function(config){
16219     
16220     
16221     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
16222     this.addEvents({
16223         /**
16224          * @event initialize
16225          * Fires when the editor is fully initialized (including the iframe)
16226          * @param {Roo.HtmlEditorCore} this
16227          */
16228         initialize: true,
16229         /**
16230          * @event activate
16231          * Fires when the editor is first receives the focus. Any insertion must wait
16232          * until after this event.
16233          * @param {Roo.HtmlEditorCore} this
16234          */
16235         activate: true,
16236          /**
16237          * @event beforesync
16238          * Fires before the textarea is updated with content from the editor iframe. Return false
16239          * to cancel the sync.
16240          * @param {Roo.HtmlEditorCore} this
16241          * @param {String} html
16242          */
16243         beforesync: true,
16244          /**
16245          * @event beforepush
16246          * Fires before the iframe editor is updated with content from the textarea. Return false
16247          * to cancel the push.
16248          * @param {Roo.HtmlEditorCore} this
16249          * @param {String} html
16250          */
16251         beforepush: true,
16252          /**
16253          * @event sync
16254          * Fires when the textarea is updated with content from the editor iframe.
16255          * @param {Roo.HtmlEditorCore} this
16256          * @param {String} html
16257          */
16258         sync: true,
16259          /**
16260          * @event push
16261          * Fires when the iframe editor is updated with content from the textarea.
16262          * @param {Roo.HtmlEditorCore} this
16263          * @param {String} html
16264          */
16265         push: true,
16266         
16267         /**
16268          * @event editorevent
16269          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
16270          * @param {Roo.HtmlEditorCore} this
16271          */
16272         editorevent: true
16273     });
16274      
16275 };
16276
16277
16278 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
16279
16280
16281      /**
16282      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
16283      */
16284     
16285     owner : false,
16286     
16287      /**
16288      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
16289      *                        Roo.resizable.
16290      */
16291     resizable : false,
16292      /**
16293      * @cfg {Number} height (in pixels)
16294      */   
16295     height: 300,
16296    /**
16297      * @cfg {Number} width (in pixels)
16298      */   
16299     width: 500,
16300     
16301     /**
16302      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
16303      * 
16304      */
16305     stylesheets: false,
16306     
16307     // id of frame..
16308     frameId: false,
16309     
16310     // private properties
16311     validationEvent : false,
16312     deferHeight: true,
16313     initialized : false,
16314     activated : false,
16315     sourceEditMode : false,
16316     onFocus : Roo.emptyFn,
16317     iframePad:3,
16318     hideMode:'offsets',
16319     
16320     clearUp: true,
16321     
16322      
16323     
16324
16325     /**
16326      * Protected method that will not generally be called directly. It
16327      * is called when the editor initializes the iframe with HTML contents. Override this method if you
16328      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
16329      */
16330     getDocMarkup : function(){
16331         // body styles..
16332         var st = '';
16333         Roo.log(this.stylesheets);
16334         
16335         // inherit styels from page...?? 
16336         if (this.stylesheets === false) {
16337             
16338             Roo.get(document.head).select('style').each(function(node) {
16339                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16340             });
16341             
16342             Roo.get(document.head).select('link').each(function(node) { 
16343                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16344             });
16345             
16346         } else if (!this.stylesheets.length) {
16347                 // simple..
16348                 st = '<style type="text/css">' +
16349                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16350                    '</style>';
16351         } else {
16352             Roo.each(this.stylesheets, function(s) {
16353                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
16354             });
16355             
16356         }
16357         
16358         st +=  '<style type="text/css">' +
16359             'IMG { cursor: pointer } ' +
16360         '</style>';
16361
16362         
16363         return '<html><head>' + st  +
16364             //<style type="text/css">' +
16365             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16366             //'</style>' +
16367             ' </head><body class="roo-htmleditor-body"></body></html>';
16368     },
16369
16370     // private
16371     onRender : function(ct, position)
16372     {
16373         var _t = this;
16374         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
16375         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
16376         
16377         
16378         this.el.dom.style.border = '0 none';
16379         this.el.dom.setAttribute('tabIndex', -1);
16380         this.el.addClass('x-hidden hide');
16381         
16382         
16383         
16384         if(Roo.isIE){ // fix IE 1px bogus margin
16385             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
16386         }
16387        
16388         
16389         this.frameId = Roo.id();
16390         
16391          
16392         
16393         var iframe = this.owner.wrap.createChild({
16394             tag: 'iframe',
16395             cls: 'form-control', // bootstrap..
16396             id: this.frameId,
16397             name: this.frameId,
16398             frameBorder : 'no',
16399             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
16400         }, this.el
16401         );
16402         
16403         
16404         this.iframe = iframe.dom;
16405
16406          this.assignDocWin();
16407         
16408         this.doc.designMode = 'on';
16409        
16410         this.doc.open();
16411         this.doc.write(this.getDocMarkup());
16412         this.doc.close();
16413
16414         
16415         var task = { // must defer to wait for browser to be ready
16416             run : function(){
16417                 //console.log("run task?" + this.doc.readyState);
16418                 this.assignDocWin();
16419                 if(this.doc.body || this.doc.readyState == 'complete'){
16420                     try {
16421                         this.doc.designMode="on";
16422                     } catch (e) {
16423                         return;
16424                     }
16425                     Roo.TaskMgr.stop(task);
16426                     this.initEditor.defer(10, this);
16427                 }
16428             },
16429             interval : 10,
16430             duration: 10000,
16431             scope: this
16432         };
16433         Roo.TaskMgr.start(task);
16434
16435         
16436          
16437     },
16438
16439     // private
16440     onResize : function(w, h)
16441     {
16442          Roo.log('resize: ' +w + ',' + h );
16443         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
16444         if(!this.iframe){
16445             return;
16446         }
16447         if(typeof w == 'number'){
16448             
16449             this.iframe.style.width = w + 'px';
16450         }
16451         if(typeof h == 'number'){
16452             
16453             this.iframe.style.height = h + 'px';
16454             if(this.doc){
16455                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
16456             }
16457         }
16458         
16459     },
16460
16461     /**
16462      * Toggles the editor between standard and source edit mode.
16463      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
16464      */
16465     toggleSourceEdit : function(sourceEditMode){
16466         
16467         this.sourceEditMode = sourceEditMode === true;
16468         
16469         if(this.sourceEditMode){
16470  
16471             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
16472             
16473         }else{
16474             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
16475             //this.iframe.className = '';
16476             this.deferFocus();
16477         }
16478         //this.setSize(this.owner.wrap.getSize());
16479         //this.fireEvent('editmodechange', this, this.sourceEditMode);
16480     },
16481
16482     
16483   
16484
16485     /**
16486      * Protected method that will not generally be called directly. If you need/want
16487      * custom HTML cleanup, this is the method you should override.
16488      * @param {String} html The HTML to be cleaned
16489      * return {String} The cleaned HTML
16490      */
16491     cleanHtml : function(html){
16492         html = String(html);
16493         if(html.length > 5){
16494             if(Roo.isSafari){ // strip safari nonsense
16495                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
16496             }
16497         }
16498         if(html == '&nbsp;'){
16499             html = '';
16500         }
16501         return html;
16502     },
16503
16504     /**
16505      * HTML Editor -> Textarea
16506      * Protected method that will not generally be called directly. Syncs the contents
16507      * of the editor iframe with the textarea.
16508      */
16509     syncValue : function(){
16510         if(this.initialized){
16511             var bd = (this.doc.body || this.doc.documentElement);
16512             //this.cleanUpPaste(); -- this is done else where and causes havoc..
16513             var html = bd.innerHTML;
16514             if(Roo.isSafari){
16515                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
16516                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
16517                 if(m && m[1]){
16518                     html = '<div style="'+m[0]+'">' + html + '</div>';
16519                 }
16520             }
16521             html = this.cleanHtml(html);
16522             // fix up the special chars.. normaly like back quotes in word...
16523             // however we do not want to do this with chinese..
16524             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
16525                 var cc = b.charCodeAt();
16526                 if (
16527                     (cc >= 0x4E00 && cc < 0xA000 ) ||
16528                     (cc >= 0x3400 && cc < 0x4E00 ) ||
16529                     (cc >= 0xf900 && cc < 0xfb00 )
16530                 ) {
16531                         return b;
16532                 }
16533                 return "&#"+cc+";" 
16534             });
16535             if(this.owner.fireEvent('beforesync', this, html) !== false){
16536                 this.el.dom.value = html;
16537                 this.owner.fireEvent('sync', this, html);
16538             }
16539         }
16540     },
16541
16542     /**
16543      * Protected method that will not generally be called directly. Pushes the value of the textarea
16544      * into the iframe editor.
16545      */
16546     pushValue : function(){
16547         if(this.initialized){
16548             var v = this.el.dom.value.trim();
16549             
16550 //            if(v.length < 1){
16551 //                v = '&#160;';
16552 //            }
16553             
16554             if(this.owner.fireEvent('beforepush', this, v) !== false){
16555                 var d = (this.doc.body || this.doc.documentElement);
16556                 d.innerHTML = v;
16557                 this.cleanUpPaste();
16558                 this.el.dom.value = d.innerHTML;
16559                 this.owner.fireEvent('push', this, v);
16560             }
16561         }
16562     },
16563
16564     // private
16565     deferFocus : function(){
16566         this.focus.defer(10, this);
16567     },
16568
16569     // doc'ed in Field
16570     focus : function(){
16571         if(this.win && !this.sourceEditMode){
16572             this.win.focus();
16573         }else{
16574             this.el.focus();
16575         }
16576     },
16577     
16578     assignDocWin: function()
16579     {
16580         var iframe = this.iframe;
16581         
16582          if(Roo.isIE){
16583             this.doc = iframe.contentWindow.document;
16584             this.win = iframe.contentWindow;
16585         } else {
16586 //            if (!Roo.get(this.frameId)) {
16587 //                return;
16588 //            }
16589 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
16590 //            this.win = Roo.get(this.frameId).dom.contentWindow;
16591             
16592             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
16593                 return;
16594             }
16595             
16596             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
16597             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
16598         }
16599     },
16600     
16601     // private
16602     initEditor : function(){
16603         //console.log("INIT EDITOR");
16604         this.assignDocWin();
16605         
16606         
16607         
16608         this.doc.designMode="on";
16609         this.doc.open();
16610         this.doc.write(this.getDocMarkup());
16611         this.doc.close();
16612         
16613         var dbody = (this.doc.body || this.doc.documentElement);
16614         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
16615         // this copies styles from the containing element into thsi one..
16616         // not sure why we need all of this..
16617         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
16618         
16619         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
16620         //ss['background-attachment'] = 'fixed'; // w3c
16621         dbody.bgProperties = 'fixed'; // ie
16622         //Roo.DomHelper.applyStyles(dbody, ss);
16623         Roo.EventManager.on(this.doc, {
16624             //'mousedown': this.onEditorEvent,
16625             'mouseup': this.onEditorEvent,
16626             'dblclick': this.onEditorEvent,
16627             'click': this.onEditorEvent,
16628             'keyup': this.onEditorEvent,
16629             buffer:100,
16630             scope: this
16631         });
16632         if(Roo.isGecko){
16633             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
16634         }
16635         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
16636             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
16637         }
16638         this.initialized = true;
16639
16640         this.owner.fireEvent('initialize', this);
16641         this.pushValue();
16642     },
16643
16644     // private
16645     onDestroy : function(){
16646         
16647         
16648         
16649         if(this.rendered){
16650             
16651             //for (var i =0; i < this.toolbars.length;i++) {
16652             //    // fixme - ask toolbars for heights?
16653             //    this.toolbars[i].onDestroy();
16654            // }
16655             
16656             //this.wrap.dom.innerHTML = '';
16657             //this.wrap.remove();
16658         }
16659     },
16660
16661     // private
16662     onFirstFocus : function(){
16663         
16664         this.assignDocWin();
16665         
16666         
16667         this.activated = true;
16668          
16669     
16670         if(Roo.isGecko){ // prevent silly gecko errors
16671             this.win.focus();
16672             var s = this.win.getSelection();
16673             if(!s.focusNode || s.focusNode.nodeType != 3){
16674                 var r = s.getRangeAt(0);
16675                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
16676                 r.collapse(true);
16677                 this.deferFocus();
16678             }
16679             try{
16680                 this.execCmd('useCSS', true);
16681                 this.execCmd('styleWithCSS', false);
16682             }catch(e){}
16683         }
16684         this.owner.fireEvent('activate', this);
16685     },
16686
16687     // private
16688     adjustFont: function(btn){
16689         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
16690         //if(Roo.isSafari){ // safari
16691         //    adjust *= 2;
16692        // }
16693         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
16694         if(Roo.isSafari){ // safari
16695             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
16696             v =  (v < 10) ? 10 : v;
16697             v =  (v > 48) ? 48 : v;
16698             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
16699             
16700         }
16701         
16702         
16703         v = Math.max(1, v+adjust);
16704         
16705         this.execCmd('FontSize', v  );
16706     },
16707
16708     onEditorEvent : function(e){
16709         this.owner.fireEvent('editorevent', this, e);
16710       //  this.updateToolbar();
16711         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
16712     },
16713
16714     insertTag : function(tg)
16715     {
16716         // could be a bit smarter... -> wrap the current selected tRoo..
16717         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
16718             
16719             range = this.createRange(this.getSelection());
16720             var wrappingNode = this.doc.createElement(tg.toLowerCase());
16721             wrappingNode.appendChild(range.extractContents());
16722             range.insertNode(wrappingNode);
16723
16724             return;
16725             
16726             
16727             
16728         }
16729         this.execCmd("formatblock",   tg);
16730         
16731     },
16732     
16733     insertText : function(txt)
16734     {
16735         
16736         
16737         var range = this.createRange();
16738         range.deleteContents();
16739                //alert(Sender.getAttribute('label'));
16740                
16741         range.insertNode(this.doc.createTextNode(txt));
16742     } ,
16743     
16744      
16745
16746     /**
16747      * Executes a Midas editor command on the editor document and performs necessary focus and
16748      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
16749      * @param {String} cmd The Midas command
16750      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16751      */
16752     relayCmd : function(cmd, value){
16753         this.win.focus();
16754         this.execCmd(cmd, value);
16755         this.owner.fireEvent('editorevent', this);
16756         //this.updateToolbar();
16757         this.owner.deferFocus();
16758     },
16759
16760     /**
16761      * Executes a Midas editor command directly on the editor document.
16762      * For visual commands, you should use {@link #relayCmd} instead.
16763      * <b>This should only be called after the editor is initialized.</b>
16764      * @param {String} cmd The Midas command
16765      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16766      */
16767     execCmd : function(cmd, value){
16768         this.doc.execCommand(cmd, false, value === undefined ? null : value);
16769         this.syncValue();
16770     },
16771  
16772  
16773    
16774     /**
16775      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
16776      * to insert tRoo.
16777      * @param {String} text | dom node.. 
16778      */
16779     insertAtCursor : function(text)
16780     {
16781         
16782         
16783         
16784         if(!this.activated){
16785             return;
16786         }
16787         /*
16788         if(Roo.isIE){
16789             this.win.focus();
16790             var r = this.doc.selection.createRange();
16791             if(r){
16792                 r.collapse(true);
16793                 r.pasteHTML(text);
16794                 this.syncValue();
16795                 this.deferFocus();
16796             
16797             }
16798             return;
16799         }
16800         */
16801         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
16802             this.win.focus();
16803             
16804             
16805             // from jquery ui (MIT licenced)
16806             var range, node;
16807             var win = this.win;
16808             
16809             if (win.getSelection && win.getSelection().getRangeAt) {
16810                 range = win.getSelection().getRangeAt(0);
16811                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
16812                 range.insertNode(node);
16813             } else if (win.document.selection && win.document.selection.createRange) {
16814                 // no firefox support
16815                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
16816                 win.document.selection.createRange().pasteHTML(txt);
16817             } else {
16818                 // no firefox support
16819                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
16820                 this.execCmd('InsertHTML', txt);
16821             } 
16822             
16823             this.syncValue();
16824             
16825             this.deferFocus();
16826         }
16827     },
16828  // private
16829     mozKeyPress : function(e){
16830         if(e.ctrlKey){
16831             var c = e.getCharCode(), cmd;
16832           
16833             if(c > 0){
16834                 c = String.fromCharCode(c).toLowerCase();
16835                 switch(c){
16836                     case 'b':
16837                         cmd = 'bold';
16838                         break;
16839                     case 'i':
16840                         cmd = 'italic';
16841                         break;
16842                     
16843                     case 'u':
16844                         cmd = 'underline';
16845                         break;
16846                     
16847                     case 'v':
16848                         this.cleanUpPaste.defer(100, this);
16849                         return;
16850                         
16851                 }
16852                 if(cmd){
16853                     this.win.focus();
16854                     this.execCmd(cmd);
16855                     this.deferFocus();
16856                     e.preventDefault();
16857                 }
16858                 
16859             }
16860         }
16861     },
16862
16863     // private
16864     fixKeys : function(){ // load time branching for fastest keydown performance
16865         if(Roo.isIE){
16866             return function(e){
16867                 var k = e.getKey(), r;
16868                 if(k == e.TAB){
16869                     e.stopEvent();
16870                     r = this.doc.selection.createRange();
16871                     if(r){
16872                         r.collapse(true);
16873                         r.pasteHTML('&#160;&#160;&#160;&#160;');
16874                         this.deferFocus();
16875                     }
16876                     return;
16877                 }
16878                 
16879                 if(k == e.ENTER){
16880                     r = this.doc.selection.createRange();
16881                     if(r){
16882                         var target = r.parentElement();
16883                         if(!target || target.tagName.toLowerCase() != 'li'){
16884                             e.stopEvent();
16885                             r.pasteHTML('<br />');
16886                             r.collapse(false);
16887                             r.select();
16888                         }
16889                     }
16890                 }
16891                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
16892                     this.cleanUpPaste.defer(100, this);
16893                     return;
16894                 }
16895                 
16896                 
16897             };
16898         }else if(Roo.isOpera){
16899             return function(e){
16900                 var k = e.getKey();
16901                 if(k == e.TAB){
16902                     e.stopEvent();
16903                     this.win.focus();
16904                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
16905                     this.deferFocus();
16906                 }
16907                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
16908                     this.cleanUpPaste.defer(100, this);
16909                     return;
16910                 }
16911                 
16912             };
16913         }else if(Roo.isSafari){
16914             return function(e){
16915                 var k = e.getKey();
16916                 
16917                 if(k == e.TAB){
16918                     e.stopEvent();
16919                     this.execCmd('InsertText','\t');
16920                     this.deferFocus();
16921                     return;
16922                 }
16923                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
16924                     this.cleanUpPaste.defer(100, this);
16925                     return;
16926                 }
16927                 
16928              };
16929         }
16930     }(),
16931     
16932     getAllAncestors: function()
16933     {
16934         var p = this.getSelectedNode();
16935         var a = [];
16936         if (!p) {
16937             a.push(p); // push blank onto stack..
16938             p = this.getParentElement();
16939         }
16940         
16941         
16942         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
16943             a.push(p);
16944             p = p.parentNode;
16945         }
16946         a.push(this.doc.body);
16947         return a;
16948     },
16949     lastSel : false,
16950     lastSelNode : false,
16951     
16952     
16953     getSelection : function() 
16954     {
16955         this.assignDocWin();
16956         return Roo.isIE ? this.doc.selection : this.win.getSelection();
16957     },
16958     
16959     getSelectedNode: function() 
16960     {
16961         // this may only work on Gecko!!!
16962         
16963         // should we cache this!!!!
16964         
16965         
16966         
16967          
16968         var range = this.createRange(this.getSelection()).cloneRange();
16969         
16970         if (Roo.isIE) {
16971             var parent = range.parentElement();
16972             while (true) {
16973                 var testRange = range.duplicate();
16974                 testRange.moveToElementText(parent);
16975                 if (testRange.inRange(range)) {
16976                     break;
16977                 }
16978                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
16979                     break;
16980                 }
16981                 parent = parent.parentElement;
16982             }
16983             return parent;
16984         }
16985         
16986         // is ancestor a text element.
16987         var ac =  range.commonAncestorContainer;
16988         if (ac.nodeType == 3) {
16989             ac = ac.parentNode;
16990         }
16991         
16992         var ar = ac.childNodes;
16993          
16994         var nodes = [];
16995         var other_nodes = [];
16996         var has_other_nodes = false;
16997         for (var i=0;i<ar.length;i++) {
16998             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
16999                 continue;
17000             }
17001             // fullly contained node.
17002             
17003             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
17004                 nodes.push(ar[i]);
17005                 continue;
17006             }
17007             
17008             // probably selected..
17009             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
17010                 other_nodes.push(ar[i]);
17011                 continue;
17012             }
17013             // outer..
17014             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
17015                 continue;
17016             }
17017             
17018             
17019             has_other_nodes = true;
17020         }
17021         if (!nodes.length && other_nodes.length) {
17022             nodes= other_nodes;
17023         }
17024         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
17025             return false;
17026         }
17027         
17028         return nodes[0];
17029     },
17030     createRange: function(sel)
17031     {
17032         // this has strange effects when using with 
17033         // top toolbar - not sure if it's a great idea.
17034         //this.editor.contentWindow.focus();
17035         if (typeof sel != "undefined") {
17036             try {
17037                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
17038             } catch(e) {
17039                 return this.doc.createRange();
17040             }
17041         } else {
17042             return this.doc.createRange();
17043         }
17044     },
17045     getParentElement: function()
17046     {
17047         
17048         this.assignDocWin();
17049         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
17050         
17051         var range = this.createRange(sel);
17052          
17053         try {
17054             var p = range.commonAncestorContainer;
17055             while (p.nodeType == 3) { // text node
17056                 p = p.parentNode;
17057             }
17058             return p;
17059         } catch (e) {
17060             return null;
17061         }
17062     
17063     },
17064     /***
17065      *
17066      * Range intersection.. the hard stuff...
17067      *  '-1' = before
17068      *  '0' = hits..
17069      *  '1' = after.
17070      *         [ -- selected range --- ]
17071      *   [fail]                        [fail]
17072      *
17073      *    basically..
17074      *      if end is before start or  hits it. fail.
17075      *      if start is after end or hits it fail.
17076      *
17077      *   if either hits (but other is outside. - then it's not 
17078      *   
17079      *    
17080      **/
17081     
17082     
17083     // @see http://www.thismuchiknow.co.uk/?p=64.
17084     rangeIntersectsNode : function(range, node)
17085     {
17086         var nodeRange = node.ownerDocument.createRange();
17087         try {
17088             nodeRange.selectNode(node);
17089         } catch (e) {
17090             nodeRange.selectNodeContents(node);
17091         }
17092     
17093         var rangeStartRange = range.cloneRange();
17094         rangeStartRange.collapse(true);
17095     
17096         var rangeEndRange = range.cloneRange();
17097         rangeEndRange.collapse(false);
17098     
17099         var nodeStartRange = nodeRange.cloneRange();
17100         nodeStartRange.collapse(true);
17101     
17102         var nodeEndRange = nodeRange.cloneRange();
17103         nodeEndRange.collapse(false);
17104     
17105         return rangeStartRange.compareBoundaryPoints(
17106                  Range.START_TO_START, nodeEndRange) == -1 &&
17107                rangeEndRange.compareBoundaryPoints(
17108                  Range.START_TO_START, nodeStartRange) == 1;
17109         
17110          
17111     },
17112     rangeCompareNode : function(range, node)
17113     {
17114         var nodeRange = node.ownerDocument.createRange();
17115         try {
17116             nodeRange.selectNode(node);
17117         } catch (e) {
17118             nodeRange.selectNodeContents(node);
17119         }
17120         
17121         
17122         range.collapse(true);
17123     
17124         nodeRange.collapse(true);
17125      
17126         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
17127         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
17128          
17129         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
17130         
17131         var nodeIsBefore   =  ss == 1;
17132         var nodeIsAfter    = ee == -1;
17133         
17134         if (nodeIsBefore && nodeIsAfter)
17135             return 0; // outer
17136         if (!nodeIsBefore && nodeIsAfter)
17137             return 1; //right trailed.
17138         
17139         if (nodeIsBefore && !nodeIsAfter)
17140             return 2;  // left trailed.
17141         // fully contined.
17142         return 3;
17143     },
17144
17145     // private? - in a new class?
17146     cleanUpPaste :  function()
17147     {
17148         // cleans up the whole document..
17149         Roo.log('cleanuppaste');
17150         
17151         this.cleanUpChildren(this.doc.body);
17152         var clean = this.cleanWordChars(this.doc.body.innerHTML);
17153         if (clean != this.doc.body.innerHTML) {
17154             this.doc.body.innerHTML = clean;
17155         }
17156         
17157     },
17158     
17159     cleanWordChars : function(input) {// change the chars to hex code
17160         var he = Roo.HtmlEditorCore;
17161         
17162         var output = input;
17163         Roo.each(he.swapCodes, function(sw) { 
17164             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
17165             
17166             output = output.replace(swapper, sw[1]);
17167         });
17168         
17169         return output;
17170     },
17171     
17172     
17173     cleanUpChildren : function (n)
17174     {
17175         if (!n.childNodes.length) {
17176             return;
17177         }
17178         for (var i = n.childNodes.length-1; i > -1 ; i--) {
17179            this.cleanUpChild(n.childNodes[i]);
17180         }
17181     },
17182     
17183     
17184         
17185     
17186     cleanUpChild : function (node)
17187     {
17188         var ed = this;
17189         //console.log(node);
17190         if (node.nodeName == "#text") {
17191             // clean up silly Windows -- stuff?
17192             return; 
17193         }
17194         if (node.nodeName == "#comment") {
17195             node.parentNode.removeChild(node);
17196             // clean up silly Windows -- stuff?
17197             return; 
17198         }
17199         
17200         if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1 && this.clearUp) {
17201             // remove node.
17202             node.parentNode.removeChild(node);
17203             return;
17204             
17205         }
17206         
17207         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
17208         
17209         // remove <a name=....> as rendering on yahoo mailer is borked with this.
17210         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
17211         
17212         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
17213         //    remove_keep_children = true;
17214         //}
17215         
17216         if (remove_keep_children) {
17217             this.cleanUpChildren(node);
17218             // inserts everything just before this node...
17219             while (node.childNodes.length) {
17220                 var cn = node.childNodes[0];
17221                 node.removeChild(cn);
17222                 node.parentNode.insertBefore(cn, node);
17223             }
17224             node.parentNode.removeChild(node);
17225             return;
17226         }
17227         
17228         if (!node.attributes || !node.attributes.length) {
17229             this.cleanUpChildren(node);
17230             return;
17231         }
17232         
17233         function cleanAttr(n,v)
17234         {
17235             
17236             if (v.match(/^\./) || v.match(/^\//)) {
17237                 return;
17238             }
17239             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
17240                 return;
17241             }
17242             if (v.match(/^#/)) {
17243                 return;
17244             }
17245 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
17246             node.removeAttribute(n);
17247             
17248         }
17249         
17250         function cleanStyle(n,v)
17251         {
17252             if (v.match(/expression/)) { //XSS?? should we even bother..
17253                 node.removeAttribute(n);
17254                 return;
17255             }
17256             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
17257             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
17258             
17259             
17260             var parts = v.split(/;/);
17261             var clean = [];
17262             
17263             Roo.each(parts, function(p) {
17264                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
17265                 if (!p.length) {
17266                     return true;
17267                 }
17268                 var l = p.split(':').shift().replace(/\s+/g,'');
17269                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
17270                 
17271                 if ( cblack.indexOf(l) > -1) {
17272 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17273                     //node.removeAttribute(n);
17274                     return true;
17275                 }
17276                 //Roo.log()
17277                 // only allow 'c whitelisted system attributes'
17278                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
17279 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17280                     //node.removeAttribute(n);
17281                     return true;
17282                 }
17283                 
17284                 
17285                  
17286                 
17287                 clean.push(p);
17288                 return true;
17289             });
17290             if (clean.length) { 
17291                 node.setAttribute(n, clean.join(';'));
17292             } else {
17293                 node.removeAttribute(n);
17294             }
17295             
17296         }
17297         
17298         
17299         for (var i = node.attributes.length-1; i > -1 ; i--) {
17300             var a = node.attributes[i];
17301             //console.log(a);
17302             
17303             if (a.name.toLowerCase().substr(0,2)=='on')  {
17304                 node.removeAttribute(a.name);
17305                 continue;
17306             }
17307             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
17308                 node.removeAttribute(a.name);
17309                 continue;
17310             }
17311             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
17312                 cleanAttr(a.name,a.value); // fixme..
17313                 continue;
17314             }
17315             if (a.name == 'style') {
17316                 cleanStyle(a.name,a.value);
17317                 continue;
17318             }
17319             /// clean up MS crap..
17320             // tecnically this should be a list of valid class'es..
17321             
17322             
17323             if (a.name == 'class') {
17324                 if (a.value.match(/^Mso/)) {
17325                     node.className = '';
17326                 }
17327                 
17328                 if (a.value.match(/body/)) {
17329                     node.className = '';
17330                 }
17331                 continue;
17332             }
17333             
17334             // style cleanup!?
17335             // class cleanup?
17336             
17337         }
17338         
17339         
17340         this.cleanUpChildren(node);
17341         
17342         
17343     },
17344     /**
17345      * Clean up MS wordisms...
17346      */
17347     cleanWord : function(node)
17348     {
17349         var _t = this;
17350         var cleanWordChildren = function()
17351         {
17352             if (!node.childNodes.length) {
17353                 return;
17354             }
17355             for (var i = node.childNodes.length-1; i > -1 ; i--) {
17356                _t.cleanWord(node.childNodes[i]);
17357             }
17358         }
17359         
17360         
17361         if (!node) {
17362             this.cleanWord(this.doc.body);
17363             return;
17364         }
17365         if (node.nodeName == "#text") {
17366             // clean up silly Windows -- stuff?
17367             return; 
17368         }
17369         if (node.nodeName == "#comment") {
17370             node.parentNode.removeChild(node);
17371             // clean up silly Windows -- stuff?
17372             return; 
17373         }
17374         
17375         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
17376             node.parentNode.removeChild(node);
17377             return;
17378         }
17379         
17380         // remove - but keep children..
17381         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
17382             while (node.childNodes.length) {
17383                 var cn = node.childNodes[0];
17384                 node.removeChild(cn);
17385                 node.parentNode.insertBefore(cn, node);
17386             }
17387             node.parentNode.removeChild(node);
17388             cleanWordChildren();
17389             return;
17390         }
17391         // clean styles
17392         if (node.className.length) {
17393             
17394             var cn = node.className.split(/\W+/);
17395             var cna = [];
17396             Roo.each(cn, function(cls) {
17397                 if (cls.match(/Mso[a-zA-Z]+/)) {
17398                     return;
17399                 }
17400                 cna.push(cls);
17401             });
17402             node.className = cna.length ? cna.join(' ') : '';
17403             if (!cna.length) {
17404                 node.removeAttribute("class");
17405             }
17406         }
17407         
17408         if (node.hasAttribute("lang")) {
17409             node.removeAttribute("lang");
17410         }
17411         
17412         if (node.hasAttribute("style")) {
17413             
17414             var styles = node.getAttribute("style").split(";");
17415             var nstyle = [];
17416             Roo.each(styles, function(s) {
17417                 if (!s.match(/:/)) {
17418                     return;
17419                 }
17420                 var kv = s.split(":");
17421                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
17422                     return;
17423                 }
17424                 // what ever is left... we allow.
17425                 nstyle.push(s);
17426             });
17427             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
17428             if (!nstyle.length) {
17429                 node.removeAttribute('style');
17430             }
17431         }
17432         
17433         cleanWordChildren();
17434         
17435         
17436     },
17437     domToHTML : function(currentElement, depth, nopadtext) {
17438         
17439             depth = depth || 0;
17440             nopadtext = nopadtext || false;
17441         
17442             if (!currentElement) {
17443                 return this.domToHTML(this.doc.body);
17444             }
17445             
17446             //Roo.log(currentElement);
17447             var j;
17448             var allText = false;
17449             var nodeName = currentElement.nodeName;
17450             var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
17451             
17452             if  (nodeName == '#text') {
17453                 return currentElement.nodeValue;
17454             }
17455             
17456             
17457             var ret = '';
17458             if (nodeName != 'BODY') {
17459                  
17460                 var i = 0;
17461                 // Prints the node tagName, such as <A>, <IMG>, etc
17462                 if (tagName) {
17463                     var attr = [];
17464                     for(i = 0; i < currentElement.attributes.length;i++) {
17465                         // quoting?
17466                         var aname = currentElement.attributes.item(i).name;
17467                         if (!currentElement.attributes.item(i).value.length) {
17468                             continue;
17469                         }
17470                         attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
17471                     }
17472                     
17473                     ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
17474                 } 
17475                 else {
17476                     
17477                     // eack
17478                 }
17479             } else {
17480                 tagName = false;
17481             }
17482             if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
17483                 return ret;
17484             }
17485             if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
17486                 nopadtext = true;
17487             }
17488             
17489             
17490             // Traverse the tree
17491             i = 0;
17492             var currentElementChild = currentElement.childNodes.item(i);
17493             var allText = true;
17494             var innerHTML  = '';
17495             lastnode = '';
17496             while (currentElementChild) {
17497                 // Formatting code (indent the tree so it looks nice on the screen)
17498                 var nopad = nopadtext;
17499                 if (lastnode == 'SPAN') {
17500                     nopad  = true;
17501                 }
17502                 // text
17503                 if  (currentElementChild.nodeName == '#text') {
17504                     var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
17505                     if (!nopad && toadd.length > 80) {
17506                         innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
17507                     }
17508                     innerHTML  += toadd;
17509                     
17510                     i++;
17511                     currentElementChild = currentElement.childNodes.item(i);
17512                     lastNode = '';
17513                     continue;
17514                 }
17515                 allText = false;
17516                 
17517                 innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
17518                     
17519                 // Recursively traverse the tree structure of the child node
17520                 innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
17521                 lastnode = currentElementChild.nodeName;
17522                 i++;
17523                 currentElementChild=currentElement.childNodes.item(i);
17524             }
17525             
17526             ret += innerHTML;
17527             
17528             if (!allText) {
17529                     // The remaining code is mostly for formatting the tree
17530                 ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
17531             }
17532             
17533             
17534             if (tagName) {
17535                 ret+= "</"+tagName+">";
17536             }
17537             return ret;
17538             
17539         }
17540     
17541     // hide stuff that is not compatible
17542     /**
17543      * @event blur
17544      * @hide
17545      */
17546     /**
17547      * @event change
17548      * @hide
17549      */
17550     /**
17551      * @event focus
17552      * @hide
17553      */
17554     /**
17555      * @event specialkey
17556      * @hide
17557      */
17558     /**
17559      * @cfg {String} fieldClass @hide
17560      */
17561     /**
17562      * @cfg {String} focusClass @hide
17563      */
17564     /**
17565      * @cfg {String} autoCreate @hide
17566      */
17567     /**
17568      * @cfg {String} inputType @hide
17569      */
17570     /**
17571      * @cfg {String} invalidClass @hide
17572      */
17573     /**
17574      * @cfg {String} invalidText @hide
17575      */
17576     /**
17577      * @cfg {String} msgFx @hide
17578      */
17579     /**
17580      * @cfg {String} validateOnBlur @hide
17581      */
17582 });
17583
17584 Roo.HtmlEditorCore.white = [
17585         'area', 'br', 'img', 'input', 'hr', 'wbr',
17586         
17587        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
17588        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
17589        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
17590        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
17591        'table',   'ul',         'xmp', 
17592        
17593        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
17594       'thead',   'tr', 
17595      
17596       'dir', 'menu', 'ol', 'ul', 'dl',
17597        
17598       'embed',  'object'
17599 ];
17600
17601
17602 Roo.HtmlEditorCore.black = [
17603     //    'embed',  'object', // enable - backend responsiblity to clean thiese
17604         'applet', // 
17605         'base',   'basefont', 'bgsound', 'blink',  'body', 
17606         'frame',  'frameset', 'head',    'html',   'ilayer', 
17607         'iframe', 'layer',  'link',     'meta',    'object',   
17608         'script', 'style' ,'title',  'xml' // clean later..
17609 ];
17610 Roo.HtmlEditorCore.clean = [
17611     'script', 'style', 'title', 'xml'
17612 ];
17613 Roo.HtmlEditorCore.remove = [
17614     'font'
17615 ];
17616 // attributes..
17617
17618 Roo.HtmlEditorCore.ablack = [
17619     'on'
17620 ];
17621     
17622 Roo.HtmlEditorCore.aclean = [ 
17623     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
17624 ];
17625
17626 // protocols..
17627 Roo.HtmlEditorCore.pwhite= [
17628         'http',  'https',  'mailto'
17629 ];
17630
17631 // white listed style attributes.
17632 Roo.HtmlEditorCore.cwhite= [
17633       //  'text-align', /// default is to allow most things..
17634       
17635          
17636 //        'font-size'//??
17637 ];
17638
17639 // black listed style attributes.
17640 Roo.HtmlEditorCore.cblack= [
17641       //  'font-size' -- this can be set by the project 
17642 ];
17643
17644
17645 Roo.HtmlEditorCore.swapCodes   =[ 
17646     [    8211, "--" ], 
17647     [    8212, "--" ], 
17648     [    8216,  "'" ],  
17649     [    8217, "'" ],  
17650     [    8220, '"' ],  
17651     [    8221, '"' ],  
17652     [    8226, "*" ],  
17653     [    8230, "..." ]
17654 ]; 
17655
17656     /*
17657  * - LGPL
17658  *
17659  * HtmlEditor
17660  * 
17661  */
17662
17663 /**
17664  * @class Roo.bootstrap.HtmlEditor
17665  * @extends Roo.bootstrap.TextArea
17666  * Bootstrap HtmlEditor class
17667
17668  * @constructor
17669  * Create a new HtmlEditor
17670  * @param {Object} config The config object
17671  */
17672
17673 Roo.bootstrap.HtmlEditor = function(config){
17674     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
17675     if (!this.toolbars) {
17676         this.toolbars = [];
17677     }
17678     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
17679     this.addEvents({
17680             /**
17681              * @event initialize
17682              * Fires when the editor is fully initialized (including the iframe)
17683              * @param {HtmlEditor} this
17684              */
17685             initialize: true,
17686             /**
17687              * @event activate
17688              * Fires when the editor is first receives the focus. Any insertion must wait
17689              * until after this event.
17690              * @param {HtmlEditor} this
17691              */
17692             activate: true,
17693              /**
17694              * @event beforesync
17695              * Fires before the textarea is updated with content from the editor iframe. Return false
17696              * to cancel the sync.
17697              * @param {HtmlEditor} this
17698              * @param {String} html
17699              */
17700             beforesync: true,
17701              /**
17702              * @event beforepush
17703              * Fires before the iframe editor is updated with content from the textarea. Return false
17704              * to cancel the push.
17705              * @param {HtmlEditor} this
17706              * @param {String} html
17707              */
17708             beforepush: true,
17709              /**
17710              * @event sync
17711              * Fires when the textarea is updated with content from the editor iframe.
17712              * @param {HtmlEditor} this
17713              * @param {String} html
17714              */
17715             sync: true,
17716              /**
17717              * @event push
17718              * Fires when the iframe editor is updated with content from the textarea.
17719              * @param {HtmlEditor} this
17720              * @param {String} html
17721              */
17722             push: true,
17723              /**
17724              * @event editmodechange
17725              * Fires when the editor switches edit modes
17726              * @param {HtmlEditor} this
17727              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
17728              */
17729             editmodechange: true,
17730             /**
17731              * @event editorevent
17732              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
17733              * @param {HtmlEditor} this
17734              */
17735             editorevent: true,
17736             /**
17737              * @event firstfocus
17738              * Fires when on first focus - needed by toolbars..
17739              * @param {HtmlEditor} this
17740              */
17741             firstfocus: true,
17742             /**
17743              * @event autosave
17744              * Auto save the htmlEditor value as a file into Events
17745              * @param {HtmlEditor} this
17746              */
17747             autosave: true,
17748             /**
17749              * @event savedpreview
17750              * preview the saved version of htmlEditor
17751              * @param {HtmlEditor} this
17752              */
17753             savedpreview: true
17754         });
17755 };
17756
17757
17758 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
17759     
17760     
17761       /**
17762      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
17763      */
17764     toolbars : false,
17765    
17766      /**
17767      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
17768      *                        Roo.resizable.
17769      */
17770     resizable : false,
17771      /**
17772      * @cfg {Number} height (in pixels)
17773      */   
17774     height: 300,
17775    /**
17776      * @cfg {Number} width (in pixels)
17777      */   
17778     width: false,
17779     
17780     /**
17781      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
17782      * 
17783      */
17784     stylesheets: false,
17785     
17786     // id of frame..
17787     frameId: false,
17788     
17789     // private properties
17790     validationEvent : false,
17791     deferHeight: true,
17792     initialized : false,
17793     activated : false,
17794     
17795     onFocus : Roo.emptyFn,
17796     iframePad:3,
17797     hideMode:'offsets',
17798     
17799     
17800     tbContainer : false,
17801     
17802     toolbarContainer :function() {
17803         return this.wrap.select('.x-html-editor-tb',true).first();
17804     },
17805
17806     /**
17807      * Protected method that will not generally be called directly. It
17808      * is called when the editor creates its toolbar. Override this method if you need to
17809      * add custom toolbar buttons.
17810      * @param {HtmlEditor} editor
17811      */
17812     createToolbar : function(){
17813         
17814         Roo.log("create toolbars");
17815         
17816         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
17817         this.toolbars[0].render(this.toolbarContainer());
17818         
17819         return;
17820         
17821 //        if (!editor.toolbars || !editor.toolbars.length) {
17822 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
17823 //        }
17824 //        
17825 //        for (var i =0 ; i < editor.toolbars.length;i++) {
17826 //            editor.toolbars[i] = Roo.factory(
17827 //                    typeof(editor.toolbars[i]) == 'string' ?
17828 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
17829 //                Roo.bootstrap.HtmlEditor);
17830 //            editor.toolbars[i].init(editor);
17831 //        }
17832     },
17833
17834      
17835     // private
17836     onRender : function(ct, position)
17837     {
17838        // Roo.log("Call onRender: " + this.xtype);
17839         var _t = this;
17840         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
17841       
17842         this.wrap = this.inputEl().wrap({
17843             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
17844         });
17845         
17846         this.editorcore.onRender(ct, position);
17847          
17848         if (this.resizable) {
17849             this.resizeEl = new Roo.Resizable(this.wrap, {
17850                 pinned : true,
17851                 wrap: true,
17852                 dynamic : true,
17853                 minHeight : this.height,
17854                 height: this.height,
17855                 handles : this.resizable,
17856                 width: this.width,
17857                 listeners : {
17858                     resize : function(r, w, h) {
17859                         _t.onResize(w,h); // -something
17860                     }
17861                 }
17862             });
17863             
17864         }
17865         this.createToolbar(this);
17866        
17867         
17868         if(!this.width && this.resizable){
17869             this.setSize(this.wrap.getSize());
17870         }
17871         if (this.resizeEl) {
17872             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
17873             // should trigger onReize..
17874         }
17875         
17876     },
17877
17878     // private
17879     onResize : function(w, h)
17880     {
17881         Roo.log('resize: ' +w + ',' + h );
17882         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
17883         var ew = false;
17884         var eh = false;
17885         
17886         if(this.inputEl() ){
17887             if(typeof w == 'number'){
17888                 var aw = w - this.wrap.getFrameWidth('lr');
17889                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
17890                 ew = aw;
17891             }
17892             if(typeof h == 'number'){
17893                  var tbh = -11;  // fixme it needs to tool bar size!
17894                 for (var i =0; i < this.toolbars.length;i++) {
17895                     // fixme - ask toolbars for heights?
17896                     tbh += this.toolbars[i].el.getHeight();
17897                     //if (this.toolbars[i].footer) {
17898                     //    tbh += this.toolbars[i].footer.el.getHeight();
17899                     //}
17900                 }
17901               
17902                 
17903                 
17904                 
17905                 
17906                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
17907                 ah -= 5; // knock a few pixes off for look..
17908                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
17909                 var eh = ah;
17910             }
17911         }
17912         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
17913         this.editorcore.onResize(ew,eh);
17914         
17915     },
17916
17917     /**
17918      * Toggles the editor between standard and source edit mode.
17919      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
17920      */
17921     toggleSourceEdit : function(sourceEditMode)
17922     {
17923         this.editorcore.toggleSourceEdit(sourceEditMode);
17924         
17925         if(this.editorcore.sourceEditMode){
17926             Roo.log('editor - showing textarea');
17927             
17928 //            Roo.log('in');
17929 //            Roo.log(this.syncValue());
17930             this.syncValue();
17931             this.inputEl().removeClass(['hide', 'x-hidden']);
17932             this.inputEl().dom.removeAttribute('tabIndex');
17933             this.inputEl().focus();
17934         }else{
17935             Roo.log('editor - hiding textarea');
17936 //            Roo.log('out')
17937 //            Roo.log(this.pushValue()); 
17938             this.pushValue();
17939             
17940             this.inputEl().addClass(['hide', 'x-hidden']);
17941             this.inputEl().dom.setAttribute('tabIndex', -1);
17942             //this.deferFocus();
17943         }
17944          
17945         if(this.resizable){
17946             this.setSize(this.wrap.getSize());
17947         }
17948         
17949         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
17950     },
17951  
17952     // private (for BoxComponent)
17953     adjustSize : Roo.BoxComponent.prototype.adjustSize,
17954
17955     // private (for BoxComponent)
17956     getResizeEl : function(){
17957         return this.wrap;
17958     },
17959
17960     // private (for BoxComponent)
17961     getPositionEl : function(){
17962         return this.wrap;
17963     },
17964
17965     // private
17966     initEvents : function(){
17967         this.originalValue = this.getValue();
17968     },
17969
17970 //    /**
17971 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
17972 //     * @method
17973 //     */
17974 //    markInvalid : Roo.emptyFn,
17975 //    /**
17976 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
17977 //     * @method
17978 //     */
17979 //    clearInvalid : Roo.emptyFn,
17980
17981     setValue : function(v){
17982         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
17983         this.editorcore.pushValue();
17984     },
17985
17986      
17987     // private
17988     deferFocus : function(){
17989         this.focus.defer(10, this);
17990     },
17991
17992     // doc'ed in Field
17993     focus : function(){
17994         this.editorcore.focus();
17995         
17996     },
17997       
17998
17999     // private
18000     onDestroy : function(){
18001         
18002         
18003         
18004         if(this.rendered){
18005             
18006             for (var i =0; i < this.toolbars.length;i++) {
18007                 // fixme - ask toolbars for heights?
18008                 this.toolbars[i].onDestroy();
18009             }
18010             
18011             this.wrap.dom.innerHTML = '';
18012             this.wrap.remove();
18013         }
18014     },
18015
18016     // private
18017     onFirstFocus : function(){
18018         //Roo.log("onFirstFocus");
18019         this.editorcore.onFirstFocus();
18020          for (var i =0; i < this.toolbars.length;i++) {
18021             this.toolbars[i].onFirstFocus();
18022         }
18023         
18024     },
18025     
18026     // private
18027     syncValue : function()
18028     {   
18029         this.editorcore.syncValue();
18030     },
18031     
18032     pushValue : function()
18033     {   
18034         this.editorcore.pushValue();
18035     }
18036      
18037     
18038     // hide stuff that is not compatible
18039     /**
18040      * @event blur
18041      * @hide
18042      */
18043     /**
18044      * @event change
18045      * @hide
18046      */
18047     /**
18048      * @event focus
18049      * @hide
18050      */
18051     /**
18052      * @event specialkey
18053      * @hide
18054      */
18055     /**
18056      * @cfg {String} fieldClass @hide
18057      */
18058     /**
18059      * @cfg {String} focusClass @hide
18060      */
18061     /**
18062      * @cfg {String} autoCreate @hide
18063      */
18064     /**
18065      * @cfg {String} inputType @hide
18066      */
18067     /**
18068      * @cfg {String} invalidClass @hide
18069      */
18070     /**
18071      * @cfg {String} invalidText @hide
18072      */
18073     /**
18074      * @cfg {String} msgFx @hide
18075      */
18076     /**
18077      * @cfg {String} validateOnBlur @hide
18078      */
18079 });
18080  
18081     
18082    
18083    
18084    
18085       
18086 Roo.namespace('Roo.bootstrap.htmleditor');
18087 /**
18088  * @class Roo.bootstrap.HtmlEditorToolbar1
18089  * Basic Toolbar
18090  * 
18091  * Usage:
18092  *
18093  new Roo.bootstrap.HtmlEditor({
18094     ....
18095     toolbars : [
18096         new Roo.bootstrap.HtmlEditorToolbar1({
18097             disable : { fonts: 1 , format: 1, ..., ... , ...],
18098             btns : [ .... ]
18099         })
18100     }
18101      
18102  * 
18103  * @cfg {Object} disable List of elements to disable..
18104  * @cfg {Array} btns List of additional buttons.
18105  * 
18106  * 
18107  * NEEDS Extra CSS? 
18108  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
18109  */
18110  
18111 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
18112 {
18113     
18114     Roo.apply(this, config);
18115     
18116     // default disabled, based on 'good practice'..
18117     this.disable = this.disable || {};
18118     Roo.applyIf(this.disable, {
18119         fontSize : true,
18120         colors : true,
18121         specialElements : true
18122     });
18123     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
18124     
18125     this.editor = config.editor;
18126     this.editorcore = config.editor.editorcore;
18127     
18128     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
18129     
18130     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
18131     // dont call parent... till later.
18132 }
18133 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
18134      
18135     bar : true,
18136     
18137     editor : false,
18138     editorcore : false,
18139     
18140     
18141     formats : [
18142         "p" ,  
18143         "h1","h2","h3","h4","h5","h6", 
18144         "pre", "code", 
18145         "abbr", "acronym", "address", "cite", "samp", "var",
18146         'div','span'
18147     ],
18148     
18149     onRender : function(ct, position)
18150     {
18151        // Roo.log("Call onRender: " + this.xtype);
18152         
18153        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
18154        Roo.log(this.el);
18155        this.el.dom.style.marginBottom = '0';
18156        var _this = this;
18157        var editorcore = this.editorcore;
18158        var editor= this.editor;
18159        
18160        var children = [];
18161        var btn = function(id,cmd , toggle, handler){
18162        
18163             var  event = toggle ? 'toggle' : 'click';
18164        
18165             var a = {
18166                 size : 'sm',
18167                 xtype: 'Button',
18168                 xns: Roo.bootstrap,
18169                 glyphicon : id,
18170                 cmd : id || cmd,
18171                 enableToggle:toggle !== false,
18172                 //html : 'submit'
18173                 pressed : toggle ? false : null,
18174                 listeners : {}
18175             }
18176             a.listeners[toggle ? 'toggle' : 'click'] = function() {
18177                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
18178             }
18179             children.push(a);
18180             return a;
18181        }
18182         
18183         var style = {
18184                 xtype: 'Button',
18185                 size : 'sm',
18186                 xns: Roo.bootstrap,
18187                 glyphicon : 'font',
18188                 //html : 'submit'
18189                 menu : {
18190                     xtype: 'Menu',
18191                     xns: Roo.bootstrap,
18192                     items:  []
18193                 }
18194         };
18195         Roo.each(this.formats, function(f) {
18196             style.menu.items.push({
18197                 xtype :'MenuItem',
18198                 xns: Roo.bootstrap,
18199                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
18200                 tagname : f,
18201                 listeners : {
18202                     click : function()
18203                     {
18204                         editorcore.insertTag(this.tagname);
18205                         editor.focus();
18206                     }
18207                 }
18208                 
18209             });
18210         });
18211          children.push(style);   
18212             
18213             
18214         btn('bold',false,true);
18215         btn('italic',false,true);
18216         btn('align-left', 'justifyleft',true);
18217         btn('align-center', 'justifycenter',true);
18218         btn('align-right' , 'justifyright',true);
18219         btn('link', false, false, function(btn) {
18220             //Roo.log("create link?");
18221             var url = prompt(this.createLinkText, this.defaultLinkValue);
18222             if(url && url != 'http:/'+'/'){
18223                 this.editorcore.relayCmd('createlink', url);
18224             }
18225         }),
18226         btn('list','insertunorderedlist',true);
18227         btn('pencil', false,true, function(btn){
18228                 Roo.log(this);
18229                 
18230                 this.toggleSourceEdit(btn.pressed);
18231         });
18232         /*
18233         var cog = {
18234                 xtype: 'Button',
18235                 size : 'sm',
18236                 xns: Roo.bootstrap,
18237                 glyphicon : 'cog',
18238                 //html : 'submit'
18239                 menu : {
18240                     xtype: 'Menu',
18241                     xns: Roo.bootstrap,
18242                     items:  []
18243                 }
18244         };
18245         
18246         cog.menu.items.push({
18247             xtype :'MenuItem',
18248             xns: Roo.bootstrap,
18249             html : Clean styles,
18250             tagname : f,
18251             listeners : {
18252                 click : function()
18253                 {
18254                     editorcore.insertTag(this.tagname);
18255                     editor.focus();
18256                 }
18257             }
18258             
18259         });
18260        */
18261         
18262          
18263        this.xtype = 'NavSimplebar';
18264         
18265         for(var i=0;i< children.length;i++) {
18266             
18267             this.buttons.add(this.addxtypeChild(children[i]));
18268             
18269         }
18270         
18271         editor.on('editorevent', this.updateToolbar, this);
18272     },
18273     onBtnClick : function(id)
18274     {
18275        this.editorcore.relayCmd(id);
18276        this.editorcore.focus();
18277     },
18278     
18279     /**
18280      * Protected method that will not generally be called directly. It triggers
18281      * a toolbar update by reading the markup state of the current selection in the editor.
18282      */
18283     updateToolbar: function(){
18284
18285         if(!this.editorcore.activated){
18286             this.editor.onFirstFocus(); // is this neeed?
18287             return;
18288         }
18289
18290         var btns = this.buttons; 
18291         var doc = this.editorcore.doc;
18292         btns.get('bold').setActive(doc.queryCommandState('bold'));
18293         btns.get('italic').setActive(doc.queryCommandState('italic'));
18294         //btns.get('underline').setActive(doc.queryCommandState('underline'));
18295         
18296         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
18297         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
18298         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
18299         
18300         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
18301         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
18302          /*
18303         
18304         var ans = this.editorcore.getAllAncestors();
18305         if (this.formatCombo) {
18306             
18307             
18308             var store = this.formatCombo.store;
18309             this.formatCombo.setValue("");
18310             for (var i =0; i < ans.length;i++) {
18311                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
18312                     // select it..
18313                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
18314                     break;
18315                 }
18316             }
18317         }
18318         
18319         
18320         
18321         // hides menus... - so this cant be on a menu...
18322         Roo.bootstrap.MenuMgr.hideAll();
18323         */
18324         Roo.bootstrap.MenuMgr.hideAll();
18325         //this.editorsyncValue();
18326     },
18327     onFirstFocus: function() {
18328         this.buttons.each(function(item){
18329            item.enable();
18330         });
18331     },
18332     toggleSourceEdit : function(sourceEditMode){
18333         
18334           
18335         if(sourceEditMode){
18336             Roo.log("disabling buttons");
18337            this.buttons.each( function(item){
18338                 if(item.cmd != 'pencil'){
18339                     item.disable();
18340                 }
18341             });
18342           
18343         }else{
18344             Roo.log("enabling buttons");
18345             if(this.editorcore.initialized){
18346                 this.buttons.each( function(item){
18347                     item.enable();
18348                 });
18349             }
18350             
18351         }
18352         Roo.log("calling toggole on editor");
18353         // tell the editor that it's been pressed..
18354         this.editor.toggleSourceEdit(sourceEditMode);
18355        
18356     }
18357 });
18358
18359
18360
18361
18362
18363 /**
18364  * @class Roo.bootstrap.Table.AbstractSelectionModel
18365  * @extends Roo.util.Observable
18366  * Abstract base class for grid SelectionModels.  It provides the interface that should be
18367  * implemented by descendant classes.  This class should not be directly instantiated.
18368  * @constructor
18369  */
18370 Roo.bootstrap.Table.AbstractSelectionModel = function(){
18371     this.locked = false;
18372     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
18373 };
18374
18375
18376 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
18377     /** @ignore Called by the grid automatically. Do not call directly. */
18378     init : function(grid){
18379         this.grid = grid;
18380         this.initEvents();
18381     },
18382
18383     /**
18384      * Locks the selections.
18385      */
18386     lock : function(){
18387         this.locked = true;
18388     },
18389
18390     /**
18391      * Unlocks the selections.
18392      */
18393     unlock : function(){
18394         this.locked = false;
18395     },
18396
18397     /**
18398      * Returns true if the selections are locked.
18399      * @return {Boolean}
18400      */
18401     isLocked : function(){
18402         return this.locked;
18403     }
18404 });
18405 /**
18406  * @extends Roo.bootstrap.Table.AbstractSelectionModel
18407  * @class Roo.bootstrap.Table.RowSelectionModel
18408  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
18409  * It supports multiple selections and keyboard selection/navigation. 
18410  * @constructor
18411  * @param {Object} config
18412  */
18413
18414 Roo.bootstrap.Table.RowSelectionModel = function(config){
18415     Roo.apply(this, config);
18416     this.selections = new Roo.util.MixedCollection(false, function(o){
18417         return o.id;
18418     });
18419
18420     this.last = false;
18421     this.lastActive = false;
18422
18423     this.addEvents({
18424         /**
18425              * @event selectionchange
18426              * Fires when the selection changes
18427              * @param {SelectionModel} this
18428              */
18429             "selectionchange" : true,
18430         /**
18431              * @event afterselectionchange
18432              * Fires after the selection changes (eg. by key press or clicking)
18433              * @param {SelectionModel} this
18434              */
18435             "afterselectionchange" : true,
18436         /**
18437              * @event beforerowselect
18438              * Fires when a row is selected being selected, return false to cancel.
18439              * @param {SelectionModel} this
18440              * @param {Number} rowIndex The selected index
18441              * @param {Boolean} keepExisting False if other selections will be cleared
18442              */
18443             "beforerowselect" : true,
18444         /**
18445              * @event rowselect
18446              * Fires when a row is selected.
18447              * @param {SelectionModel} this
18448              * @param {Number} rowIndex The selected index
18449              * @param {Roo.data.Record} r The record
18450              */
18451             "rowselect" : true,
18452         /**
18453              * @event rowdeselect
18454              * Fires when a row is deselected.
18455              * @param {SelectionModel} this
18456              * @param {Number} rowIndex The selected index
18457              */
18458         "rowdeselect" : true
18459     });
18460     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
18461     this.locked = false;
18462 };
18463
18464 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
18465     /**
18466      * @cfg {Boolean} singleSelect
18467      * True to allow selection of only one row at a time (defaults to false)
18468      */
18469     singleSelect : false,
18470
18471     // private
18472     initEvents : function(){
18473
18474         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
18475             this.grid.on("mousedown", this.handleMouseDown, this);
18476         }else{ // allow click to work like normal
18477             this.grid.on("rowclick", this.handleDragableRowClick, this);
18478         }
18479
18480         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
18481             "up" : function(e){
18482                 if(!e.shiftKey){
18483                     this.selectPrevious(e.shiftKey);
18484                 }else if(this.last !== false && this.lastActive !== false){
18485                     var last = this.last;
18486                     this.selectRange(this.last,  this.lastActive-1);
18487                     this.grid.getView().focusRow(this.lastActive);
18488                     if(last !== false){
18489                         this.last = last;
18490                     }
18491                 }else{
18492                     this.selectFirstRow();
18493                 }
18494                 this.fireEvent("afterselectionchange", this);
18495             },
18496             "down" : function(e){
18497                 if(!e.shiftKey){
18498                     this.selectNext(e.shiftKey);
18499                 }else if(this.last !== false && this.lastActive !== false){
18500                     var last = this.last;
18501                     this.selectRange(this.last,  this.lastActive+1);
18502                     this.grid.getView().focusRow(this.lastActive);
18503                     if(last !== false){
18504                         this.last = last;
18505                     }
18506                 }else{
18507                     this.selectFirstRow();
18508                 }
18509                 this.fireEvent("afterselectionchange", this);
18510             },
18511             scope: this
18512         });
18513
18514         var view = this.grid.view;
18515         view.on("refresh", this.onRefresh, this);
18516         view.on("rowupdated", this.onRowUpdated, this);
18517         view.on("rowremoved", this.onRemove, this);
18518     },
18519
18520     // private
18521     onRefresh : function(){
18522         var ds = this.grid.dataSource, i, v = this.grid.view;
18523         var s = this.selections;
18524         s.each(function(r){
18525             if((i = ds.indexOfId(r.id)) != -1){
18526                 v.onRowSelect(i);
18527             }else{
18528                 s.remove(r);
18529             }
18530         });
18531     },
18532
18533     // private
18534     onRemove : function(v, index, r){
18535         this.selections.remove(r);
18536     },
18537
18538     // private
18539     onRowUpdated : function(v, index, r){
18540         if(this.isSelected(r)){
18541             v.onRowSelect(index);
18542         }
18543     },
18544
18545     /**
18546      * Select records.
18547      * @param {Array} records The records to select
18548      * @param {Boolean} keepExisting (optional) True to keep existing selections
18549      */
18550     selectRecords : function(records, keepExisting){
18551         if(!keepExisting){
18552             this.clearSelections();
18553         }
18554         var ds = this.grid.dataSource;
18555         for(var i = 0, len = records.length; i < len; i++){
18556             this.selectRow(ds.indexOf(records[i]), true);
18557         }
18558     },
18559
18560     /**
18561      * Gets the number of selected rows.
18562      * @return {Number}
18563      */
18564     getCount : function(){
18565         return this.selections.length;
18566     },
18567
18568     /**
18569      * Selects the first row in the grid.
18570      */
18571     selectFirstRow : function(){
18572         this.selectRow(0);
18573     },
18574
18575     /**
18576      * Select the last row.
18577      * @param {Boolean} keepExisting (optional) True to keep existing selections
18578      */
18579     selectLastRow : function(keepExisting){
18580         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
18581     },
18582
18583     /**
18584      * Selects the row immediately following the last selected row.
18585      * @param {Boolean} keepExisting (optional) True to keep existing selections
18586      */
18587     selectNext : function(keepExisting){
18588         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
18589             this.selectRow(this.last+1, keepExisting);
18590             this.grid.getView().focusRow(this.last);
18591         }
18592     },
18593
18594     /**
18595      * Selects the row that precedes the last selected row.
18596      * @param {Boolean} keepExisting (optional) True to keep existing selections
18597      */
18598     selectPrevious : function(keepExisting){
18599         if(this.last){
18600             this.selectRow(this.last-1, keepExisting);
18601             this.grid.getView().focusRow(this.last);
18602         }
18603     },
18604
18605     /**
18606      * Returns the selected records
18607      * @return {Array} Array of selected records
18608      */
18609     getSelections : function(){
18610         return [].concat(this.selections.items);
18611     },
18612
18613     /**
18614      * Returns the first selected record.
18615      * @return {Record}
18616      */
18617     getSelected : function(){
18618         return this.selections.itemAt(0);
18619     },
18620
18621
18622     /**
18623      * Clears all selections.
18624      */
18625     clearSelections : function(fast){
18626         if(this.locked) return;
18627         if(fast !== true){
18628             var ds = this.grid.dataSource;
18629             var s = this.selections;
18630             s.each(function(r){
18631                 this.deselectRow(ds.indexOfId(r.id));
18632             }, this);
18633             s.clear();
18634         }else{
18635             this.selections.clear();
18636         }
18637         this.last = false;
18638     },
18639
18640
18641     /**
18642      * Selects all rows.
18643      */
18644     selectAll : function(){
18645         if(this.locked) return;
18646         this.selections.clear();
18647         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
18648             this.selectRow(i, true);
18649         }
18650     },
18651
18652     /**
18653      * Returns True if there is a selection.
18654      * @return {Boolean}
18655      */
18656     hasSelection : function(){
18657         return this.selections.length > 0;
18658     },
18659
18660     /**
18661      * Returns True if the specified row is selected.
18662      * @param {Number/Record} record The record or index of the record to check
18663      * @return {Boolean}
18664      */
18665     isSelected : function(index){
18666         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
18667         return (r && this.selections.key(r.id) ? true : false);
18668     },
18669
18670     /**
18671      * Returns True if the specified record id is selected.
18672      * @param {String} id The id of record to check
18673      * @return {Boolean}
18674      */
18675     isIdSelected : function(id){
18676         return (this.selections.key(id) ? true : false);
18677     },
18678
18679     // private
18680     handleMouseDown : function(e, t){
18681         var view = this.grid.getView(), rowIndex;
18682         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
18683             return;
18684         };
18685         if(e.shiftKey && this.last !== false){
18686             var last = this.last;
18687             this.selectRange(last, rowIndex, e.ctrlKey);
18688             this.last = last; // reset the last
18689             view.focusRow(rowIndex);
18690         }else{
18691             var isSelected = this.isSelected(rowIndex);
18692             if(e.button !== 0 && isSelected){
18693                 view.focusRow(rowIndex);
18694             }else if(e.ctrlKey && isSelected){
18695                 this.deselectRow(rowIndex);
18696             }else if(!isSelected){
18697                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
18698                 view.focusRow(rowIndex);
18699             }
18700         }
18701         this.fireEvent("afterselectionchange", this);
18702     },
18703     // private
18704     handleDragableRowClick :  function(grid, rowIndex, e) 
18705     {
18706         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
18707             this.selectRow(rowIndex, false);
18708             grid.view.focusRow(rowIndex);
18709              this.fireEvent("afterselectionchange", this);
18710         }
18711     },
18712     
18713     /**
18714      * Selects multiple rows.
18715      * @param {Array} rows Array of the indexes of the row to select
18716      * @param {Boolean} keepExisting (optional) True to keep existing selections
18717      */
18718     selectRows : function(rows, keepExisting){
18719         if(!keepExisting){
18720             this.clearSelections();
18721         }
18722         for(var i = 0, len = rows.length; i < len; i++){
18723             this.selectRow(rows[i], true);
18724         }
18725     },
18726
18727     /**
18728      * Selects a range of rows. All rows in between startRow and endRow are also selected.
18729      * @param {Number} startRow The index of the first row in the range
18730      * @param {Number} endRow The index of the last row in the range
18731      * @param {Boolean} keepExisting (optional) True to retain existing selections
18732      */
18733     selectRange : function(startRow, endRow, keepExisting){
18734         if(this.locked) return;
18735         if(!keepExisting){
18736             this.clearSelections();
18737         }
18738         if(startRow <= endRow){
18739             for(var i = startRow; i <= endRow; i++){
18740                 this.selectRow(i, true);
18741             }
18742         }else{
18743             for(var i = startRow; i >= endRow; i--){
18744                 this.selectRow(i, true);
18745             }
18746         }
18747     },
18748
18749     /**
18750      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
18751      * @param {Number} startRow The index of the first row in the range
18752      * @param {Number} endRow The index of the last row in the range
18753      */
18754     deselectRange : function(startRow, endRow, preventViewNotify){
18755         if(this.locked) return;
18756         for(var i = startRow; i <= endRow; i++){
18757             this.deselectRow(i, preventViewNotify);
18758         }
18759     },
18760
18761     /**
18762      * Selects a row.
18763      * @param {Number} row The index of the row to select
18764      * @param {Boolean} keepExisting (optional) True to keep existing selections
18765      */
18766     selectRow : function(index, keepExisting, preventViewNotify){
18767         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
18768         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
18769             if(!keepExisting || this.singleSelect){
18770                 this.clearSelections();
18771             }
18772             var r = this.grid.dataSource.getAt(index);
18773             this.selections.add(r);
18774             this.last = this.lastActive = index;
18775             if(!preventViewNotify){
18776                 this.grid.getView().onRowSelect(index);
18777             }
18778             this.fireEvent("rowselect", this, index, r);
18779             this.fireEvent("selectionchange", this);
18780         }
18781     },
18782
18783     /**
18784      * Deselects a row.
18785      * @param {Number} row The index of the row to deselect
18786      */
18787     deselectRow : function(index, preventViewNotify){
18788         if(this.locked) return;
18789         if(this.last == index){
18790             this.last = false;
18791         }
18792         if(this.lastActive == index){
18793             this.lastActive = false;
18794         }
18795         var r = this.grid.dataSource.getAt(index);
18796         this.selections.remove(r);
18797         if(!preventViewNotify){
18798             this.grid.getView().onRowDeselect(index);
18799         }
18800         this.fireEvent("rowdeselect", this, index);
18801         this.fireEvent("selectionchange", this);
18802     },
18803
18804     // private
18805     restoreLast : function(){
18806         if(this._last){
18807             this.last = this._last;
18808         }
18809     },
18810
18811     // private
18812     acceptsNav : function(row, col, cm){
18813         return !cm.isHidden(col) && cm.isCellEditable(col, row);
18814     },
18815
18816     // private
18817     onEditorKey : function(field, e){
18818         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
18819         if(k == e.TAB){
18820             e.stopEvent();
18821             ed.completeEdit();
18822             if(e.shiftKey){
18823                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
18824             }else{
18825                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
18826             }
18827         }else if(k == e.ENTER && !e.ctrlKey){
18828             e.stopEvent();
18829             ed.completeEdit();
18830             if(e.shiftKey){
18831                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
18832             }else{
18833                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
18834             }
18835         }else if(k == e.ESC){
18836             ed.cancelEdit();
18837         }
18838         if(newCell){
18839             g.startEditing(newCell[0], newCell[1]);
18840         }
18841     }
18842 });/*
18843  * Based on:
18844  * Ext JS Library 1.1.1
18845  * Copyright(c) 2006-2007, Ext JS, LLC.
18846  *
18847  * Originally Released Under LGPL - original licence link has changed is not relivant.
18848  *
18849  * Fork - LGPL
18850  * <script type="text/javascript">
18851  */
18852  
18853 /**
18854  * @class Roo.bootstrap.PagingToolbar
18855  * @extends Roo.Row
18856  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
18857  * @constructor
18858  * Create a new PagingToolbar
18859  * @param {Object} config The config object
18860  */
18861 Roo.bootstrap.PagingToolbar = function(config)
18862 {
18863     // old args format still supported... - xtype is prefered..
18864         // created from xtype...
18865     var ds = config.dataSource;
18866     this.toolbarItems = [];
18867     if (config.items) {
18868         this.toolbarItems = config.items;
18869 //        config.items = [];
18870     }
18871     
18872     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
18873     this.ds = ds;
18874     this.cursor = 0;
18875     if (ds) { 
18876         this.bind(ds);
18877     }
18878     
18879     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
18880     
18881 };
18882
18883 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
18884     /**
18885      * @cfg {Roo.data.Store} dataSource
18886      * The underlying data store providing the paged data
18887      */
18888     /**
18889      * @cfg {String/HTMLElement/Element} container
18890      * container The id or element that will contain the toolbar
18891      */
18892     /**
18893      * @cfg {Boolean} displayInfo
18894      * True to display the displayMsg (defaults to false)
18895      */
18896     /**
18897      * @cfg {Number} pageSize
18898      * The number of records to display per page (defaults to 20)
18899      */
18900     pageSize: 20,
18901     /**
18902      * @cfg {String} displayMsg
18903      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
18904      */
18905     displayMsg : 'Displaying {0} - {1} of {2}',
18906     /**
18907      * @cfg {String} emptyMsg
18908      * The message to display when no records are found (defaults to "No data to display")
18909      */
18910     emptyMsg : 'No data to display',
18911     /**
18912      * Customizable piece of the default paging text (defaults to "Page")
18913      * @type String
18914      */
18915     beforePageText : "Page",
18916     /**
18917      * Customizable piece of the default paging text (defaults to "of %0")
18918      * @type String
18919      */
18920     afterPageText : "of {0}",
18921     /**
18922      * Customizable piece of the default paging text (defaults to "First Page")
18923      * @type String
18924      */
18925     firstText : "First Page",
18926     /**
18927      * Customizable piece of the default paging text (defaults to "Previous Page")
18928      * @type String
18929      */
18930     prevText : "Previous Page",
18931     /**
18932      * Customizable piece of the default paging text (defaults to "Next Page")
18933      * @type String
18934      */
18935     nextText : "Next Page",
18936     /**
18937      * Customizable piece of the default paging text (defaults to "Last Page")
18938      * @type String
18939      */
18940     lastText : "Last Page",
18941     /**
18942      * Customizable piece of the default paging text (defaults to "Refresh")
18943      * @type String
18944      */
18945     refreshText : "Refresh",
18946
18947     buttons : false,
18948     // private
18949     onRender : function(ct, position) 
18950     {
18951         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
18952         this.navgroup.parentId = this.id;
18953         this.navgroup.onRender(this.el, null);
18954         // add the buttons to the navgroup
18955         
18956         if(this.displayInfo){
18957             Roo.log(this.el.select('ul.navbar-nav',true).first());
18958             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
18959             this.displayEl = this.el.select('.x-paging-info', true).first();
18960 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
18961 //            this.displayEl = navel.el.select('span',true).first();
18962         }
18963         
18964         var _this = this;
18965         
18966         if(this.buttons){
18967             Roo.each(_this.buttons, function(e){
18968                Roo.factory(e).onRender(_this.el, null);
18969             });
18970         }
18971             
18972         Roo.each(_this.toolbarItems, function(e) {
18973             _this.navgroup.addItem(e);
18974         });
18975         
18976         this.first = this.navgroup.addItem({
18977             tooltip: this.firstText,
18978             cls: "prev",
18979             icon : 'fa fa-backward',
18980             disabled: true,
18981             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
18982         });
18983         
18984         this.prev =  this.navgroup.addItem({
18985             tooltip: this.prevText,
18986             cls: "prev",
18987             icon : 'fa fa-step-backward',
18988             disabled: true,
18989             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
18990         });
18991     //this.addSeparator();
18992         
18993         
18994         var field = this.navgroup.addItem( {
18995             tagtype : 'span',
18996             cls : 'x-paging-position',
18997             
18998             html : this.beforePageText  +
18999                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
19000                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
19001          } ); //?? escaped?
19002         
19003         this.field = field.el.select('input', true).first();
19004         this.field.on("keydown", this.onPagingKeydown, this);
19005         this.field.on("focus", function(){this.dom.select();});
19006     
19007     
19008         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
19009         //this.field.setHeight(18);
19010         //this.addSeparator();
19011         this.next = this.navgroup.addItem({
19012             tooltip: this.nextText,
19013             cls: "next",
19014             html : ' <i class="fa fa-step-forward">',
19015             disabled: true,
19016             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
19017         });
19018         this.last = this.navgroup.addItem({
19019             tooltip: this.lastText,
19020             icon : 'fa fa-forward',
19021             cls: "next",
19022             disabled: true,
19023             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
19024         });
19025     //this.addSeparator();
19026         this.loading = this.navgroup.addItem({
19027             tooltip: this.refreshText,
19028             icon: 'fa fa-refresh',
19029             
19030             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
19031         });
19032
19033     },
19034
19035     // private
19036     updateInfo : function(){
19037         if(this.displayEl){
19038             var count = this.ds.getCount();
19039             var msg = count == 0 ?
19040                 this.emptyMsg :
19041                 String.format(
19042                     this.displayMsg,
19043                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
19044                 );
19045             this.displayEl.update(msg);
19046         }
19047     },
19048
19049     // private
19050     onLoad : function(ds, r, o){
19051        this.cursor = o.params ? o.params.start : 0;
19052        var d = this.getPageData(),
19053             ap = d.activePage,
19054             ps = d.pages;
19055         
19056        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
19057        this.field.dom.value = ap;
19058        this.first.setDisabled(ap == 1);
19059        this.prev.setDisabled(ap == 1);
19060        this.next.setDisabled(ap == ps);
19061        this.last.setDisabled(ap == ps);
19062        this.loading.enable();
19063        this.updateInfo();
19064     },
19065
19066     // private
19067     getPageData : function(){
19068         var total = this.ds.getTotalCount();
19069         return {
19070             total : total,
19071             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
19072             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
19073         };
19074     },
19075
19076     // private
19077     onLoadError : function(){
19078         this.loading.enable();
19079     },
19080
19081     // private
19082     onPagingKeydown : function(e){
19083         var k = e.getKey();
19084         var d = this.getPageData();
19085         if(k == e.RETURN){
19086             var v = this.field.dom.value, pageNum;
19087             if(!v || isNaN(pageNum = parseInt(v, 10))){
19088                 this.field.dom.value = d.activePage;
19089                 return;
19090             }
19091             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
19092             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
19093             e.stopEvent();
19094         }
19095         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))
19096         {
19097           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
19098           this.field.dom.value = pageNum;
19099           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
19100           e.stopEvent();
19101         }
19102         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
19103         {
19104           var v = this.field.dom.value, pageNum; 
19105           var increment = (e.shiftKey) ? 10 : 1;
19106           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
19107             increment *= -1;
19108           if(!v || isNaN(pageNum = parseInt(v, 10))) {
19109             this.field.dom.value = d.activePage;
19110             return;
19111           }
19112           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
19113           {
19114             this.field.dom.value = parseInt(v, 10) + increment;
19115             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
19116             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
19117           }
19118           e.stopEvent();
19119         }
19120     },
19121
19122     // private
19123     beforeLoad : function(){
19124         if(this.loading){
19125             this.loading.disable();
19126         }
19127     },
19128
19129     // private
19130     onClick : function(which){
19131         var ds = this.ds;
19132         if (!ds) {
19133             return;
19134         }
19135         switch(which){
19136             case "first":
19137                 ds.load({params:{start: 0, limit: this.pageSize}});
19138             break;
19139             case "prev":
19140                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
19141             break;
19142             case "next":
19143                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
19144             break;
19145             case "last":
19146                 var total = ds.getTotalCount();
19147                 var extra = total % this.pageSize;
19148                 var lastStart = extra ? (total - extra) : total-this.pageSize;
19149                 ds.load({params:{start: lastStart, limit: this.pageSize}});
19150             break;
19151             case "refresh":
19152                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
19153             break;
19154         }
19155     },
19156
19157     /**
19158      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
19159      * @param {Roo.data.Store} store The data store to unbind
19160      */
19161     unbind : function(ds){
19162         ds.un("beforeload", this.beforeLoad, this);
19163         ds.un("load", this.onLoad, this);
19164         ds.un("loadexception", this.onLoadError, this);
19165         ds.un("remove", this.updateInfo, this);
19166         ds.un("add", this.updateInfo, this);
19167         this.ds = undefined;
19168     },
19169
19170     /**
19171      * Binds the paging toolbar to the specified {@link Roo.data.Store}
19172      * @param {Roo.data.Store} store The data store to bind
19173      */
19174     bind : function(ds){
19175         ds.on("beforeload", this.beforeLoad, this);
19176         ds.on("load", this.onLoad, this);
19177         ds.on("loadexception", this.onLoadError, this);
19178         ds.on("remove", this.updateInfo, this);
19179         ds.on("add", this.updateInfo, this);
19180         this.ds = ds;
19181     }
19182 });/*
19183  * - LGPL
19184  *
19185  * element
19186  * 
19187  */
19188
19189 /**
19190  * @class Roo.bootstrap.MessageBar
19191  * @extends Roo.bootstrap.Component
19192  * Bootstrap MessageBar class
19193  * @cfg {String} html contents of the MessageBar
19194  * @cfg {String} weight (info | success | warning | danger) default info
19195  * @cfg {String} beforeClass insert the bar before the given class
19196  * @cfg {Boolean} closable (true | false) default false
19197  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
19198  * 
19199  * @constructor
19200  * Create a new Element
19201  * @param {Object} config The config object
19202  */
19203
19204 Roo.bootstrap.MessageBar = function(config){
19205     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
19206 };
19207
19208 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
19209     
19210     html: '',
19211     weight: 'info',
19212     closable: false,
19213     fixed: false,
19214     beforeClass: 'bootstrap-sticky-wrap',
19215     
19216     getAutoCreate : function(){
19217         
19218         var cfg = {
19219             tag: 'div',
19220             cls: 'alert alert-dismissable alert-' + this.weight,
19221             cn: [
19222                 {
19223                     tag: 'span',
19224                     cls: 'message',
19225                     html: this.html || ''
19226                 }
19227             ]
19228         }
19229         
19230         if(this.fixed){
19231             cfg.cls += ' alert-messages-fixed';
19232         }
19233         
19234         if(this.closable){
19235             cfg.cn.push({
19236                 tag: 'button',
19237                 cls: 'close',
19238                 html: 'x'
19239             });
19240         }
19241         
19242         return cfg;
19243     },
19244     
19245     onRender : function(ct, position)
19246     {
19247         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19248         
19249         if(!this.el){
19250             var cfg = Roo.apply({},  this.getAutoCreate());
19251             cfg.id = Roo.id();
19252             
19253             if (this.cls) {
19254                 cfg.cls += ' ' + this.cls;
19255             }
19256             if (this.style) {
19257                 cfg.style = this.style;
19258             }
19259             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
19260             
19261             this.el.setVisibilityMode(Roo.Element.DISPLAY);
19262         }
19263         
19264         this.el.select('>button.close').on('click', this.hide, this);
19265         
19266     },
19267     
19268     show : function()
19269     {
19270         if (!this.rendered) {
19271             this.render();
19272         }
19273         
19274         this.el.show();
19275         
19276         this.fireEvent('show', this);
19277         
19278     },
19279     
19280     hide : function()
19281     {
19282         if (!this.rendered) {
19283             this.render();
19284         }
19285         
19286         this.el.hide();
19287         
19288         this.fireEvent('hide', this);
19289     },
19290     
19291     update : function()
19292     {
19293 //        var e = this.el.dom.firstChild;
19294 //        
19295 //        if(this.closable){
19296 //            e = e.nextSibling;
19297 //        }
19298 //        
19299 //        e.data = this.html || '';
19300
19301         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
19302     }
19303    
19304 });
19305
19306  
19307
19308      /*
19309  * - LGPL
19310  *
19311  * Graph
19312  * 
19313  */
19314
19315
19316 /**
19317  * @class Roo.bootstrap.Graph
19318  * @extends Roo.bootstrap.Component
19319  * Bootstrap Graph class
19320 > Prameters
19321  -sm {number} sm 4
19322  -md {number} md 5
19323  @cfg {String} graphtype  bar | vbar | pie
19324  @cfg {number} g_x coodinator | centre x (pie)
19325  @cfg {number} g_y coodinator | centre y (pie)
19326  @cfg {number} g_r radius (pie)
19327  @cfg {number} g_height height of the chart (respected by all elements in the set)
19328  @cfg {number} g_width width of the chart (respected by all elements in the set)
19329  @cfg {Object} title The title of the chart
19330     
19331  -{Array}  values
19332  -opts (object) options for the chart 
19333      o {
19334      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
19335      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
19336      o vgutter (number)
19337      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.
19338      o stacked (boolean) whether or not to tread values as in a stacked bar chart
19339      o to
19340      o stretch (boolean)
19341      o }
19342  -opts (object) options for the pie
19343      o{
19344      o cut
19345      o startAngle (number)
19346      o endAngle (number)
19347      } 
19348  *
19349  * @constructor
19350  * Create a new Input
19351  * @param {Object} config The config object
19352  */
19353
19354 Roo.bootstrap.Graph = function(config){
19355     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
19356     
19357     this.addEvents({
19358         // img events
19359         /**
19360          * @event click
19361          * The img click event for the img.
19362          * @param {Roo.EventObject} e
19363          */
19364         "click" : true
19365     });
19366 };
19367
19368 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
19369     
19370     sm: 4,
19371     md: 5,
19372     graphtype: 'bar',
19373     g_height: 250,
19374     g_width: 400,
19375     g_x: 50,
19376     g_y: 50,
19377     g_r: 30,
19378     opts:{
19379         //g_colors: this.colors,
19380         g_type: 'soft',
19381         g_gutter: '20%'
19382
19383     },
19384     title : false,
19385
19386     getAutoCreate : function(){
19387         
19388         var cfg = {
19389             tag: 'div',
19390             html : null
19391         }
19392         
19393         
19394         return  cfg;
19395     },
19396
19397     onRender : function(ct,position){
19398         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
19399         this.raphael = Raphael(this.el.dom);
19400         
19401                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19402                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19403                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19404                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
19405                 /*
19406                 r.text(160, 10, "Single Series Chart").attr(txtattr);
19407                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
19408                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
19409                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
19410                 
19411                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
19412                 r.barchart(330, 10, 300, 220, data1);
19413                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
19414                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
19415                 */
19416                 
19417                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19418                 // r.barchart(30, 30, 560, 250,  xdata, {
19419                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
19420                 //     axis : "0 0 1 1",
19421                 //     axisxlabels :  xdata
19422                 //     //yvalues : cols,
19423                    
19424                 // });
19425 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19426 //        
19427 //        this.load(null,xdata,{
19428 //                axis : "0 0 1 1",
19429 //                axisxlabels :  xdata
19430 //                });
19431
19432     },
19433
19434     load : function(graphtype,xdata,opts){
19435         this.raphael.clear();
19436         if(!graphtype) {
19437             graphtype = this.graphtype;
19438         }
19439         if(!opts){
19440             opts = this.opts;
19441         }
19442         var r = this.raphael,
19443             fin = function () {
19444                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
19445             },
19446             fout = function () {
19447                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
19448             },
19449             pfin = function() {
19450                 this.sector.stop();
19451                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
19452
19453                 if (this.label) {
19454                     this.label[0].stop();
19455                     this.label[0].attr({ r: 7.5 });
19456                     this.label[1].attr({ "font-weight": 800 });
19457                 }
19458             },
19459             pfout = function() {
19460                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
19461
19462                 if (this.label) {
19463                     this.label[0].animate({ r: 5 }, 500, "bounce");
19464                     this.label[1].attr({ "font-weight": 400 });
19465                 }
19466             };
19467
19468         switch(graphtype){
19469             case 'bar':
19470                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
19471                 break;
19472             case 'hbar':
19473                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
19474                 break;
19475             case 'pie':
19476 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
19477 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
19478 //            
19479                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
19480                 
19481                 break;
19482
19483         }
19484         
19485         if(this.title){
19486             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
19487         }
19488         
19489     },
19490     
19491     setTitle: function(o)
19492     {
19493         this.title = o;
19494     },
19495     
19496     initEvents: function() {
19497         
19498         if(!this.href){
19499             this.el.on('click', this.onClick, this);
19500         }
19501     },
19502     
19503     onClick : function(e)
19504     {
19505         Roo.log('img onclick');
19506         this.fireEvent('click', this, e);
19507     }
19508    
19509 });
19510
19511  
19512 /*
19513  * - LGPL
19514  *
19515  * numberBox
19516  * 
19517  */
19518 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19519
19520 /**
19521  * @class Roo.bootstrap.dash.NumberBox
19522  * @extends Roo.bootstrap.Component
19523  * Bootstrap NumberBox class
19524  * @cfg {String} bgcolor Box background color, such as (aqua | green | yellow | red etc..) Default aqua
19525  * @cfg {String} headline Box headline
19526  * @cfg {String} content Box content
19527  * @cfg {String} icon Box icon
19528  * @cfg {String} footer Footer text
19529  * @cfg {String} fhref Footer href
19530  * 
19531  * @constructor
19532  * Create a new NumberBox
19533  * @param {Object} config The config object
19534  */
19535
19536
19537 Roo.bootstrap.dash.NumberBox = function(config){
19538     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
19539     
19540 };
19541
19542 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
19543     
19544     bgcolor : 'aqua',
19545     headline : '',
19546     content : '',
19547     icon : '',
19548     footer : '',
19549     fhref : '',
19550     ficon : '',
19551     
19552     getAutoCreate : function(){
19553         
19554         var cfg = {
19555             tag : 'div',
19556             cls : 'small-box bg-' + this.bgcolor,
19557             cn : [
19558                 {
19559                     tag : 'div',
19560                     cls : 'inner',
19561                     cn :[
19562                         {
19563                             tag : 'h3',
19564                             cls : 'roo-headline',
19565                             html : this.headline
19566                         },
19567                         {
19568                             tag : 'p',
19569                             cls : 'roo-content',
19570                             html : this.content
19571                         }
19572                     ]
19573                 }
19574             ]
19575         }
19576         
19577         if(this.icon){
19578             cfg.cn.push({
19579                 tag : 'div',
19580                 cls : 'icon',
19581                 cn :[
19582                     {
19583                         tag : 'i',
19584                         cls : 'ion ' + this.icon
19585                     }
19586                 ]
19587             });
19588         }
19589         
19590         if(this.footer){
19591             var footer = {
19592                 tag : 'a',
19593                 cls : 'small-box-footer',
19594                 href : this.fhref || '#',
19595                 html : this.footer
19596             };
19597             
19598             cfg.cn.push(footer);
19599             
19600         }
19601         
19602         return  cfg;
19603     },
19604
19605     onRender : function(ct,position){
19606         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
19607
19608
19609        
19610                 
19611     },
19612
19613     setHeadline: function (value)
19614     {
19615         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
19616     },
19617     
19618     setFooter: function (value, href)
19619     {
19620         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
19621         
19622         if(href){
19623             this.el.select('a.small-box-footer',true).first().attr('href', href);
19624         }
19625         
19626     },
19627
19628     setContent: function (value)
19629     {
19630         this.el.select('.roo-content',true).first().dom.innerHTML = value;
19631     },
19632
19633     initEvents: function() 
19634     {   
19635         
19636     }
19637     
19638 });
19639
19640  
19641 /*
19642  * - LGPL
19643  *
19644  * TabBox
19645  * 
19646  */
19647 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19648
19649 /**
19650  * @class Roo.bootstrap.dash.TabBox
19651  * @extends Roo.bootstrap.Component
19652  * Bootstrap TabBox class
19653  * @cfg {String} title Title of the TabBox
19654  * @cfg {String} icon Icon of the TabBox
19655  * @cfg {Boolean} showtabs (true|false) show the tabs default true
19656  * 
19657  * @constructor
19658  * Create a new TabBox
19659  * @param {Object} config The config object
19660  */
19661
19662
19663 Roo.bootstrap.dash.TabBox = function(config){
19664     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
19665     this.addEvents({
19666         // raw events
19667         /**
19668          * @event addpane
19669          * When a pane is added
19670          * @param {Roo.bootstrap.dash.TabPane} pane
19671          */
19672         "addpane" : true
19673          
19674     });
19675 };
19676
19677 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
19678
19679     title : '',
19680     icon : false,
19681     showtabs : true,
19682     
19683     getChildContainer : function()
19684     {
19685         return this.el.select('.tab-content', true).first();
19686     },
19687     
19688     getAutoCreate : function(){
19689         
19690         var header = {
19691             tag: 'li',
19692             cls: 'pull-left header',
19693             html: this.title,
19694             cn : []
19695         };
19696         
19697         if(this.icon){
19698             header.cn.push({
19699                 tag: 'i',
19700                 cls: 'fa ' + this.icon
19701             });
19702         }
19703         
19704         
19705         var cfg = {
19706             tag: 'div',
19707             cls: 'nav-tabs-custom',
19708             cn: [
19709                 {
19710                     tag: 'ul',
19711                     cls: 'nav nav-tabs pull-right',
19712                     cn: [
19713                         header
19714                     ]
19715                 },
19716                 {
19717                     tag: 'div',
19718                     cls: 'tab-content no-padding',
19719                     cn: []
19720                 }
19721             ]
19722         }
19723
19724         return  cfg;
19725     },
19726     initEvents : function()
19727     {
19728         //Roo.log('add add pane handler');
19729         this.on('addpane', this.onAddPane, this);
19730     },
19731      /**
19732      * Updates the box title
19733      * @param {String} html to set the title to.
19734      */
19735     setTitle : function(value)
19736     {
19737         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
19738     },
19739     onAddPane : function(pane)
19740     {
19741         //Roo.log('addpane');
19742         //Roo.log(pane);
19743         // tabs are rendere left to right..
19744         if(!this.showtabs){
19745             return;
19746         }
19747         
19748         var ctr = this.el.select('.nav-tabs', true).first();
19749          
19750          
19751         var existing = ctr.select('.nav-tab',true);
19752         var qty = existing.getCount();;
19753         
19754         
19755         var tab = ctr.createChild({
19756             tag : 'li',
19757             cls : 'nav-tab' + (qty ? '' : ' active'),
19758             cn : [
19759                 {
19760                     tag : 'a',
19761                     href:'#',
19762                     html : pane.title
19763                 }
19764             ]
19765         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
19766         pane.tab = tab;
19767         
19768         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
19769         if (!qty) {
19770             pane.el.addClass('active');
19771         }
19772         
19773                 
19774     },
19775     onTabClick : function(ev,un,ob,pane)
19776     {
19777         //Roo.log('tab - prev default');
19778         ev.preventDefault();
19779         
19780         
19781         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
19782         pane.tab.addClass('active');
19783         //Roo.log(pane.title);
19784         this.getChildContainer().select('.tab-pane',true).removeClass('active');
19785         // technically we should have a deactivate event.. but maybe add later.
19786         // and it should not de-activate the selected tab...
19787         
19788         pane.el.addClass('active');
19789         pane.fireEvent('activate');
19790         
19791         
19792     }
19793     
19794     
19795 });
19796
19797  
19798 /*
19799  * - LGPL
19800  *
19801  * Tab pane
19802  * 
19803  */
19804 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19805 /**
19806  * @class Roo.bootstrap.TabPane
19807  * @extends Roo.bootstrap.Component
19808  * Bootstrap TabPane class
19809  * @cfg {Boolean} active (false | true) Default false
19810  * @cfg {String} title title of panel
19811
19812  * 
19813  * @constructor
19814  * Create a new TabPane
19815  * @param {Object} config The config object
19816  */
19817
19818 Roo.bootstrap.dash.TabPane = function(config){
19819     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
19820     
19821     this.addEvents({
19822         // raw events
19823         /**
19824          * @event activate
19825          * When a pane is activated
19826          * @param {Roo.bootstrap.dash.TabPane} pane
19827          */
19828         "activate" : true
19829          
19830     });
19831 };
19832
19833 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
19834     
19835     active : false,
19836     title : '',
19837     
19838     // the tabBox that this is attached to.
19839     tab : false,
19840      
19841     getAutoCreate : function() 
19842     {
19843         var cfg = {
19844             tag: 'div',
19845             cls: 'tab-pane'
19846         }
19847         
19848         if(this.active){
19849             cfg.cls += ' active';
19850         }
19851         
19852         return cfg;
19853     },
19854     initEvents  : function()
19855     {
19856         //Roo.log('trigger add pane handler');
19857         this.parent().fireEvent('addpane', this)
19858     },
19859     
19860      /**
19861      * Updates the tab title 
19862      * @param {String} html to set the title to.
19863      */
19864     setTitle: function(str)
19865     {
19866         if (!this.tab) {
19867             return;
19868         }
19869         this.title = str;
19870         this.tab.select('a', true).first().dom.innerHTML = str;
19871         
19872     }
19873     
19874     
19875     
19876 });
19877
19878  
19879
19880
19881  /*
19882  * - LGPL
19883  *
19884  * menu
19885  * 
19886  */
19887 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
19888
19889 /**
19890  * @class Roo.bootstrap.menu.Menu
19891  * @extends Roo.bootstrap.Component
19892  * Bootstrap Menu class - container for Menu
19893  * @cfg {String} html Text of the menu
19894  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
19895  * @cfg {String} icon Font awesome icon
19896  * @cfg {String} pos Menu align to (top | bottom) default bottom
19897  * 
19898  * 
19899  * @constructor
19900  * Create a new Menu
19901  * @param {Object} config The config object
19902  */
19903
19904
19905 Roo.bootstrap.menu.Menu = function(config){
19906     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
19907     
19908     this.addEvents({
19909         /**
19910          * @event beforeshow
19911          * Fires before this menu is displayed
19912          * @param {Roo.bootstrap.menu.Menu} this
19913          */
19914         beforeshow : true,
19915         /**
19916          * @event beforehide
19917          * Fires before this menu is hidden
19918          * @param {Roo.bootstrap.menu.Menu} this
19919          */
19920         beforehide : true,
19921         /**
19922          * @event show
19923          * Fires after this menu is displayed
19924          * @param {Roo.bootstrap.menu.Menu} this
19925          */
19926         show : true,
19927         /**
19928          * @event hide
19929          * Fires after this menu is hidden
19930          * @param {Roo.bootstrap.menu.Menu} this
19931          */
19932         hide : true,
19933         /**
19934          * @event click
19935          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19936          * @param {Roo.bootstrap.menu.Menu} this
19937          * @param {Roo.EventObject} e
19938          */
19939         click : true
19940     });
19941     
19942 };
19943
19944 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
19945     
19946     submenu : false,
19947     html : '',
19948     weight : 'default',
19949     icon : false,
19950     pos : 'bottom',
19951     
19952     
19953     getChildContainer : function() {
19954         if(this.isSubMenu){
19955             return this.el;
19956         }
19957         
19958         return this.el.select('ul.dropdown-menu', true).first();  
19959     },
19960     
19961     getAutoCreate : function()
19962     {
19963         var text = [
19964             {
19965                 tag : 'span',
19966                 cls : 'roo-menu-text',
19967                 html : this.html
19968             }
19969         ];
19970         
19971         if(this.icon){
19972             text.unshift({
19973                 tag : 'i',
19974                 cls : 'fa ' + this.icon
19975             })
19976         }
19977         
19978         
19979         var cfg = {
19980             tag : 'div',
19981             cls : 'btn-group',
19982             cn : [
19983                 {
19984                     tag : 'button',
19985                     cls : 'dropdown-button btn btn-' + this.weight,
19986                     cn : text
19987                 },
19988                 {
19989                     tag : 'button',
19990                     cls : 'dropdown-toggle btn btn-' + this.weight,
19991                     cn : [
19992                         {
19993                             tag : 'span',
19994                             cls : 'caret'
19995                         }
19996                     ]
19997                 },
19998                 {
19999                     tag : 'ul',
20000                     cls : 'dropdown-menu'
20001                 }
20002             ]
20003             
20004         };
20005         
20006         if(this.pos == 'top'){
20007             cfg.cls += ' dropup';
20008         }
20009         
20010         if(this.isSubMenu){
20011             cfg = {
20012                 tag : 'ul',
20013                 cls : 'dropdown-menu'
20014             }
20015         }
20016         
20017         return cfg;
20018     },
20019     
20020     onRender : function(ct, position)
20021     {
20022         this.isSubMenu = ct.hasClass('dropdown-submenu');
20023         
20024         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
20025     },
20026     
20027     initEvents : function() 
20028     {
20029         if(this.isSubMenu){
20030             return;
20031         }
20032         
20033         this.hidden = true;
20034         
20035         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
20036         this.triggerEl.on('click', this.onTriggerPress, this);
20037         
20038         this.buttonEl = this.el.select('button.dropdown-button', true).first();
20039         this.buttonEl.on('click', this.onClick, this);
20040         
20041     },
20042     
20043     list : function()
20044     {
20045         if(this.isSubMenu){
20046             return this.el;
20047         }
20048         
20049         return this.el.select('ul.dropdown-menu', true).first();
20050     },
20051     
20052     onClick : function(e)
20053     {
20054         this.fireEvent("click", this, e);
20055     },
20056     
20057     onTriggerPress  : function(e)
20058     {   
20059         if (this.isVisible()) {
20060             this.hide();
20061         } else {
20062             this.show();
20063         }
20064     },
20065     
20066     isVisible : function(){
20067         return !this.hidden;
20068     },
20069     
20070     show : function()
20071     {
20072         this.fireEvent("beforeshow", this);
20073         
20074         this.hidden = false;
20075         this.el.addClass('open');
20076         
20077         Roo.get(document).on("mouseup", this.onMouseUp, this);
20078         
20079         this.fireEvent("show", this);
20080         
20081         
20082     },
20083     
20084     hide : function()
20085     {
20086         this.fireEvent("beforehide", this);
20087         
20088         this.hidden = true;
20089         this.el.removeClass('open');
20090         
20091         Roo.get(document).un("mouseup", this.onMouseUp);
20092         
20093         this.fireEvent("hide", this);
20094     },
20095     
20096     onMouseUp : function()
20097     {
20098         this.hide();
20099     }
20100     
20101 });
20102
20103  
20104  /*
20105  * - LGPL
20106  *
20107  * menu item
20108  * 
20109  */
20110 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20111
20112 /**
20113  * @class Roo.bootstrap.menu.Item
20114  * @extends Roo.bootstrap.Component
20115  * Bootstrap MenuItem class
20116  * @cfg {Boolean} submenu (true | false) default false
20117  * @cfg {String} html text of the item
20118  * @cfg {String} href the link
20119  * @cfg {Boolean} disable (true | false) default false
20120  * @cfg {Boolean} preventDefault (true | false) default true
20121  * @cfg {String} icon Font awesome icon
20122  * @cfg {String} pos Submenu align to (left | right) default right 
20123  * 
20124  * 
20125  * @constructor
20126  * Create a new Item
20127  * @param {Object} config The config object
20128  */
20129
20130
20131 Roo.bootstrap.menu.Item = function(config){
20132     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
20133     this.addEvents({
20134         /**
20135          * @event mouseover
20136          * Fires when the mouse is hovering over this menu
20137          * @param {Roo.bootstrap.menu.Item} this
20138          * @param {Roo.EventObject} e
20139          */
20140         mouseover : true,
20141         /**
20142          * @event mouseout
20143          * Fires when the mouse exits this menu
20144          * @param {Roo.bootstrap.menu.Item} this
20145          * @param {Roo.EventObject} e
20146          */
20147         mouseout : true,
20148         // raw events
20149         /**
20150          * @event click
20151          * The raw click event for the entire grid.
20152          * @param {Roo.EventObject} e
20153          */
20154         click : true
20155     });
20156 };
20157
20158 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
20159     
20160     submenu : false,
20161     href : '',
20162     html : '',
20163     preventDefault: true,
20164     disable : false,
20165     icon : false,
20166     pos : 'right',
20167     
20168     getAutoCreate : function()
20169     {
20170         var text = [
20171             {
20172                 tag : 'span',
20173                 cls : 'roo-menu-item-text',
20174                 html : this.html
20175             }
20176         ];
20177         
20178         if(this.icon){
20179             text.unshift({
20180                 tag : 'i',
20181                 cls : 'fa ' + this.icon
20182             })
20183         }
20184         
20185         var cfg = {
20186             tag : 'li',
20187             cn : [
20188                 {
20189                     tag : 'a',
20190                     href : this.href || '#',
20191                     cn : text
20192                 }
20193             ]
20194         };
20195         
20196         if(this.disable){
20197             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
20198         }
20199         
20200         if(this.submenu){
20201             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
20202             
20203             if(this.pos == 'left'){
20204                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
20205             }
20206         }
20207         
20208         return cfg;
20209     },
20210     
20211     initEvents : function() 
20212     {
20213         this.el.on('mouseover', this.onMouseOver, this);
20214         this.el.on('mouseout', this.onMouseOut, this);
20215         
20216         this.el.select('a', true).first().on('click', this.onClick, this);
20217         
20218     },
20219     
20220     onClick : function(e)
20221     {
20222         if(this.preventDefault){
20223             e.preventDefault();
20224         }
20225         
20226         this.fireEvent("click", this, e);
20227     },
20228     
20229     onMouseOver : function(e)
20230     {
20231         if(this.submenu && this.pos == 'left'){
20232             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
20233         }
20234         
20235         this.fireEvent("mouseover", this, e);
20236     },
20237     
20238     onMouseOut : function(e)
20239     {
20240         this.fireEvent("mouseout", this, e);
20241     }
20242 });
20243
20244  
20245
20246  /*
20247  * - LGPL
20248  *
20249  * menu separator
20250  * 
20251  */
20252 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20253
20254 /**
20255  * @class Roo.bootstrap.menu.Separator
20256  * @extends Roo.bootstrap.Component
20257  * Bootstrap Separator class
20258  * 
20259  * @constructor
20260  * Create a new Separator
20261  * @param {Object} config The config object
20262  */
20263
20264
20265 Roo.bootstrap.menu.Separator = function(config){
20266     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
20267 };
20268
20269 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
20270     
20271     getAutoCreate : function(){
20272         var cfg = {
20273             tag : 'li',
20274             cls: 'divider'
20275         };
20276         
20277         return cfg;
20278     }
20279    
20280 });
20281
20282  
20283
20284