Roo/bootstrap/Column.js
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * 
20  * @constructor
21  * Do not use directly - it does not do anything..
22  * @param {Object} config The config object
23  */
24
25
26
27 Roo.bootstrap.Component = function(config){
28     Roo.bootstrap.Component.superclass.constructor.call(this, config);
29 };
30
31 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
32     
33     
34     allowDomMove : false, // to stop relocations in parent onRender...
35     
36     cls : false,
37     
38     style : false,
39     
40     autoCreate : false,
41     
42     initEvents : function() {  },
43     
44     xattr : false,
45     
46     parentId : false,
47     
48     can_build_overlaid : true,
49     
50     dataId : false,
51     
52     name : false,
53     
54     parent: function() {
55         // returns the parent component..
56         return Roo.ComponentMgr.get(this.parentId)
57         
58         
59     },
60     
61     // private
62     onRender : function(ct, position)
63     {
64        // Roo.log("Call onRender: " + this.xtype);
65         
66         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
67         
68         if(this.el){
69             if (this.el.attr('xtype')) {
70                 this.el.attr('xtypex', this.el.attr('xtype'));
71                 this.el.dom.removeAttribute('xtype');
72                 
73                 this.initEvents();
74             }
75             
76             return;
77         }
78         
79          
80         
81         var cfg = Roo.apply({},  this.getAutoCreate());
82         cfg.id = Roo.id();
83         
84         // fill in the extra attributes 
85         if (this.xattr && typeof(this.xattr) =='object') {
86             for (var i in this.xattr) {
87                 cfg[i] = this.xattr[i];
88             }
89         }
90         
91         if(this.dataId){
92             cfg.dataId = this.dataId;
93         }
94         
95         if (this.cls) {
96             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
97         }
98         
99         if (this.style) { // fixme needs to support more complex style data.
100             cfg.style = this.style;
101         }
102         
103         if(this.name){
104             cfg.name = this.name;
105         }
106         
107         this.el = ct.createChild(cfg, position);
108         
109         if(this.tabIndex !== undefined){
110             this.el.dom.setAttribute('tabIndex', this.tabIndex);
111         }
112         this.initEvents();
113         
114         
115     },
116     
117     getChildContainer : function()
118     {
119         return this.el;
120     },
121     
122     
123     addxtype  : function(tree,cntr)
124     {
125         var cn = this;
126         
127         cn = Roo.factory(tree);
128            
129         cn.parentType = this.xtype; //??
130         cn.parentId = this.id;
131         
132         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
133         
134         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
135         
136         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
137         
138         var build_from_html =  Roo.XComponent.build_from_html;
139           
140         var is_body  = (tree.xtype == 'Body') ;
141           
142         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
143           
144         var self_cntr_el = Roo.get(this[cntr](false));
145         
146         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
147             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
148                 return this.addxtypeChild(tree,cntr);
149             }
150             
151             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
152                 
153             if(echild){
154                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
155             }
156             
157             Roo.log('skipping render');
158             return cn;
159             
160         }
161         
162         var ret = false;
163         
164         while (true) {
165             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
166             
167             if (!echild) {
168                 break;
169             }
170             
171             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
172                 break;
173             }
174             
175             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
176         }
177         return ret;
178     },
179     
180     addxtypeChild : function (tree, cntr)
181     {
182         Roo.log('addxtypeChild:' + cntr);
183         var cn = this;
184         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
185         
186         
187         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
188                     (typeof(tree['flexy:foreach']) != 'undefined');
189           
190         
191         
192         
193         // render the element if it's not BODY.
194         if (tree.xtype != 'Body') {
195            
196             cn = Roo.factory(tree);
197            
198             cn.parentType = this.xtype; //??
199             cn.parentId = this.id;
200             
201             var build_from_html =  Roo.XComponent.build_from_html;
202             
203             
204             // does the container contain child eleemnts with 'xtype' attributes.
205             // that match this xtype..
206             // note - when we render we create these as well..
207             // so we should check to see if body has xtype set.
208             if (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
209                
210                 var self_cntr_el = Roo.get(this[cntr](false));
211                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
212                 
213                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
214                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
215                   
216                   
217                   
218                     cn.el = echild;
219                   //  Roo.log("GOT");
220                     //echild.dom.removeAttribute('xtype');
221                 } else {
222                     Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
223                    
224                 }
225             }
226            
227             
228                
229             // if object has flexy:if - then it may or may not be rendered.
230             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
231                 // skip a flexy if element.
232                 Roo.log('skipping render');
233              } else {
234                  
235                 // actually if flexy:foreach is found, we really want to create 
236                 // multiple copies here...
237                 //Roo.log('render');
238                 //Roo.log(this[cntr]());
239                 cn.render(this[cntr](true));
240              }
241             // then add the element..
242         }
243         
244         
245         // handle the kids..
246         
247         var nitems = [];
248         /*
249         if (typeof (tree.menu) != 'undefined') {
250             tree.menu.parentType = cn.xtype;
251             tree.menu.triggerEl = cn.el;
252             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
253             
254         }
255         */
256         if (!tree.items || !tree.items.length) {
257             cn.items = nitems;
258             return cn;
259         }
260         var items = tree.items;
261         delete tree.items;
262         
263         //Roo.log(items.length);
264             // add the items..
265         for(var i =0;i < items.length;i++) {
266             nitems.push(cn.addxtype(Roo.apply({}, items[i])));
267         }
268         
269         cn.items = nitems;
270         
271         return cn;
272     }
273     
274     
275     
276     
277 });
278
279  /*
280  * - LGPL
281  *
282  * Body
283  * 
284  */
285
286 /**
287  * @class Roo.bootstrap.Body
288  * @extends Roo.bootstrap.Component
289  * Bootstrap Body class
290  * 
291  * @constructor
292  * Create a new body
293  * @param {Object} config The config object
294  */
295
296 Roo.bootstrap.Body = function(config){
297     Roo.bootstrap.Body.superclass.constructor.call(this, config);
298     this.el = Roo.get(document.body);
299     if (this.cls && this.cls.length) {
300         Roo.get(document.body).addClass(this.cls);
301     }
302 };
303
304 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
305       
306         autoCreate : {
307         cls: 'container'
308     },
309     onRender : function(ct, position)
310     {
311        /* Roo.log("Roo.bootstrap.Body - onRender");
312         if (this.cls && this.cls.length) {
313             Roo.get(document.body).addClass(this.cls);
314         }
315         // style??? xttr???
316         */
317     }
318     
319     
320  
321    
322 });
323
324  /*
325  * - LGPL
326  *
327  * button group
328  * 
329  */
330
331
332 /**
333  * @class Roo.bootstrap.ButtonGroup
334  * @extends Roo.bootstrap.Component
335  * Bootstrap ButtonGroup class
336  * @cfg {String} size lg | sm | xs (default empty normal)
337  * @cfg {String} align vertical | justified  (default none)
338  * @cfg {String} direction up | down (default down)
339  * @cfg {Boolean} toolbar false | true
340  * @cfg {Boolean} btn true | false
341  * 
342  * 
343  * @constructor
344  * Create a new Input
345  * @param {Object} config The config object
346  */
347
348 Roo.bootstrap.ButtonGroup = function(config){
349     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
350 };
351
352 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
353     
354     size: '',
355     align: '',
356     direction: '',
357     toolbar: false,
358     btn: true,
359
360     getAutoCreate : function(){
361         var cfg = {
362             cls: 'btn-group',
363             html : null
364         }
365         
366         cfg.html = this.html || cfg.html;
367         
368         if (this.toolbar) {
369             cfg = {
370                 cls: 'btn-toolbar',
371                 html: null
372             }
373             
374             return cfg;
375         }
376         
377         if (['vertical','justified'].indexOf(this.align)!==-1) {
378             cfg.cls = 'btn-group-' + this.align;
379             
380             if (this.align == 'justified') {
381                 console.log(this.items);
382             }
383         }
384         
385         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
386             cfg.cls += ' btn-group-' + this.size;
387         }
388         
389         if (this.direction == 'up') {
390             cfg.cls += ' dropup' ;
391         }
392         
393         return cfg;
394     }
395    
396 });
397
398  /*
399  * - LGPL
400  *
401  * button
402  * 
403  */
404
405 /**
406  * @class Roo.bootstrap.Button
407  * @extends Roo.bootstrap.Component
408  * Bootstrap Button class
409  * @cfg {String} html The button content
410  * @cfg {String} weight default (or empty) | primary | success | info | warning | danger | link
411  * @cfg {String} size empty | lg | sm | xs
412  * @cfg {String} tag empty | a | input | submit
413  * @cfg {String} href empty or href
414  * @cfg {Boolean} disabled false | true
415  * @cfg {Boolean} isClose false | true
416  * @cfg {String} glyphicon empty | adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out
417  * @cfg {String} badge text for badge
418  * @cfg {String} theme default (or empty) | glow
419  * @cfg {Boolean} inverse false | true
420  * @cfg {Boolean} toggle false | true
421  * @cfg {String} ontext text for on toggle state
422  * @cfg {String} offtext text for off toggle state
423  * @cfg {Boolean} defaulton true | false
424  * @cfg {Boolean} preventDefault (true | false) default true
425  * @cfg {Boolean} removeClass true | false remove the standard class..
426  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
427  * 
428  * @constructor
429  * Create a new button
430  * @param {Object} config The config object
431  */
432
433
434 Roo.bootstrap.Button = function(config){
435     Roo.bootstrap.Button.superclass.constructor.call(this, config);
436     this.addEvents({
437         // raw events
438         /**
439          * @event click
440          * When a butotn is pressed
441          * @param {Roo.EventObject} e
442          */
443         "click" : true,
444          /**
445          * @event toggle
446          * After the button has been toggles
447          * @param {Roo.EventObject} e
448          * @param {boolean} pressed (also available as button.pressed)
449          */
450         "toggle" : true
451     });
452 };
453
454 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
455     html: false,
456     active: false,
457     weight: '',
458     size: '',
459     tag: 'button',
460     href: '',
461     disabled: false,
462     isClose: false,
463     glyphicon: '',
464     badge: '',
465     theme: 'default',
466     inverse: false,
467     
468     toggle: false,
469     ontext: 'ON',
470     offtext: 'OFF',
471     defaulton: true,
472     preventDefault: true,
473     removeClass: false,
474     name: false,
475     target: false,
476     
477     
478     pressed : null,
479      
480     
481     getAutoCreate : function(){
482         
483         var cfg = {
484             tag : 'button',
485             cls : 'roo-button',
486             html: ''
487         };
488         
489         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
490             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
491             this.tag = 'button';
492         } else {
493             cfg.tag = this.tag;
494         }
495         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
496         
497         if (this.toggle == true) {
498             cfg={
499                 tag: 'div',
500                 cls: 'slider-frame roo-button',
501                 cn: [
502                     {
503                         tag: 'span',
504                         'data-on-text':'ON',
505                         'data-off-text':'OFF',
506                         cls: 'slider-button',
507                         html: this.offtext
508                     }
509                 ]
510             };
511             
512             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
513                 cfg.cls += ' '+this.weight;
514             }
515             
516             return cfg;
517         }
518         
519         if (this.isClose) {
520             cfg.cls += ' close';
521             
522             cfg["aria-hidden"] = true;
523             
524             cfg.html = "&times;";
525             
526             return cfg;
527         }
528         
529          
530         if (this.theme==='default') {
531             cfg.cls = 'btn roo-button';
532             
533             //if (this.parentType != 'Navbar') {
534             this.weight = this.weight.length ?  this.weight : 'default';
535             //}
536             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
537                 
538                 cfg.cls += ' btn-' + this.weight;
539             }
540         } else if (this.theme==='glow') {
541             
542             cfg.tag = 'a';
543             cfg.cls = 'btn-glow roo-button';
544             
545             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
546                 
547                 cfg.cls += ' ' + this.weight;
548             }
549         }
550    
551         
552         if (this.inverse) {
553             this.cls += ' inverse';
554         }
555         
556         
557         if (this.active) {
558             cfg.cls += ' active';
559         }
560         
561         if (this.disabled) {
562             cfg.disabled = 'disabled';
563         }
564         
565         if (this.items) {
566             Roo.log('changing to ul' );
567             cfg.tag = 'ul';
568             this.glyphicon = 'caret';
569         }
570         
571         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
572          
573         //gsRoo.log(this.parentType);
574         if (this.parentType === 'Navbar' && !this.parent().bar) {
575             Roo.log('changing to li?');
576             
577             cfg.tag = 'li';
578             
579             cfg.cls = '';
580             cfg.cn =  [{
581                 tag : 'a',
582                 cls : 'roo-button',
583                 html : this.html,
584                 href : this.href || '#'
585             }];
586             if (this.menu) {
587                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
588                 cfg.cls += ' dropdown';
589             }   
590             
591             delete cfg.html;
592             
593         }
594         
595        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
596         
597         if (this.glyphicon) {
598             cfg.html = ' ' + cfg.html;
599             
600             cfg.cn = [
601                 {
602                     tag: 'span',
603                     cls: 'glyphicon glyphicon-' + this.glyphicon
604                 }
605             ];
606         }
607         
608         if (this.badge) {
609             cfg.html += ' ';
610             
611             cfg.tag = 'a';
612             
613 //            cfg.cls='btn roo-button';
614             
615             cfg.href=this.href;
616             
617             var value = cfg.html;
618             
619             if(this.glyphicon){
620                 value = {
621                             tag: 'span',
622                             cls: 'glyphicon glyphicon-' + this.glyphicon,
623                             html: this.html
624                         };
625                 
626             }
627             
628             cfg.cn = [
629                 value,
630                 {
631                     tag: 'span',
632                     cls: 'badge',
633                     html: this.badge
634                 }
635             ];
636             
637             cfg.html='';
638         }
639         
640         if (this.menu) {
641             cfg.cls += ' dropdown';
642             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
643         }
644         
645         if (cfg.tag !== 'a' && this.href !== '') {
646             throw "Tag must be a to set href.";
647         } else if (this.href.length > 0) {
648             cfg.href = this.href;
649         }
650         
651         if(this.removeClass){
652             cfg.cls = '';
653         }
654         
655         if(this.target){
656             cfg.target = this.target;
657         }
658         
659         return cfg;
660     },
661     initEvents: function() {
662        // Roo.log('init events?');
663 //        Roo.log(this.el.dom);
664         // add the menu...
665         
666         if (typeof (this.menu) != 'undefined') {
667             this.menu.parentType = this.xtype;
668             this.menu.triggerEl = this.el;
669             this.addxtype(Roo.apply({}, this.menu));
670         }
671
672
673        if (this.el.hasClass('roo-button')) {
674             this.el.on('click', this.onClick, this);
675        } else {
676             this.el.select('.roo-button').on('click', this.onClick, this);
677        }
678        
679        if(this.removeClass){
680            this.el.on('click', this.onClick, this);
681        }
682        
683        this.el.enableDisplayMode();
684         
685     },
686     onClick : function(e)
687     {
688         if (this.disabled) {
689             return;
690         }
691         
692         Roo.log('button on click ');
693         if(this.preventDefault){
694             e.preventDefault();
695         }
696         if (this.pressed === true || this.pressed === false) {
697             this.pressed = !this.pressed;
698             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
699             this.fireEvent('toggle', this, e, this.pressed);
700         }
701         
702         
703         this.fireEvent('click', this, e);
704     },
705     
706     /**
707      * Enables this button
708      */
709     enable : function()
710     {
711         this.disabled = false;
712         this.el.removeClass('disabled');
713     },
714     
715     /**
716      * Disable this button
717      */
718     disable : function()
719     {
720         this.disabled = true;
721         this.el.addClass('disabled');
722     },
723      /**
724      * sets the active state on/off, 
725      * @param {Boolean} state (optional) Force a particular state
726      */
727     setActive : function(v) {
728         
729         this.el[v ? 'addClass' : 'removeClass']('active');
730     },
731      /**
732      * toggles the current active state 
733      */
734     toggleActive : function()
735     {
736        var active = this.el.hasClass('active');
737        this.setActive(!active);
738        
739         
740     },
741     setText : function(str)
742     {
743         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
744     },
745     getText : function()
746     {
747         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
748     },
749     hide: function() {
750        
751      
752         this.el.hide();   
753     },
754     show: function() {
755        
756         this.el.show();   
757     }
758     
759     
760 });
761
762  /*
763  * - LGPL
764  *
765  * column
766  * 
767  */
768
769 /**
770  * @class Roo.bootstrap.Column
771  * @extends Roo.bootstrap.Component
772  * Bootstrap Column class
773  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
774  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
775  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
776  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
777  * @cfg {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             Roo.log( size + ':' + settings[size]);
815             if (settings[size] === false) {
816                 return;
817             }
818             Roo.log(settings[size]);
819             if (!settings[size]) { // 0 = hidden
820                 cfg.cls += ' hidden-' + size;
821                 return;
822             }
823             cfg.cls += ' col-' + size + '-' + settings[size];
824             
825         });
826         
827         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
828             cfg.cls +=' alert alert-' + this.alert;
829         }
830         
831         
832         if (this.html.length) {
833             cfg.html = this.html;
834         }
835         if (this.fa) {
836             cfg.html = '<i class="fa fa-'+this.fa + '"></i>' + (cfg.html || '');
837         }
838         if (this.icon) {
839             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + + (cfg.html || '')
840         }
841         
842         return cfg;
843     }
844    
845 });
846
847  
848
849  /*
850  * - LGPL
851  *
852  * page container.
853  * 
854  */
855
856
857 /**
858  * @class Roo.bootstrap.Container
859  * @extends Roo.bootstrap.Component
860  * Bootstrap Container class
861  * @cfg {Boolean} jumbotron is it a jumbotron element
862  * @cfg {String} html content of element
863  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
864  * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
865  * @cfg {String} header content of header (for panel)
866  * @cfg {String} footer content of footer (for panel)
867  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
868  * @cfg {String} tag (header|aside|section) type of HTML tag.
869  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
870  * @cfg {String} fa (ban|check|...) font awesome icon
871  * @cfg {String} icon (info-sign|check|...) glyphicon name
872
873  *     
874  * @constructor
875  * Create a new Container
876  * @param {Object} config The config object
877  */
878
879 Roo.bootstrap.Container = function(config){
880     Roo.bootstrap.Container.superclass.constructor.call(this, config);
881 };
882
883 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
884     
885     jumbotron : false,
886     well: '',
887     panel : '',
888     header: '',
889     footer : '',
890     sticky: '',
891     tag : false,
892     alert : false,
893     fa: false,
894     icon : false,
895   
896      
897     getChildContainer : function() {
898         
899         if(!this.el){
900             return false;
901         }
902         
903         if (this.panel.length) {
904             return this.el.select('.panel-body',true).first();
905         }
906         
907         return this.el;
908     },
909     
910     
911     getAutoCreate : function(){
912         
913         var cfg = {
914             tag : this.tag || 'div',
915             html : '',
916             cls : ''
917         };
918         if (this.jumbotron) {
919             cfg.cls = 'jumbotron';
920         }
921         
922         
923         
924         // - this is applied by the parent..
925         //if (this.cls) {
926         //    cfg.cls = this.cls + '';
927         //}
928         
929         if (this.sticky.length) {
930             
931             var bd = Roo.get(document.body);
932             if (!bd.hasClass('bootstrap-sticky')) {
933                 bd.addClass('bootstrap-sticky');
934                 Roo.select('html',true).setStyle('height', '100%');
935             }
936              
937             cfg.cls += 'bootstrap-sticky-' + this.sticky;
938         }
939         
940         
941         if (this.well.length) {
942             switch (this.well) {
943                 case 'lg':
944                 case 'sm':
945                     cfg.cls +=' well well-' +this.well;
946                     break;
947                 default:
948                     cfg.cls +=' well';
949                     break;
950             }
951         }
952         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
953             cfg.cls +=' alert alert-' + this.alert;
954         }
955         
956         var body = cfg;
957         
958         if (this.panel.length) {
959             cfg.cls += ' panel panel-' + this.panel;
960             cfg.cn = [];
961             if (this.header.length) {
962                 cfg.cn.push({
963                     
964                     cls : 'panel-heading',
965                     cn : [{
966                         tag: 'h3',
967                         cls : 'panel-title',
968                         html : this.header
969                     }]
970                     
971                 });
972             }
973             body = false;
974             cfg.cn.push({
975                 cls : 'panel-body',
976                 html : this.html
977             });
978             
979             
980             if (this.footer.length) {
981                 cfg.cn.push({
982                     cls : 'panel-footer',
983                     html : this.footer
984                     
985                 });
986             }
987             
988         }
989         
990         if (body) {
991             body.html = this.html || cfg.html;
992             // prefix with the icons..
993             if (this.fa) {
994                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
995             }
996             if (this.icon) {
997                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
998             }
999             
1000             
1001         }
1002         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1003             cfg.cls =  'container';
1004         }
1005         
1006         return cfg;
1007     },
1008     
1009     titleEl : function()
1010     {
1011         if(!this.el || !this.panel.length || !this.header.length){
1012             return;
1013         }
1014         
1015         return this.el.select('.panel-title',true).first();
1016     },
1017     
1018     setTitle : function(v)
1019     {
1020         var titleEl = this.titleEl();
1021         
1022         if(!titleEl){
1023             return;
1024         }
1025         
1026         titleEl.dom.innerHTML = v;
1027     },
1028     
1029     getTitle : function()
1030     {
1031         
1032         var titleEl = this.titleEl();
1033         
1034         if(!titleEl){
1035             return '';
1036         }
1037         
1038         return titleEl.dom.innerHTML;
1039     }
1040    
1041 });
1042
1043  /*
1044  * - LGPL
1045  *
1046  * image
1047  * 
1048  */
1049
1050
1051 /**
1052  * @class Roo.bootstrap.Img
1053  * @extends Roo.bootstrap.Component
1054  * Bootstrap Img class
1055  * @cfg {Boolean} imgResponsive false | true
1056  * @cfg {String} border rounded | circle | thumbnail
1057  * @cfg {String} src image source
1058  * @cfg {String} alt image alternative text
1059  * @cfg {String} href a tag href
1060  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1061  * 
1062  * @constructor
1063  * Create a new Input
1064  * @param {Object} config The config object
1065  */
1066
1067 Roo.bootstrap.Img = function(config){
1068     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1069     
1070     this.addEvents({
1071         // img events
1072         /**
1073          * @event click
1074          * The img click event for the img.
1075          * @param {Roo.EventObject} e
1076          */
1077         "click" : true
1078     });
1079 };
1080
1081 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1082     
1083     imgResponsive: true,
1084     border: '',
1085     src: '',
1086     href: false,
1087     target: false,
1088
1089     getAutoCreate : function(){
1090         
1091         var cfg = {
1092             tag: 'img',
1093             cls: (this.imgResponsive) ? 'img-responsive' : '',
1094             html : null
1095         }
1096         
1097         cfg.html = this.html || cfg.html;
1098         
1099         cfg.src = this.src || cfg.src;
1100         
1101         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1102             cfg.cls += ' img-' + this.border;
1103         }
1104         
1105         if(this.alt){
1106             cfg.alt = this.alt;
1107         }
1108         
1109         if(this.href){
1110             var a = {
1111                 tag: 'a',
1112                 href: this.href,
1113                 cn: [
1114                     cfg
1115                 ]
1116             }
1117             
1118             if(this.target){
1119                 a.target = this.target;
1120             }
1121             
1122         }
1123         
1124         
1125         return (this.href) ? a : cfg;
1126     },
1127     
1128     initEvents: function() {
1129         
1130         if(!this.href){
1131             this.el.on('click', this.onClick, this);
1132         }
1133     },
1134     
1135     onClick : function(e)
1136     {
1137         Roo.log('img onclick');
1138         this.fireEvent('click', this, e);
1139     }
1140    
1141 });
1142
1143  /*
1144  * - LGPL
1145  *
1146  * image
1147  * 
1148  */
1149
1150
1151 /**
1152  * @class Roo.bootstrap.Link
1153  * @extends Roo.bootstrap.Component
1154  * Bootstrap Link Class
1155  * @cfg {String} alt image alternative text
1156  * @cfg {String} href a tag href
1157  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1158  * @cfg {String} html the content of the link.
1159  * @cfg {Boolean} preventDefault (true | false) default false
1160
1161  * 
1162  * @constructor
1163  * Create a new Input
1164  * @param {Object} config The config object
1165  */
1166
1167 Roo.bootstrap.Link = function(config){
1168     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1169     
1170     this.addEvents({
1171         // img events
1172         /**
1173          * @event click
1174          * The img click event for the img.
1175          * @param {Roo.EventObject} e
1176          */
1177         "click" : true
1178     });
1179 };
1180
1181 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1182     
1183     href: false,
1184     target: false,
1185     preventDefault: false,
1186
1187     getAutoCreate : function(){
1188         
1189         var cfg = {
1190             tag: 'a',
1191             html : this.html || 'html-missing'
1192         }
1193         
1194         
1195         if(this.alt){
1196             cfg.alt = this.alt;
1197         }
1198         cfg.href = this.href || '#';
1199         if(this.target){
1200             cfg.target = this.target;
1201         }
1202         
1203         return cfg;
1204     },
1205     
1206     initEvents: function() {
1207         
1208         if(!this.href || this.preventDefault){
1209             this.el.on('click', this.onClick, this);
1210         }
1211     },
1212     
1213     onClick : function(e)
1214     {
1215         if(this.preventDefault){
1216             e.preventDefault();
1217         }
1218         //Roo.log('img onclick');
1219         this.fireEvent('click', this, e);
1220     }
1221    
1222 });
1223
1224  /*
1225  * - LGPL
1226  *
1227  * header
1228  * 
1229  */
1230
1231 /**
1232  * @class Roo.bootstrap.Header
1233  * @extends Roo.bootstrap.Component
1234  * Bootstrap Header class
1235  * @cfg {String} html content of header
1236  * @cfg {Number} level (1|2|3|4|5|6) default 1
1237  * 
1238  * @constructor
1239  * Create a new Header
1240  * @param {Object} config The config object
1241  */
1242
1243
1244 Roo.bootstrap.Header  = function(config){
1245     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1246 };
1247
1248 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1249     
1250     //href : false,
1251     html : false,
1252     level : 1,
1253     
1254     
1255     
1256     getAutoCreate : function(){
1257         
1258         var cfg = {
1259             tag: 'h' + (1 *this.level),
1260             html: this.html || 'fill in html'
1261         } ;
1262         
1263         return cfg;
1264     }
1265    
1266 });
1267
1268  
1269
1270  /*
1271  * Based on:
1272  * Ext JS Library 1.1.1
1273  * Copyright(c) 2006-2007, Ext JS, LLC.
1274  *
1275  * Originally Released Under LGPL - original licence link has changed is not relivant.
1276  *
1277  * Fork - LGPL
1278  * <script type="text/javascript">
1279  */
1280  
1281 /**
1282  * @class Roo.bootstrap.MenuMgr
1283  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1284  * @singleton
1285  */
1286 Roo.bootstrap.MenuMgr = function(){
1287    var menus, active, groups = {}, attached = false, lastShow = new Date();
1288
1289    // private - called when first menu is created
1290    function init(){
1291        menus = {};
1292        active = new Roo.util.MixedCollection();
1293        Roo.get(document).addKeyListener(27, function(){
1294            if(active.length > 0){
1295                hideAll();
1296            }
1297        });
1298    }
1299
1300    // private
1301    function hideAll(){
1302        if(active && active.length > 0){
1303            var c = active.clone();
1304            c.each(function(m){
1305                m.hide();
1306            });
1307        }
1308    }
1309
1310    // private
1311    function onHide(m){
1312        active.remove(m);
1313        if(active.length < 1){
1314            Roo.get(document).un("mouseup", onMouseDown);
1315             
1316            attached = false;
1317        }
1318    }
1319
1320    // private
1321    function onShow(m){
1322        var last = active.last();
1323        lastShow = new Date();
1324        active.add(m);
1325        if(!attached){
1326           Roo.get(document).on("mouseup", onMouseDown);
1327            
1328            attached = true;
1329        }
1330        if(m.parentMenu){
1331           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1332           m.parentMenu.activeChild = m;
1333        }else if(last && last.isVisible()){
1334           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1335        }
1336    }
1337
1338    // private
1339    function onBeforeHide(m){
1340        if(m.activeChild){
1341            m.activeChild.hide();
1342        }
1343        if(m.autoHideTimer){
1344            clearTimeout(m.autoHideTimer);
1345            delete m.autoHideTimer;
1346        }
1347    }
1348
1349    // private
1350    function onBeforeShow(m){
1351        var pm = m.parentMenu;
1352        if(!pm && !m.allowOtherMenus){
1353            hideAll();
1354        }else if(pm && pm.activeChild && active != m){
1355            pm.activeChild.hide();
1356        }
1357    }
1358
1359    // private
1360    function onMouseDown(e){
1361         Roo.log("on MouseDown");
1362         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
1363            hideAll();
1364         }
1365         
1366         
1367    }
1368
1369    // private
1370    function onBeforeCheck(mi, state){
1371        if(state){
1372            var g = groups[mi.group];
1373            for(var i = 0, l = g.length; i < l; i++){
1374                if(g[i] != mi){
1375                    g[i].setChecked(false);
1376                }
1377            }
1378        }
1379    }
1380
1381    return {
1382
1383        /**
1384         * Hides all menus that are currently visible
1385         */
1386        hideAll : function(){
1387             hideAll();  
1388        },
1389
1390        // private
1391        register : function(menu){
1392            if(!menus){
1393                init();
1394            }
1395            menus[menu.id] = menu;
1396            menu.on("beforehide", onBeforeHide);
1397            menu.on("hide", onHide);
1398            menu.on("beforeshow", onBeforeShow);
1399            menu.on("show", onShow);
1400            var g = menu.group;
1401            if(g && menu.events["checkchange"]){
1402                if(!groups[g]){
1403                    groups[g] = [];
1404                }
1405                groups[g].push(menu);
1406                menu.on("checkchange", onCheck);
1407            }
1408        },
1409
1410         /**
1411          * Returns a {@link Roo.menu.Menu} object
1412          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1413          * be used to generate and return a new Menu instance.
1414          */
1415        get : function(menu){
1416            if(typeof menu == "string"){ // menu id
1417                return menus[menu];
1418            }else if(menu.events){  // menu instance
1419                return menu;
1420            }
1421            /*else if(typeof menu.length == 'number'){ // array of menu items?
1422                return new Roo.bootstrap.Menu({items:menu});
1423            }else{ // otherwise, must be a config
1424                return new Roo.bootstrap.Menu(menu);
1425            }
1426            */
1427            return false;
1428        },
1429
1430        // private
1431        unregister : function(menu){
1432            delete menus[menu.id];
1433            menu.un("beforehide", onBeforeHide);
1434            menu.un("hide", onHide);
1435            menu.un("beforeshow", onBeforeShow);
1436            menu.un("show", onShow);
1437            var g = menu.group;
1438            if(g && menu.events["checkchange"]){
1439                groups[g].remove(menu);
1440                menu.un("checkchange", onCheck);
1441            }
1442        },
1443
1444        // private
1445        registerCheckable : function(menuItem){
1446            var g = menuItem.group;
1447            if(g){
1448                if(!groups[g]){
1449                    groups[g] = [];
1450                }
1451                groups[g].push(menuItem);
1452                menuItem.on("beforecheckchange", onBeforeCheck);
1453            }
1454        },
1455
1456        // private
1457        unregisterCheckable : function(menuItem){
1458            var g = menuItem.group;
1459            if(g){
1460                groups[g].remove(menuItem);
1461                menuItem.un("beforecheckchange", onBeforeCheck);
1462            }
1463        }
1464    };
1465 }();/*
1466  * - LGPL
1467  *
1468  * menu
1469  * 
1470  */
1471
1472 /**
1473  * @class Roo.bootstrap.Menu
1474  * @extends Roo.bootstrap.Component
1475  * Bootstrap Menu class - container for MenuItems
1476  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1477  * 
1478  * @constructor
1479  * Create a new Menu
1480  * @param {Object} config The config object
1481  */
1482
1483
1484 Roo.bootstrap.Menu = function(config){
1485     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1486     if (this.registerMenu) {
1487         Roo.bootstrap.MenuMgr.register(this);
1488     }
1489     this.addEvents({
1490         /**
1491          * @event beforeshow
1492          * Fires before this menu is displayed
1493          * @param {Roo.menu.Menu} this
1494          */
1495         beforeshow : true,
1496         /**
1497          * @event beforehide
1498          * Fires before this menu is hidden
1499          * @param {Roo.menu.Menu} this
1500          */
1501         beforehide : true,
1502         /**
1503          * @event show
1504          * Fires after this menu is displayed
1505          * @param {Roo.menu.Menu} this
1506          */
1507         show : true,
1508         /**
1509          * @event hide
1510          * Fires after this menu is hidden
1511          * @param {Roo.menu.Menu} this
1512          */
1513         hide : true,
1514         /**
1515          * @event click
1516          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1517          * @param {Roo.menu.Menu} this
1518          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1519          * @param {Roo.EventObject} e
1520          */
1521         click : true,
1522         /**
1523          * @event mouseover
1524          * Fires when the mouse is hovering over this menu
1525          * @param {Roo.menu.Menu} this
1526          * @param {Roo.EventObject} e
1527          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1528          */
1529         mouseover : true,
1530         /**
1531          * @event mouseout
1532          * Fires when the mouse exits this menu
1533          * @param {Roo.menu.Menu} this
1534          * @param {Roo.EventObject} e
1535          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1536          */
1537         mouseout : true,
1538         /**
1539          * @event itemclick
1540          * Fires when a menu item contained in this menu is clicked
1541          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1542          * @param {Roo.EventObject} e
1543          */
1544         itemclick: true
1545     });
1546     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1547 };
1548
1549 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1550     
1551    /// html : false,
1552     //align : '',
1553     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1554     type: false,
1555     /**
1556      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1557      */
1558     registerMenu : true,
1559     
1560     menuItems :false, // stores the menu items..
1561     
1562     hidden:true,
1563     
1564     parentMenu : false,
1565     
1566     getChildContainer : function() {
1567         return this.el;  
1568     },
1569     
1570     getAutoCreate : function(){
1571          
1572         //if (['right'].indexOf(this.align)!==-1) {
1573         //    cfg.cn[1].cls += ' pull-right'
1574         //}
1575         
1576         
1577         var cfg = {
1578             tag : 'ul',
1579             cls : 'dropdown-menu' ,
1580             style : 'z-index:1000'
1581             
1582         }
1583         
1584         if (this.type === 'submenu') {
1585             cfg.cls = 'submenu active';
1586         }
1587         if (this.type === 'treeview') {
1588             cfg.cls = 'treeview-menu';
1589         }
1590         
1591         return cfg;
1592     },
1593     initEvents : function() {
1594         
1595        // Roo.log("ADD event");
1596        // Roo.log(this.triggerEl.dom);
1597         this.triggerEl.on('click', this.onTriggerPress, this);
1598         this.triggerEl.addClass('dropdown-toggle');
1599         this.el.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
1600
1601         this.el.on("mouseover", this.onMouseOver, this);
1602         this.el.on("mouseout", this.onMouseOut, this);
1603         
1604         
1605     },
1606     findTargetItem : function(e){
1607         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
1608         if(!t){
1609             return false;
1610         }
1611         //Roo.log(t);         Roo.log(t.id);
1612         if(t && t.id){
1613             //Roo.log(this.menuitems);
1614             return this.menuitems.get(t.id);
1615             
1616             //return this.items.get(t.menuItemId);
1617         }
1618         
1619         return false;
1620     },
1621     onClick : function(e){
1622         Roo.log("menu.onClick");
1623         var t = this.findTargetItem(e);
1624         if(!t){
1625             return;
1626         }
1627         Roo.log(e);
1628         /*
1629         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
1630             if(t == this.activeItem && t.shouldDeactivate(e)){
1631                 this.activeItem.deactivate();
1632                 delete this.activeItem;
1633                 return;
1634             }
1635             if(t.canActivate){
1636                 this.setActiveItem(t, true);
1637             }
1638             return;
1639             
1640             
1641         }
1642         */
1643         Roo.log('pass click event');
1644         
1645         t.onClick(e);
1646         
1647         this.fireEvent("click", this, t, e);
1648         
1649         this.hide();
1650     },
1651      onMouseOver : function(e){
1652         var t  = this.findTargetItem(e);
1653         //Roo.log(t);
1654         //if(t){
1655         //    if(t.canActivate && !t.disabled){
1656         //        this.setActiveItem(t, true);
1657         //    }
1658         //}
1659         
1660         this.fireEvent("mouseover", this, e, t);
1661     },
1662     isVisible : function(){
1663         return !this.hidden;
1664     },
1665      onMouseOut : function(e){
1666         var t  = this.findTargetItem(e);
1667         
1668         //if(t ){
1669         //    if(t == this.activeItem && t.shouldDeactivate(e)){
1670         //        this.activeItem.deactivate();
1671         //        delete this.activeItem;
1672         //    }
1673         //}
1674         this.fireEvent("mouseout", this, e, t);
1675     },
1676     
1677     
1678     /**
1679      * Displays this menu relative to another element
1680      * @param {String/HTMLElement/Roo.Element} element The element to align to
1681      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
1682      * the element (defaults to this.defaultAlign)
1683      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1684      */
1685     show : function(el, pos, parentMenu){
1686         this.parentMenu = parentMenu;
1687         if(!this.el){
1688             this.render();
1689         }
1690         this.fireEvent("beforeshow", this);
1691         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
1692     },
1693      /**
1694      * Displays this menu at a specific xy position
1695      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
1696      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1697      */
1698     showAt : function(xy, parentMenu, /* private: */_e){
1699         this.parentMenu = parentMenu;
1700         if(!this.el){
1701             this.render();
1702         }
1703         if(_e !== false){
1704             this.fireEvent("beforeshow", this);
1705             
1706             //xy = this.el.adjustForConstraints(xy);
1707         }
1708         //this.el.setXY(xy);
1709         //this.el.show();
1710         this.hideMenuItems();
1711         this.hidden = false;
1712         this.triggerEl.addClass('open');
1713         this.focus();
1714         this.fireEvent("show", this);
1715     },
1716     
1717     focus : function(){
1718         return;
1719         if(!this.hidden){
1720             this.doFocus.defer(50, this);
1721         }
1722     },
1723
1724     doFocus : function(){
1725         if(!this.hidden){
1726             this.focusEl.focus();
1727         }
1728     },
1729
1730     /**
1731      * Hides this menu and optionally all parent menus
1732      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
1733      */
1734     hide : function(deep){
1735         
1736         this.hideMenuItems();
1737         if(this.el && this.isVisible()){
1738             this.fireEvent("beforehide", this);
1739             if(this.activeItem){
1740                 this.activeItem.deactivate();
1741                 this.activeItem = null;
1742             }
1743             this.triggerEl.removeClass('open');;
1744             this.hidden = true;
1745             this.fireEvent("hide", this);
1746         }
1747         if(deep === true && this.parentMenu){
1748             this.parentMenu.hide(true);
1749         }
1750     },
1751     
1752     onTriggerPress  : function(e)
1753     {
1754         
1755         Roo.log('trigger press');
1756         //Roo.log(e.getTarget());
1757        // Roo.log(this.triggerEl.dom);
1758         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
1759             return;
1760         }
1761         if (this.isVisible()) {
1762             Roo.log('hide');
1763             this.hide();
1764         } else {
1765             this.show(this.triggerEl, false, false);
1766         }
1767         
1768         
1769     },
1770     
1771          
1772        
1773     
1774     hideMenuItems : function()
1775     {
1776         //$(backdrop).remove()
1777         Roo.select('.open',true).each(function(aa) {
1778             
1779             aa.removeClass('open');
1780           //var parent = getParent($(this))
1781           //var relatedTarget = { relatedTarget: this }
1782           
1783            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
1784           //if (e.isDefaultPrevented()) return
1785            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
1786         })
1787     },
1788     addxtypeChild : function (tree, cntr) {
1789         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
1790           
1791         this.menuitems.add(comp);
1792         return comp;
1793
1794     },
1795     getEl : function()
1796     {
1797         Roo.log(this.el);
1798         return this.el;
1799     }
1800 });
1801
1802  
1803  /*
1804  * - LGPL
1805  *
1806  * menu item
1807  * 
1808  */
1809
1810
1811 /**
1812  * @class Roo.bootstrap.MenuItem
1813  * @extends Roo.bootstrap.Component
1814  * Bootstrap MenuItem class
1815  * @cfg {String} html the menu label
1816  * @cfg {String} href the link
1817  * @cfg {Boolean} preventDefault (true | false) default true
1818  * 
1819  * 
1820  * @constructor
1821  * Create a new MenuItem
1822  * @param {Object} config The config object
1823  */
1824
1825
1826 Roo.bootstrap.MenuItem = function(config){
1827     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
1828     this.addEvents({
1829         // raw events
1830         /**
1831          * @event click
1832          * The raw click event for the entire grid.
1833          * @param {Roo.EventObject} e
1834          */
1835         "click" : true
1836     });
1837 };
1838
1839 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
1840     
1841     href : false,
1842     html : false,
1843     preventDefault: true,
1844     
1845     getAutoCreate : function(){
1846         var cfg= {
1847             tag: 'li',
1848             cls: 'dropdown-menu-item',
1849             cn: [
1850                     {
1851                         tag : 'a',
1852                         href : '#',
1853                         html : 'Link'
1854                     }
1855                 ]
1856         };
1857         if (this.parent().type == 'treeview') {
1858             cfg.cls = 'treeview-menu';
1859         }
1860         
1861         cfg.cn[0].href = this.href || cfg.cn[0].href ;
1862         cfg.cn[0].html = this.html || cfg.cn[0].html ;
1863         return cfg;
1864     },
1865     
1866     initEvents: function() {
1867         
1868         //this.el.select('a').on('click', this.onClick, this);
1869         
1870     },
1871     onClick : function(e)
1872     {
1873         Roo.log('item on click ');
1874         //if(this.preventDefault){
1875         //    e.preventDefault();
1876         //}
1877         //this.parent().hideMenuItems();
1878         
1879         this.fireEvent('click', this, e);
1880     },
1881     getEl : function()
1882     {
1883         return this.el;
1884     }
1885 });
1886
1887  
1888
1889  /*
1890  * - LGPL
1891  *
1892  * menu separator
1893  * 
1894  */
1895
1896
1897 /**
1898  * @class Roo.bootstrap.MenuSeparator
1899  * @extends Roo.bootstrap.Component
1900  * Bootstrap MenuSeparator class
1901  * 
1902  * @constructor
1903  * Create a new MenuItem
1904  * @param {Object} config The config object
1905  */
1906
1907
1908 Roo.bootstrap.MenuSeparator = function(config){
1909     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
1910 };
1911
1912 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
1913     
1914     getAutoCreate : function(){
1915         var cfg = {
1916             cls: 'divider',
1917             tag : 'li'
1918         };
1919         
1920         return cfg;
1921     }
1922    
1923 });
1924
1925  
1926
1927  
1928 /*
1929 <div class="modal fade">
1930   <div class="modal-dialog">
1931     <div class="modal-content">
1932       <div class="modal-header">
1933         <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
1934         <h4 class="modal-title">Modal title</h4>
1935       </div>
1936       <div class="modal-body">
1937         <p>One fine body&hellip;</p>
1938       </div>
1939       <div class="modal-footer">
1940         <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
1941         <button type="button" class="btn btn-primary">Save changes</button>
1942       </div>
1943     </div><!-- /.modal-content -->
1944   </div><!-- /.modal-dialog -->
1945 </div><!-- /.modal -->
1946 */
1947 /*
1948  * - LGPL
1949  *
1950  * page contgainer.
1951  * 
1952  */
1953
1954 /**
1955  * @class Roo.bootstrap.Modal
1956  * @extends Roo.bootstrap.Component
1957  * Bootstrap Modal class
1958  * @cfg {String} title Title of dialog
1959  * @cfg {Boolean} specificTitle (true|false) default false
1960  * @cfg {Array} buttons Array of buttons or standard button set..
1961  * @cfg {String} buttonPosition (left|right|center) default right
1962  * 
1963  * @constructor
1964  * Create a new Modal Dialog
1965  * @param {Object} config The config object
1966  */
1967
1968 Roo.bootstrap.Modal = function(config){
1969     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
1970     this.addEvents({
1971         // raw events
1972         /**
1973          * @event btnclick
1974          * The raw btnclick event for the button
1975          * @param {Roo.EventObject} e
1976          */
1977         "btnclick" : true
1978     });
1979     this.buttons = this.buttons || [];
1980 };
1981
1982 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
1983     
1984     title : 'test dialog',
1985    
1986     buttons : false,
1987     
1988     // set on load...
1989     body:  false,
1990     
1991     specificTitle: false,
1992     
1993     buttonPosition: 'right',
1994     
1995     onRender : function(ct, position)
1996     {
1997         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
1998      
1999         if(!this.el){
2000             var cfg = Roo.apply({},  this.getAutoCreate());
2001             cfg.id = Roo.id();
2002             //if(!cfg.name){
2003             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2004             //}
2005             //if (!cfg.name.length) {
2006             //    delete cfg.name;
2007            // }
2008             if (this.cls) {
2009                 cfg.cls += ' ' + this.cls;
2010             }
2011             if (this.style) {
2012                 cfg.style = this.style;
2013             }
2014             this.el = Roo.get(document.body).createChild(cfg, position);
2015         }
2016         //var type = this.el.dom.type;
2017         
2018         if(this.tabIndex !== undefined){
2019             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2020         }
2021         
2022         
2023         
2024         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2025         this.maskEl.enableDisplayMode("block");
2026         this.maskEl.hide();
2027         //this.el.addClass("x-dlg-modal");
2028     
2029         if (this.buttons.length) {
2030             Roo.each(this.buttons, function(bb) {
2031                 b = Roo.apply({}, bb);
2032                 b.xns = b.xns || Roo.bootstrap;
2033                 b.xtype = b.xtype || 'Button';
2034                 if (typeof(b.listeners) == 'undefined') {
2035                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2036                 }
2037                 
2038                 var btn = Roo.factory(b);
2039                 
2040                 btn.onRender(this.el.select('.modal-footer div').first());
2041                 
2042             },this);
2043         }
2044         // render the children.
2045         var nitems = [];
2046         
2047         if(typeof(this.items) != 'undefined'){
2048             var items = this.items;
2049             delete this.items;
2050
2051             for(var i =0;i < items.length;i++) {
2052                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2053             }
2054         }
2055         
2056         this.items = nitems;
2057         
2058         this.body = this.el.select('.modal-body',true).first();
2059         this.close = this.el.select('.modal-header .close', true).first();
2060         this.footer = this.el.select('.modal-footer',true).first();
2061         this.initEvents();
2062         //this.el.addClass([this.fieldClass, this.cls]);
2063         
2064     },
2065     getAutoCreate : function(){
2066         
2067         
2068         var bdy = {
2069                 cls : 'modal-body',
2070                 html : this.html || ''
2071         };
2072         
2073         var title = {
2074             tag: 'h4',
2075             cls : 'modal-title',
2076             html : this.title
2077         };
2078         
2079         if(this.specificTitle){
2080             title = this.title;
2081         };
2082         
2083         return modal = {
2084             cls: "modal fade",
2085             style : 'display: none',
2086             cn : [
2087                 {
2088                     cls: "modal-dialog",
2089                     cn : [
2090                         {
2091                             cls : "modal-content",
2092                             cn : [
2093                                 {
2094                                     cls : 'modal-header',
2095                                     cn : [
2096                                         {
2097                                             tag: 'button',
2098                                             cls : 'close',
2099                                             html : '&times'
2100                                         },
2101                                         title
2102                                     ]
2103                                 },
2104                                 bdy,
2105                                 {
2106                                     cls : 'modal-footer',
2107                                     cn : [
2108                                         {
2109                                             tag: 'div',
2110                                             cls: 'btn-' + this.buttonPosition
2111                                         }
2112                                     ]
2113                                     
2114                                 }
2115                                 
2116                                 
2117                             ]
2118                             
2119                         }
2120                     ]
2121                         
2122                 }
2123             ]
2124             
2125             
2126         };
2127           
2128     },
2129     getChildContainer : function() {
2130          
2131          return this.el.select('.modal-body',true).first();
2132         
2133     },
2134     getButtonContainer : function() {
2135          return this.el.select('.modal-footer div',true).first();
2136         
2137     },
2138     initEvents : function()
2139     {
2140         this.el.select('.modal-header .close').on('click', this.hide, this);
2141 //        
2142 //        this.addxtype(this);
2143     },
2144     show : function() {
2145         
2146         if (!this.rendered) {
2147             this.render();
2148         }
2149        
2150         this.el.addClass('on');
2151         this.el.removeClass('fade');
2152         this.el.setStyle('display', 'block');
2153         Roo.get(document.body).addClass("x-body-masked");
2154         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2155         this.maskEl.show();
2156         this.el.setStyle('zIndex', '10001');
2157         this.fireEvent('show', this);
2158         
2159         
2160     },
2161     hide : function()
2162     {
2163         Roo.log('Modal hide?!');
2164         this.maskEl.hide();
2165         Roo.get(document.body).removeClass("x-body-masked");
2166         this.el.removeClass('on');
2167         this.el.addClass('fade');
2168         this.el.setStyle('display', 'none');
2169         this.fireEvent('hide', this);
2170     },
2171     
2172     addButton : function(str, cb)
2173     {
2174          
2175         
2176         var b = Roo.apply({}, { html : str } );
2177         b.xns = b.xns || Roo.bootstrap;
2178         b.xtype = b.xtype || 'Button';
2179         if (typeof(b.listeners) == 'undefined') {
2180             b.listeners = { click : cb.createDelegate(this)  };
2181         }
2182         
2183         var btn = Roo.factory(b);
2184            
2185         btn.onRender(this.el.select('.modal-footer div').first());
2186         
2187         return btn;   
2188        
2189     },
2190     
2191     setDefaultButton : function(btn)
2192     {
2193         //this.el.select('.modal-footer').()
2194     },
2195     resizeTo: function(w,h)
2196     {
2197         // skip..
2198     },
2199     setContentSize  : function(w, h)
2200     {
2201         
2202     },
2203     onButtonClick: function(btn,e)
2204     {
2205         //Roo.log([a,b,c]);
2206         this.fireEvent('btnclick', btn.name, e);
2207     },
2208     setTitle: function(str) {
2209         this.el.select('.modal-title',true).first().dom.innerHTML = str;
2210         
2211     }
2212 });
2213
2214
2215 Roo.apply(Roo.bootstrap.Modal,  {
2216     /**
2217          * Button config that displays a single OK button
2218          * @type Object
2219          */
2220         OK :  [{
2221             name : 'ok',
2222             weight : 'primary',
2223             html : 'OK'
2224         }], 
2225         /**
2226          * Button config that displays Yes and No buttons
2227          * @type Object
2228          */
2229         YESNO : [
2230             {
2231                 name  : 'no',
2232                 html : 'No'
2233             },
2234             {
2235                 name  :'yes',
2236                 weight : 'primary',
2237                 html : 'Yes'
2238             }
2239         ],
2240         
2241         /**
2242          * Button config that displays OK and Cancel buttons
2243          * @type Object
2244          */
2245         OKCANCEL : [
2246             {
2247                name : 'cancel',
2248                 html : 'Cancel'
2249             },
2250             {
2251                 name : 'ok',
2252                 weight : 'primary',
2253                 html : 'OK'
2254             }
2255         ],
2256         /**
2257          * Button config that displays Yes, No and Cancel buttons
2258          * @type Object
2259          */
2260         YESNOCANCEL : [
2261             {
2262                 name : 'yes',
2263                 weight : 'primary',
2264                 html : 'Yes'
2265             },
2266             {
2267                 name : 'no',
2268                 html : 'No'
2269             },
2270             {
2271                 name : 'cancel',
2272                 html : 'Cancel'
2273             }
2274         ]
2275 });
2276  /*
2277  * - LGPL
2278  *
2279  * messagebox - can be used as a replace
2280  * 
2281  */
2282 /**
2283  * @class Roo.MessageBox
2284  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2285  * Example usage:
2286  *<pre><code>
2287 // Basic alert:
2288 Roo.Msg.alert('Status', 'Changes saved successfully.');
2289
2290 // Prompt for user data:
2291 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2292     if (btn == 'ok'){
2293         // process text value...
2294     }
2295 });
2296
2297 // Show a dialog using config options:
2298 Roo.Msg.show({
2299    title:'Save Changes?',
2300    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2301    buttons: Roo.Msg.YESNOCANCEL,
2302    fn: processResult,
2303    animEl: 'elId'
2304 });
2305 </code></pre>
2306  * @singleton
2307  */
2308 Roo.bootstrap.MessageBox = function(){
2309     var dlg, opt, mask, waitTimer;
2310     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2311     var buttons, activeTextEl, bwidth;
2312
2313     
2314     // private
2315     var handleButton = function(button){
2316         dlg.hide();
2317         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2318     };
2319
2320     // private
2321     var handleHide = function(){
2322         if(opt && opt.cls){
2323             dlg.el.removeClass(opt.cls);
2324         }
2325         //if(waitTimer){
2326         //    Roo.TaskMgr.stop(waitTimer);
2327         //    waitTimer = null;
2328         //}
2329     };
2330
2331     // private
2332     var updateButtons = function(b){
2333         var width = 0;
2334         if(!b){
2335             buttons["ok"].hide();
2336             buttons["cancel"].hide();
2337             buttons["yes"].hide();
2338             buttons["no"].hide();
2339             //dlg.footer.dom.style.display = 'none';
2340             return width;
2341         }
2342         dlg.footer.dom.style.display = '';
2343         for(var k in buttons){
2344             if(typeof buttons[k] != "function"){
2345                 if(b[k]){
2346                     buttons[k].show();
2347                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2348                     width += buttons[k].el.getWidth()+15;
2349                 }else{
2350                     buttons[k].hide();
2351                 }
2352             }
2353         }
2354         return width;
2355     };
2356
2357     // private
2358     var handleEsc = function(d, k, e){
2359         if(opt && opt.closable !== false){
2360             dlg.hide();
2361         }
2362         if(e){
2363             e.stopEvent();
2364         }
2365     };
2366
2367     return {
2368         /**
2369          * Returns a reference to the underlying {@link Roo.BasicDialog} element
2370          * @return {Roo.BasicDialog} The BasicDialog element
2371          */
2372         getDialog : function(){
2373            if(!dlg){
2374                 dlg = new Roo.bootstrap.Modal( {
2375                     //draggable: true,
2376                     //resizable:false,
2377                     //constraintoviewport:false,
2378                     //fixedcenter:true,
2379                     //collapsible : false,
2380                     //shim:true,
2381                     //modal: true,
2382                   //  width:400,
2383                   //  height:100,
2384                     //buttonAlign:"center",
2385                     closeClick : function(){
2386                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2387                             handleButton("no");
2388                         }else{
2389                             handleButton("cancel");
2390                         }
2391                     }
2392                 });
2393                 dlg.render();
2394                 dlg.on("hide", handleHide);
2395                 mask = dlg.mask;
2396                 //dlg.addKeyListener(27, handleEsc);
2397                 buttons = {};
2398                 this.buttons = buttons;
2399                 var bt = this.buttonText;
2400                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2401                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2402                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2403                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2404                 Roo.log(buttons)
2405                 bodyEl = dlg.body.createChild({
2406
2407                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2408                         '<textarea class="roo-mb-textarea"></textarea>' +
2409                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
2410                 });
2411                 msgEl = bodyEl.dom.firstChild;
2412                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2413                 textboxEl.enableDisplayMode();
2414                 textboxEl.addKeyListener([10,13], function(){
2415                     if(dlg.isVisible() && opt && opt.buttons){
2416                         if(opt.buttons.ok){
2417                             handleButton("ok");
2418                         }else if(opt.buttons.yes){
2419                             handleButton("yes");
2420                         }
2421                     }
2422                 });
2423                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2424                 textareaEl.enableDisplayMode();
2425                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2426                 progressEl.enableDisplayMode();
2427                 var pf = progressEl.dom.firstChild;
2428                 if (pf) {
2429                     pp = Roo.get(pf.firstChild);
2430                     pp.setHeight(pf.offsetHeight);
2431                 }
2432                 
2433             }
2434             return dlg;
2435         },
2436
2437         /**
2438          * Updates the message box body text
2439          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2440          * the XHTML-compliant non-breaking space character '&amp;#160;')
2441          * @return {Roo.MessageBox} This message box
2442          */
2443         updateText : function(text){
2444             if(!dlg.isVisible() && !opt.width){
2445                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2446             }
2447             msgEl.innerHTML = text || '&#160;';
2448       
2449             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2450             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2451             var w = Math.max(
2452                     Math.min(opt.width || cw , this.maxWidth), 
2453                     Math.max(opt.minWidth || this.minWidth, bwidth)
2454             );
2455             if(opt.prompt){
2456                 activeTextEl.setWidth(w);
2457             }
2458             if(dlg.isVisible()){
2459                 dlg.fixedcenter = false;
2460             }
2461             // to big, make it scroll. = But as usual stupid IE does not support
2462             // !important..
2463             
2464             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2465                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2466                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2467             } else {
2468                 bodyEl.dom.style.height = '';
2469                 bodyEl.dom.style.overflowY = '';
2470             }
2471             if (cw > w) {
2472                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2473             } else {
2474                 bodyEl.dom.style.overflowX = '';
2475             }
2476             
2477             dlg.setContentSize(w, bodyEl.getHeight());
2478             if(dlg.isVisible()){
2479                 dlg.fixedcenter = true;
2480             }
2481             return this;
2482         },
2483
2484         /**
2485          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
2486          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2487          * @param {Number} value Any number between 0 and 1 (e.g., .5)
2488          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2489          * @return {Roo.MessageBox} This message box
2490          */
2491         updateProgress : function(value, text){
2492             if(text){
2493                 this.updateText(text);
2494             }
2495             if (pp) { // weird bug on my firefox - for some reason this is not defined
2496                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2497             }
2498             return this;
2499         },        
2500
2501         /**
2502          * Returns true if the message box is currently displayed
2503          * @return {Boolean} True if the message box is visible, else false
2504          */
2505         isVisible : function(){
2506             return dlg && dlg.isVisible();  
2507         },
2508
2509         /**
2510          * Hides the message box if it is displayed
2511          */
2512         hide : function(){
2513             if(this.isVisible()){
2514                 dlg.hide();
2515             }  
2516         },
2517
2518         /**
2519          * Displays a new message box, or reinitializes an existing message box, based on the config options
2520          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2521          * The following config object properties are supported:
2522          * <pre>
2523 Property    Type             Description
2524 ----------  ---------------  ------------------------------------------------------------------------------------
2525 animEl            String/Element   An id or Element from which the message box should animate as it opens and
2526                                    closes (defaults to undefined)
2527 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
2528                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
2529 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
2530                                    progress and wait dialogs will ignore this property and always hide the
2531                                    close button as they can only be closed programmatically.
2532 cls               String           A custom CSS class to apply to the message box element
2533 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
2534                                    displayed (defaults to 75)
2535 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
2536                                    function will be btn (the name of the button that was clicked, if applicable,
2537                                    e.g. "ok"), and text (the value of the active text field, if applicable).
2538                                    Progress and wait dialogs will ignore this option since they do not respond to
2539                                    user actions and can only be closed programmatically, so any required function
2540                                    should be called by the same code after it closes the dialog.
2541 icon              String           A CSS class that provides a background image to be used as an icon for
2542                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
2543 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
2544 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
2545 modal             Boolean          False to allow user interaction with the page while the message box is
2546                                    displayed (defaults to true)
2547 msg               String           A string that will replace the existing message box body text (defaults
2548                                    to the XHTML-compliant non-breaking space character '&#160;')
2549 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
2550 progress          Boolean          True to display a progress bar (defaults to false)
2551 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
2552 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
2553 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
2554 title             String           The title text
2555 value             String           The string value to set into the active textbox element if displayed
2556 wait              Boolean          True to display a progress bar (defaults to false)
2557 width             Number           The width of the dialog in pixels
2558 </pre>
2559          *
2560          * Example usage:
2561          * <pre><code>
2562 Roo.Msg.show({
2563    title: 'Address',
2564    msg: 'Please enter your address:',
2565    width: 300,
2566    buttons: Roo.MessageBox.OKCANCEL,
2567    multiline: true,
2568    fn: saveAddress,
2569    animEl: 'addAddressBtn'
2570 });
2571 </code></pre>
2572          * @param {Object} config Configuration options
2573          * @return {Roo.MessageBox} This message box
2574          */
2575         show : function(options)
2576         {
2577             
2578             // this causes nightmares if you show one dialog after another
2579             // especially on callbacks..
2580              
2581             if(this.isVisible()){
2582                 
2583                 this.hide();
2584                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
2585                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
2586                 Roo.log("New Dialog Message:" +  options.msg )
2587                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
2588                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
2589                 
2590             }
2591             var d = this.getDialog();
2592             opt = options;
2593             d.setTitle(opt.title || "&#160;");
2594             d.close.setDisplayed(opt.closable !== false);
2595             activeTextEl = textboxEl;
2596             opt.prompt = opt.prompt || (opt.multiline ? true : false);
2597             if(opt.prompt){
2598                 if(opt.multiline){
2599                     textboxEl.hide();
2600                     textareaEl.show();
2601                     textareaEl.setHeight(typeof opt.multiline == "number" ?
2602                         opt.multiline : this.defaultTextHeight);
2603                     activeTextEl = textareaEl;
2604                 }else{
2605                     textboxEl.show();
2606                     textareaEl.hide();
2607                 }
2608             }else{
2609                 textboxEl.hide();
2610                 textareaEl.hide();
2611             }
2612             progressEl.setDisplayed(opt.progress === true);
2613             this.updateProgress(0);
2614             activeTextEl.dom.value = opt.value || "";
2615             if(opt.prompt){
2616                 dlg.setDefaultButton(activeTextEl);
2617             }else{
2618                 var bs = opt.buttons;
2619                 var db = null;
2620                 if(bs && bs.ok){
2621                     db = buttons["ok"];
2622                 }else if(bs && bs.yes){
2623                     db = buttons["yes"];
2624                 }
2625                 dlg.setDefaultButton(db);
2626             }
2627             bwidth = updateButtons(opt.buttons);
2628             this.updateText(opt.msg);
2629             if(opt.cls){
2630                 d.el.addClass(opt.cls);
2631             }
2632             d.proxyDrag = opt.proxyDrag === true;
2633             d.modal = opt.modal !== false;
2634             d.mask = opt.modal !== false ? mask : false;
2635             if(!d.isVisible()){
2636                 // force it to the end of the z-index stack so it gets a cursor in FF
2637                 document.body.appendChild(dlg.el.dom);
2638                 d.animateTarget = null;
2639                 d.show(options.animEl);
2640             }
2641             return this;
2642         },
2643
2644         /**
2645          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
2646          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
2647          * and closing the message box when the process is complete.
2648          * @param {String} title The title bar text
2649          * @param {String} msg The message box body text
2650          * @return {Roo.MessageBox} This message box
2651          */
2652         progress : function(title, msg){
2653             this.show({
2654                 title : title,
2655                 msg : msg,
2656                 buttons: false,
2657                 progress:true,
2658                 closable:false,
2659                 minWidth: this.minProgressWidth,
2660                 modal : true
2661             });
2662             return this;
2663         },
2664
2665         /**
2666          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
2667          * If a callback function is passed it will be called after the user clicks the button, and the
2668          * id of the button that was clicked will be passed as the only parameter to the callback
2669          * (could also be the top-right close button).
2670          * @param {String} title The title bar text
2671          * @param {String} msg The message box body text
2672          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2673          * @param {Object} scope (optional) The scope of the callback function
2674          * @return {Roo.MessageBox} This message box
2675          */
2676         alert : function(title, msg, fn, scope){
2677             this.show({
2678                 title : title,
2679                 msg : msg,
2680                 buttons: this.OK,
2681                 fn: fn,
2682                 scope : scope,
2683                 modal : true
2684             });
2685             return this;
2686         },
2687
2688         /**
2689          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
2690          * interaction while waiting for a long-running process to complete that does not have defined intervals.
2691          * You are responsible for closing the message box when the process is complete.
2692          * @param {String} msg The message box body text
2693          * @param {String} title (optional) The title bar text
2694          * @return {Roo.MessageBox} This message box
2695          */
2696         wait : function(msg, title){
2697             this.show({
2698                 title : title,
2699                 msg : msg,
2700                 buttons: false,
2701                 closable:false,
2702                 progress:true,
2703                 modal:true,
2704                 width:300,
2705                 wait:true
2706             });
2707             waitTimer = Roo.TaskMgr.start({
2708                 run: function(i){
2709                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
2710                 },
2711                 interval: 1000
2712             });
2713             return this;
2714         },
2715
2716         /**
2717          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
2718          * If a callback function is passed it will be called after the user clicks either button, and the id of the
2719          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
2720          * @param {String} title The title bar text
2721          * @param {String} msg The message box body text
2722          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2723          * @param {Object} scope (optional) The scope of the callback function
2724          * @return {Roo.MessageBox} This message box
2725          */
2726         confirm : function(title, msg, fn, scope){
2727             this.show({
2728                 title : title,
2729                 msg : msg,
2730                 buttons: this.YESNO,
2731                 fn: fn,
2732                 scope : scope,
2733                 modal : true
2734             });
2735             return this;
2736         },
2737
2738         /**
2739          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
2740          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
2741          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
2742          * (could also be the top-right close button) and the text that was entered will be passed as the two
2743          * parameters to the callback.
2744          * @param {String} title The title bar text
2745          * @param {String} msg The message box body text
2746          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2747          * @param {Object} scope (optional) The scope of the callback function
2748          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
2749          * property, or the height in pixels to create the textbox (defaults to false / single-line)
2750          * @return {Roo.MessageBox} This message box
2751          */
2752         prompt : function(title, msg, fn, scope, multiline){
2753             this.show({
2754                 title : title,
2755                 msg : msg,
2756                 buttons: this.OKCANCEL,
2757                 fn: fn,
2758                 minWidth:250,
2759                 scope : scope,
2760                 prompt:true,
2761                 multiline: multiline,
2762                 modal : true
2763             });
2764             return this;
2765         },
2766
2767         /**
2768          * Button config that displays a single OK button
2769          * @type Object
2770          */
2771         OK : {ok:true},
2772         /**
2773          * Button config that displays Yes and No buttons
2774          * @type Object
2775          */
2776         YESNO : {yes:true, no:true},
2777         /**
2778          * Button config that displays OK and Cancel buttons
2779          * @type Object
2780          */
2781         OKCANCEL : {ok:true, cancel:true},
2782         /**
2783          * Button config that displays Yes, No and Cancel buttons
2784          * @type Object
2785          */
2786         YESNOCANCEL : {yes:true, no:true, cancel:true},
2787
2788         /**
2789          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
2790          * @type Number
2791          */
2792         defaultTextHeight : 75,
2793         /**
2794          * The maximum width in pixels of the message box (defaults to 600)
2795          * @type Number
2796          */
2797         maxWidth : 600,
2798         /**
2799          * The minimum width in pixels of the message box (defaults to 100)
2800          * @type Number
2801          */
2802         minWidth : 100,
2803         /**
2804          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
2805          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
2806          * @type Number
2807          */
2808         minProgressWidth : 250,
2809         /**
2810          * An object containing the default button text strings that can be overriden for localized language support.
2811          * Supported properties are: ok, cancel, yes and no.
2812          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
2813          * @type Object
2814          */
2815         buttonText : {
2816             ok : "OK",
2817             cancel : "Cancel",
2818             yes : "Yes",
2819             no : "No"
2820         }
2821     };
2822 }();
2823
2824 /**
2825  * Shorthand for {@link Roo.MessageBox}
2826  */
2827 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox 
2828 Roo.Msg = Roo.Msg || Roo.MessageBox;
2829 /*
2830  * - LGPL
2831  *
2832  * navbar
2833  * 
2834  */
2835
2836 /**
2837  * @class Roo.bootstrap.Navbar
2838  * @extends Roo.bootstrap.Component
2839  * Bootstrap Navbar class
2840
2841  * @constructor
2842  * Create a new Navbar
2843  * @param {Object} config The config object
2844  */
2845
2846
2847 Roo.bootstrap.Navbar = function(config){
2848     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
2849     
2850 };
2851
2852 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
2853     
2854     
2855    
2856     // private
2857     navItems : false,
2858     loadMask : false,
2859     
2860     
2861     getAutoCreate : function(){
2862         
2863         
2864         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
2865         
2866     },
2867     
2868     initEvents :function ()
2869     {
2870         //Roo.log(this.el.select('.navbar-toggle',true));
2871         this.el.select('.navbar-toggle',true).on('click', function() {
2872            // Roo.log('click');
2873             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
2874         }, this);
2875         
2876         var mark = {
2877             tag: "div",
2878             cls:"x-dlg-mask"
2879         }
2880         
2881         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
2882         
2883         var size = this.el.getSize();
2884         this.maskEl.setSize(size.width, size.height);
2885         this.maskEl.enableDisplayMode("block");
2886         this.maskEl.hide();
2887         
2888         if(this.loadMask){
2889             this.maskEl.show();
2890         }
2891     },
2892     
2893     
2894     getChildContainer : function()
2895     {
2896         if (this.el.select('.collapse').getCount()) {
2897             return this.el.select('.collapse',true).first();
2898         }
2899         
2900         return this.el;
2901     },
2902     
2903     mask : function()
2904     {
2905         this.maskEl.show();
2906     },
2907     
2908     unmask : function()
2909     {
2910         this.maskEl.hide();
2911     } 
2912     
2913     
2914     
2915     
2916 });
2917
2918
2919
2920  
2921
2922  /*
2923  * - LGPL
2924  *
2925  * navbar
2926  * 
2927  */
2928
2929 /**
2930  * @class Roo.bootstrap.NavSimplebar
2931  * @extends Roo.bootstrap.Navbar
2932  * Bootstrap Sidebar class
2933  *
2934  * @cfg {Boolean} inverse is inverted color
2935  * 
2936  * @cfg {String} type (nav | pills | tabs)
2937  * @cfg {Boolean} arrangement stacked | justified
2938  * @cfg {String} align (left | right) alignment
2939  * 
2940  * @cfg {Boolean} main (true|false) main nav bar? default false
2941  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
2942  * 
2943  * @cfg {String} tag (header|footer|nav|div) default is nav 
2944
2945  * 
2946  * 
2947  * 
2948  * @constructor
2949  * Create a new Sidebar
2950  * @param {Object} config The config object
2951  */
2952
2953
2954 Roo.bootstrap.NavSimplebar = function(config){
2955     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
2956 };
2957
2958 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
2959     
2960     inverse: false,
2961     
2962     type: false,
2963     arrangement: '',
2964     align : false,
2965     
2966     
2967     
2968     main : false,
2969     
2970     
2971     tag : false,
2972     
2973     
2974     getAutoCreate : function(){
2975         
2976         
2977         var cfg = {
2978             tag : this.tag || 'div',
2979             cls : 'navbar'
2980         };
2981           
2982         
2983         cfg.cn = [
2984             {
2985                 cls: 'nav',
2986                 tag : 'ul'
2987             }
2988         ];
2989         
2990          
2991         this.type = this.type || 'nav';
2992         if (['tabs','pills'].indexOf(this.type)!==-1) {
2993             cfg.cn[0].cls += ' nav-' + this.type
2994         
2995         
2996         } else {
2997             if (this.type!=='nav') {
2998                 Roo.log('nav type must be nav/tabs/pills')
2999             }
3000             cfg.cn[0].cls += ' navbar-nav'
3001         }
3002         
3003         
3004         
3005         
3006         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3007             cfg.cn[0].cls += ' nav-' + this.arrangement;
3008         }
3009         
3010         
3011         if (this.align === 'right') {
3012             cfg.cn[0].cls += ' navbar-right';
3013         }
3014         
3015         if (this.inverse) {
3016             cfg.cls += ' navbar-inverse';
3017             
3018         }
3019         
3020         
3021         return cfg;
3022     
3023         
3024     }
3025     
3026     
3027     
3028 });
3029
3030
3031
3032  
3033
3034  
3035        /*
3036  * - LGPL
3037  *
3038  * navbar
3039  * 
3040  */
3041
3042 /**
3043  * @class Roo.bootstrap.NavHeaderbar
3044  * @extends Roo.bootstrap.NavSimplebar
3045  * Bootstrap Sidebar class
3046  *
3047  * @cfg {String} brand what is brand
3048  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3049  * @cfg {String} brand_href href of the brand
3050  * @cfg {Boolean} srButton generate the sr-only button (true | false) default true
3051  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3052  * 
3053  * @constructor
3054  * Create a new Sidebar
3055  * @param {Object} config The config object
3056  */
3057
3058
3059 Roo.bootstrap.NavHeaderbar = function(config){
3060     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3061 };
3062
3063 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3064     
3065     position: '',
3066     brand: '',
3067     brand_href: false,
3068     srButton : true,
3069     autohide : false,
3070     
3071     getAutoCreate : function(){
3072         
3073         var   cfg = {
3074             tag: this.nav || 'nav',
3075             cls: 'navbar',
3076             role: 'navigation',
3077             cn: []
3078         };
3079         
3080         if(this.srButton){
3081             cfg.cn.push({
3082                 tag: 'div',
3083                 cls: 'navbar-header',
3084                 cn: [
3085                     {
3086                         tag: 'button',
3087                         type: 'button',
3088                         cls: 'navbar-toggle',
3089                         'data-toggle': 'collapse',
3090                         cn: [
3091                             {
3092                                 tag: 'span',
3093                                 cls: 'sr-only',
3094                                 html: 'Toggle navigation'
3095                             },
3096                             {
3097                                 tag: 'span',
3098                                 cls: 'icon-bar'
3099                             },
3100                             {
3101                                 tag: 'span',
3102                                 cls: 'icon-bar'
3103                             },
3104                             {
3105                                 tag: 'span',
3106                                 cls: 'icon-bar'
3107                             }
3108                         ]
3109                     }
3110                 ]
3111             });
3112         }
3113         
3114         cfg.cn.push({
3115             tag: 'div',
3116             cls: 'collapse navbar-collapse',
3117             cn : []
3118         });
3119         
3120         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3121         
3122         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3123             cfg.cls += ' navbar-' + this.position;
3124             
3125             // tag can override this..
3126             
3127             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3128         }
3129         
3130         if (this.brand !== '') {
3131             cfg.cn[0].cn.push({
3132                 tag: 'a',
3133                 href: this.brand_href ? this.brand_href : '#',
3134                 cls: 'navbar-brand',
3135                 cn: [
3136                 this.brand
3137                 ]
3138             });
3139         }
3140         
3141         if(this.main){
3142             cfg.cls += ' main-nav';
3143         }
3144         
3145         
3146         return cfg;
3147
3148         
3149     },
3150     initEvents : function()
3151     {
3152         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3153         
3154         if (this.autohide) {
3155             
3156             var prevScroll = 0;
3157             var ft = this.el;
3158             
3159             Roo.get(document).on('scroll',function(e) {
3160                 var ns = Roo.get(document).getScroll().top;
3161                 var os = prevScroll;
3162                 prevScroll = ns;
3163                 
3164                 if(ns > os){
3165                     ft.removeClass('slideDown');
3166                     ft.addClass('slideUp');
3167                     return;
3168                 }
3169                 ft.removeClass('slideUp');
3170                 ft.addClass('slideDown');
3171                  
3172               
3173           },this);
3174         }
3175     }    
3176           
3177       
3178     
3179     
3180 });
3181
3182
3183
3184  
3185
3186  /*
3187  * - LGPL
3188  *
3189  * navbar
3190  * 
3191  */
3192
3193 /**
3194  * @class Roo.bootstrap.NavSidebar
3195  * @extends Roo.bootstrap.Navbar
3196  * Bootstrap Sidebar class
3197  * 
3198  * @constructor
3199  * Create a new Sidebar
3200  * @param {Object} config The config object
3201  */
3202
3203
3204 Roo.bootstrap.NavSidebar = function(config){
3205     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3206 };
3207
3208 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3209     
3210     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3211     
3212     getAutoCreate : function(){
3213         
3214         
3215         return  {
3216             tag: 'div',
3217             cls: 'sidebar sidebar-nav'
3218         };
3219     
3220         
3221     }
3222     
3223     
3224     
3225 });
3226
3227
3228
3229  
3230
3231  /*
3232  * - LGPL
3233  *
3234  * nav group
3235  * 
3236  */
3237
3238 /**
3239  * @class Roo.bootstrap.NavGroup
3240  * @extends Roo.bootstrap.Component
3241  * Bootstrap NavGroup class
3242  * @cfg {String} align left | right
3243  * @cfg {Boolean} inverse false | true
3244  * @cfg {String} type (nav|pills|tab) default nav
3245  * @cfg {String} navId - reference Id for navbar.
3246
3247  * 
3248  * @constructor
3249  * Create a new nav group
3250  * @param {Object} config The config object
3251  */
3252
3253 Roo.bootstrap.NavGroup = function(config){
3254     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3255     this.navItems = [];
3256    
3257     Roo.bootstrap.NavGroup.register(this);
3258      this.addEvents({
3259         /**
3260              * @event changed
3261              * Fires when the active item changes
3262              * @param {Roo.bootstrap.NavGroup} this
3263              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3264              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3265          */
3266         'changed': true
3267      });
3268     
3269 };
3270
3271 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3272     
3273     align: '',
3274     inverse: false,
3275     form: false,
3276     type: 'nav',
3277     navId : '',
3278     // private
3279     
3280     navItems : false, 
3281     
3282     getAutoCreate : function()
3283     {
3284         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3285         
3286         cfg = {
3287             tag : 'ul',
3288             cls: 'nav' 
3289         }
3290         
3291         if (['tabs','pills'].indexOf(this.type)!==-1) {
3292             cfg.cls += ' nav-' + this.type
3293         } else {
3294             if (this.type!=='nav') {
3295                 Roo.log('nav type must be nav/tabs/pills')
3296             }
3297             cfg.cls += ' navbar-nav'
3298         }
3299         
3300         if (this.parent().sidebar) {
3301             cfg = {
3302                 tag: 'ul',
3303                 cls: 'dashboard-menu sidebar-menu'
3304             }
3305             
3306             return cfg;
3307         }
3308         
3309         if (this.form === true) {
3310             cfg = {
3311                 tag: 'form',
3312                 cls: 'navbar-form'
3313             }
3314             
3315             if (this.align === 'right') {
3316                 cfg.cls += ' navbar-right';
3317             } else {
3318                 cfg.cls += ' navbar-left';
3319             }
3320         }
3321         
3322         if (this.align === 'right') {
3323             cfg.cls += ' navbar-right';
3324         }
3325         
3326         if (this.inverse) {
3327             cfg.cls += ' navbar-inverse';
3328             
3329         }
3330         
3331         
3332         return cfg;
3333     },
3334     /**
3335     * sets the active Navigation item
3336     * @param {Roo.bootstrap.NavItem} the new current navitem
3337     */
3338     setActiveItem : function(item)
3339     {
3340         var prev = false;
3341         Roo.each(this.navItems, function(v){
3342             if (v == item) {
3343                 return ;
3344             }
3345             if (v.isActive()) {
3346                 v.setActive(false, true);
3347                 prev = v;
3348                 
3349             }
3350             
3351         });
3352
3353         item.setActive(true, true);
3354         this.fireEvent('changed', this, item, prev);
3355         
3356         
3357     },
3358     /**
3359     * gets the active Navigation item
3360     * @return {Roo.bootstrap.NavItem} the current navitem
3361     */
3362     getActive : function()
3363     {
3364         
3365         var prev = false;
3366         Roo.each(this.navItems, function(v){
3367             
3368             if (v.isActive()) {
3369                 prev = v;
3370                 
3371             }
3372             
3373         });
3374         return prev;
3375     },
3376     
3377     indexOfNav : function()
3378     {
3379         
3380         var prev = false;
3381         Roo.each(this.navItems, function(v,i){
3382             
3383             if (v.isActive()) {
3384                 prev = i;
3385                 
3386             }
3387             
3388         });
3389         return prev;
3390     },
3391     /**
3392     * adds a Navigation item
3393     * @param {Roo.bootstrap.NavItem} the navitem to add
3394     */
3395     addItem : function(cfg)
3396     {
3397         var cn = new Roo.bootstrap.NavItem(cfg);
3398         this.register(cn);
3399         cn.parentId = this.id;
3400         cn.onRender(this.el, null);
3401         return cn;
3402     },
3403     /**
3404     * register a Navigation item
3405     * @param {Roo.bootstrap.NavItem} the navitem to add
3406     */
3407     register : function(item)
3408     {
3409         this.navItems.push( item);
3410         item.navId = this.navId;
3411     
3412     },
3413   
3414     
3415     getNavItem: function(tabId)
3416     {
3417         var ret = false;
3418         Roo.each(this.navItems, function(e) {
3419             if (e.tabId == tabId) {
3420                ret =  e;
3421                return false;
3422             }
3423             return true;
3424             
3425         });
3426         return ret;
3427     },
3428     
3429     setActiveNext : function()
3430     {
3431         var i = this.indexOfNav(this.getActive());
3432         if (i > this.navItems.length) {
3433             return;
3434         }
3435         this.setActiveItem(this.navItems[i+1]);
3436     },
3437     setActivePrev : function()
3438     {
3439         var i = this.indexOfNav(this.getActive());
3440         if (i  < 1) {
3441             return;
3442         }
3443         this.setActiveItem(this.navItems[i-1]);
3444     },
3445     clearWasActive : function(except) {
3446         Roo.each(this.navItems, function(e) {
3447             if (e.tabId != except.tabId && e.was_active) {
3448                e.was_active = false;
3449                return false;
3450             }
3451             return true;
3452             
3453         });
3454     },
3455     getWasActive : function ()
3456     {
3457         var r = false;
3458         Roo.each(this.navItems, function(e) {
3459             if (e.was_active) {
3460                r = e;
3461                return false;
3462             }
3463             return true;
3464             
3465         });
3466         return r;
3467     }
3468     
3469     
3470 });
3471
3472  
3473 Roo.apply(Roo.bootstrap.NavGroup, {
3474     
3475     groups: {},
3476      /**
3477     * register a Navigation Group
3478     * @param {Roo.bootstrap.NavGroup} the navgroup to add
3479     */
3480     register : function(navgrp)
3481     {
3482         this.groups[navgrp.navId] = navgrp;
3483         
3484     },
3485     /**
3486     * fetch a Navigation Group based on the navigation ID
3487     * @param {string} the navgroup to add
3488     * @returns {Roo.bootstrap.NavGroup} the navgroup 
3489     */
3490     get: function(navId) {
3491         if (typeof(this.groups[navId]) == 'undefined') {
3492             return false;
3493             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
3494         }
3495         return this.groups[navId] ;
3496     }
3497     
3498     
3499     
3500 });
3501
3502  /*
3503  * - LGPL
3504  *
3505  * row
3506  * 
3507  */
3508
3509 /**
3510  * @class Roo.bootstrap.NavItem
3511  * @extends Roo.bootstrap.Component
3512  * Bootstrap Navbar.NavItem class
3513  * @cfg {String} href  link to
3514  * @cfg {String} html content of button
3515  * @cfg {String} badge text inside badge
3516  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
3517  * @cfg {String} glyphicon name of glyphicon
3518  * @cfg {String} icon name of font awesome icon
3519  * @cfg {Boolean} active Is item active
3520  * @cfg {Boolean} disabled Is item disabled
3521  
3522  * @cfg {Boolean} preventDefault (true | false) default false
3523  * @cfg {String} tabId the tab that this item activates.
3524  * @cfg {String} tagtype (a|span) render as a href or span?
3525   
3526  * @constructor
3527  * Create a new Navbar Item
3528  * @param {Object} config The config object
3529  */
3530 Roo.bootstrap.NavItem = function(config){
3531     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
3532     this.addEvents({
3533         // raw events
3534         /**
3535          * @event click
3536          * The raw click event for the entire grid.
3537          * @param {Roo.EventObject} e
3538          */
3539         "click" : true,
3540          /**
3541             * @event changed
3542             * Fires when the active item active state changes
3543             * @param {Roo.bootstrap.NavItem} this
3544             * @param {boolean} state the new state
3545              
3546          */
3547         'changed': true
3548     });
3549    
3550 };
3551
3552 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
3553     
3554     href: false,
3555     html: '',
3556     badge: '',
3557     icon: false,
3558     glyphicon: false,
3559     active: false,
3560     preventDefault : false,
3561     tabId : false,
3562     tagtype : 'a',
3563     disabled : false,
3564     
3565     was_active : false,
3566     
3567     getAutoCreate : function(){
3568          
3569         var cfg = {
3570             tag: 'li',
3571             cls: 'nav-item'
3572             
3573         }
3574         if (this.active) {
3575             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
3576         }
3577         if (this.disabled) {
3578             cfg.cls += ' disabled';
3579         }
3580         
3581         if (this.href || this.html || this.glyphicon || this.icon) {
3582             cfg.cn = [
3583                 {
3584                     tag: this.tagtype,
3585                     href : this.href || "#",
3586                     html: this.html || ''
3587                 }
3588             ];
3589             
3590             if (this.icon) {
3591                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
3592             }
3593
3594             if(this.glyphicon) {
3595                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
3596             }
3597             
3598             if (this.menu) {
3599                 
3600                 cfg.cn[0].html += " <span class='caret'></span>";
3601              
3602             }
3603             
3604             if (this.badge !== '') {
3605                  
3606                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
3607             }
3608         }
3609         
3610         
3611         
3612         return cfg;
3613     },
3614     initEvents: function() {
3615        // Roo.log('init events?');
3616        // Roo.log(this.el.dom);
3617         if (typeof (this.menu) != 'undefined') {
3618             this.menu.parentType = this.xtype;
3619             this.menu.triggerEl = this.el;
3620             this.addxtype(Roo.apply({}, this.menu));
3621         }
3622
3623        
3624         this.el.select('a',true).on('click', this.onClick, this);
3625         // at this point parent should be available..
3626         this.parent().register(this);
3627     },
3628     
3629     onClick : function(e)
3630     {
3631          
3632         if(this.preventDefault){
3633             e.preventDefault();
3634         }
3635         if (this.disabled) {
3636             return;
3637         }
3638         
3639         var tg = Roo.bootstrap.TabGroup.get(this.navId);
3640         if (tg && tg.transition) {
3641             Roo.log("waiting for the transitionend");
3642             return;
3643         }
3644         
3645         Roo.log("fire event clicked");
3646         if(this.fireEvent('click', this, e) === false){
3647             return;
3648         };
3649         
3650         if (['tabs','pills'].indexOf(this.parent().type)!==-1) {
3651             if (typeof(this.parent().setActiveItem) !== 'undefined') {
3652                 this.parent().setActiveItem(this);
3653             }
3654         } 
3655     },
3656     
3657     isActive: function () {
3658         return this.active
3659     },
3660     setActive : function(state, fire, is_was_active)
3661     {
3662         if (this.active && !state & this.navId) {
3663             this.was_active = true;
3664             var nv = Roo.bootstrap.NavGroup.get(this.navId);
3665             if (nv) {
3666                 nv.clearWasActive(this);
3667             }
3668             
3669         }
3670         this.active = state;
3671         
3672         if (!state ) {
3673             this.el.removeClass('active');
3674         } else if (!this.el.hasClass('active')) {
3675             this.el.addClass('active');
3676         }
3677         if (fire) {
3678             this.fireEvent('changed', this, state);
3679         }
3680         
3681         // show a panel if it's registered and related..
3682         
3683         if (!this.navId || !this.tabId || !state || is_was_active) {
3684             return;
3685         }
3686         
3687         var tg = Roo.bootstrap.TabGroup.get(this.navId);
3688         if (!tg) {
3689             return;
3690         }
3691         var pan = tg.getPanelByName(this.tabId);
3692         if (!pan) {
3693             return;
3694         }
3695         // if we can not flip to new panel - go back to old nav highlight..
3696         if (false == tg.showPanel(pan)) {
3697             var nv = Roo.bootstrap.NavGroup.get(this.navId);
3698             if (nv) {
3699                 var onav = nv.getWasActive();
3700                 if (onav) {
3701                     onav.setActive(true, false, true);
3702                 }
3703             }
3704             
3705         }
3706         
3707         
3708         
3709     },
3710      // this should not be here...
3711     setDisabled : function(state)
3712     {
3713         this.disabled = state;
3714         if (!state ) {
3715             this.el.removeClass('disabled');
3716         } else if (!this.el.hasClass('disabled')) {
3717             this.el.addClass('disabled');
3718         }
3719         
3720     }
3721 });
3722  
3723
3724  /*
3725  * - LGPL
3726  *
3727  * sidebar item
3728  *
3729  *  li
3730  *    <span> icon </span>
3731  *    <span> text </span>
3732  *    <span>badge </span>
3733  */
3734
3735 /**
3736  * @class Roo.bootstrap.NavSidebarItem
3737  * @extends Roo.bootstrap.NavItem
3738  * Bootstrap Navbar.NavSidebarItem class
3739  * @constructor
3740  * Create a new Navbar Button
3741  * @param {Object} config The config object
3742  */
3743 Roo.bootstrap.NavSidebarItem = function(config){
3744     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
3745     this.addEvents({
3746         // raw events
3747         /**
3748          * @event click
3749          * The raw click event for the entire grid.
3750          * @param {Roo.EventObject} e
3751          */
3752         "click" : true,
3753          /**
3754             * @event changed
3755             * Fires when the active item active state changes
3756             * @param {Roo.bootstrap.NavSidebarItem} this
3757             * @param {boolean} state the new state
3758              
3759          */
3760         'changed': true
3761     });
3762    
3763 };
3764
3765 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
3766     
3767     
3768     getAutoCreate : function(){
3769         
3770         
3771         var a = {
3772                 tag: 'a',
3773                 href : this.href || '#',
3774                 cls: '',
3775                 html : '',
3776                 cn : []
3777         };
3778         var cfg = {
3779             tag: 'li',
3780             cls: '',
3781             cn: [ a ]
3782         }
3783         var span = {
3784             tag: 'span',
3785             html : this.html || ''
3786         }
3787         
3788         
3789         if (this.active) {
3790             cfg.cls += ' active';
3791         }
3792         
3793         // left icon..
3794         if (this.glyphicon || this.icon) {
3795             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
3796             a.cn.push({ tag : 'i', cls : c }) ;
3797         }
3798         // html..
3799         a.cn.push(span);
3800         // then badge..
3801         if (this.badge !== '') {
3802             a.cn.push({ tag: 'span',  cls : 'badge pull-right ' + (this.badgecls || ''), html: this.badge }); 
3803         }
3804         // fi
3805         if (this.menu) {
3806             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
3807             a.cls += 'dropdown-toggle treeview' ;
3808             
3809         }
3810         
3811         
3812         
3813         return cfg;
3814          
3815            
3816     }
3817    
3818      
3819  
3820 });
3821  
3822
3823  /*
3824  * - LGPL
3825  *
3826  * row
3827  * 
3828  */
3829
3830 /**
3831  * @class Roo.bootstrap.Row
3832  * @extends Roo.bootstrap.Component
3833  * Bootstrap Row class (contains columns...)
3834  * 
3835  * @constructor
3836  * Create a new Row
3837  * @param {Object} config The config object
3838  */
3839
3840 Roo.bootstrap.Row = function(config){
3841     Roo.bootstrap.Row.superclass.constructor.call(this, config);
3842 };
3843
3844 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
3845     
3846     getAutoCreate : function(){
3847        return {
3848             cls: 'row clearfix'
3849        };
3850     }
3851     
3852     
3853 });
3854
3855  
3856
3857  /*
3858  * - LGPL
3859  *
3860  * element
3861  * 
3862  */
3863
3864 /**
3865  * @class Roo.bootstrap.Element
3866  * @extends Roo.bootstrap.Component
3867  * Bootstrap Element class
3868  * @cfg {String} html contents of the element
3869  * @cfg {String} tag tag of the element
3870  * @cfg {String} cls class of the element
3871  * 
3872  * @constructor
3873  * Create a new Element
3874  * @param {Object} config The config object
3875  */
3876
3877 Roo.bootstrap.Element = function(config){
3878     Roo.bootstrap.Element.superclass.constructor.call(this, config);
3879 };
3880
3881 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
3882     
3883     tag: 'div',
3884     cls: '',
3885     html: '',
3886      
3887     
3888     getAutoCreate : function(){
3889         
3890         var cfg = {
3891             tag: this.tag,
3892             cls: this.cls,
3893             html: this.html
3894         }
3895         
3896         
3897         
3898         return cfg;
3899     }
3900    
3901 });
3902
3903  
3904
3905  /*
3906  * - LGPL
3907  *
3908  * pagination
3909  * 
3910  */
3911
3912 /**
3913  * @class Roo.bootstrap.Pagination
3914  * @extends Roo.bootstrap.Component
3915  * Bootstrap Pagination class
3916  * @cfg {String} size xs | sm | md | lg
3917  * @cfg {Boolean} inverse false | true
3918  * 
3919  * @constructor
3920  * Create a new Pagination
3921  * @param {Object} config The config object
3922  */
3923
3924 Roo.bootstrap.Pagination = function(config){
3925     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
3926 };
3927
3928 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
3929     
3930     cls: false,
3931     size: false,
3932     inverse: false,
3933     
3934     getAutoCreate : function(){
3935         var cfg = {
3936             tag: 'ul',
3937                 cls: 'pagination'
3938         };
3939         if (this.inverse) {
3940             cfg.cls += ' inverse';
3941         }
3942         if (this.html) {
3943             cfg.html=this.html;
3944         }
3945         if (this.cls) {
3946             cfg.cls += " " + this.cls;
3947         }
3948         return cfg;
3949     }
3950    
3951 });
3952
3953  
3954
3955  /*
3956  * - LGPL
3957  *
3958  * Pagination item
3959  * 
3960  */
3961
3962
3963 /**
3964  * @class Roo.bootstrap.PaginationItem
3965  * @extends Roo.bootstrap.Component
3966  * Bootstrap PaginationItem class
3967  * @cfg {String} html text
3968  * @cfg {String} href the link
3969  * @cfg {Boolean} preventDefault (true | false) default true
3970  * @cfg {Boolean} active (true | false) default false
3971  * 
3972  * 
3973  * @constructor
3974  * Create a new PaginationItem
3975  * @param {Object} config The config object
3976  */
3977
3978
3979 Roo.bootstrap.PaginationItem = function(config){
3980     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
3981     this.addEvents({
3982         // raw events
3983         /**
3984          * @event click
3985          * The raw click event for the entire grid.
3986          * @param {Roo.EventObject} e
3987          */
3988         "click" : true
3989     });
3990 };
3991
3992 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
3993     
3994     href : false,
3995     html : false,
3996     preventDefault: true,
3997     active : false,
3998     cls : false,
3999     
4000     getAutoCreate : function(){
4001         var cfg= {
4002             tag: 'li',
4003             cn: [
4004                 {
4005                     tag : 'a',
4006                     href : this.href ? this.href : '#',
4007                     html : this.html ? this.html : ''
4008                 }
4009             ]
4010         };
4011         
4012         if(this.cls){
4013             cfg.cls = this.cls;
4014         }
4015         
4016         if(this.active){
4017             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4018         }
4019         
4020         return cfg;
4021     },
4022     
4023     initEvents: function() {
4024         
4025         this.el.on('click', this.onClick, this);
4026         
4027     },
4028     onClick : function(e)
4029     {
4030         Roo.log('PaginationItem on click ');
4031         if(this.preventDefault){
4032             e.preventDefault();
4033         }
4034         
4035         this.fireEvent('click', this, e);
4036     }
4037    
4038 });
4039
4040  
4041
4042  /*
4043  * - LGPL
4044  *
4045  * slider
4046  * 
4047  */
4048
4049
4050 /**
4051  * @class Roo.bootstrap.Slider
4052  * @extends Roo.bootstrap.Component
4053  * Bootstrap Slider class
4054  *    
4055  * @constructor
4056  * Create a new Slider
4057  * @param {Object} config The config object
4058  */
4059
4060 Roo.bootstrap.Slider = function(config){
4061     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4062 };
4063
4064 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
4065     
4066     getAutoCreate : function(){
4067         
4068         var cfg = {
4069             tag: 'div',
4070             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4071             cn: [
4072                 {
4073                     tag: 'a',
4074                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
4075                 }
4076             ]
4077         }
4078         
4079         return cfg;
4080     }
4081    
4082 });
4083
4084  /*
4085  * Based on:
4086  * Ext JS Library 1.1.1
4087  * Copyright(c) 2006-2007, Ext JS, LLC.
4088  *
4089  * Originally Released Under LGPL - original licence link has changed is not relivant.
4090  *
4091  * Fork - LGPL
4092  * <script type="text/javascript">
4093  */
4094  
4095
4096 /**
4097  * @class Roo.grid.ColumnModel
4098  * @extends Roo.util.Observable
4099  * This is the default implementation of a ColumnModel used by the Grid. It defines
4100  * the columns in the grid.
4101  * <br>Usage:<br>
4102  <pre><code>
4103  var colModel = new Roo.grid.ColumnModel([
4104         {header: "Ticker", width: 60, sortable: true, locked: true},
4105         {header: "Company Name", width: 150, sortable: true},
4106         {header: "Market Cap.", width: 100, sortable: true},
4107         {header: "$ Sales", width: 100, sortable: true, renderer: money},
4108         {header: "Employees", width: 100, sortable: true, resizable: false}
4109  ]);
4110  </code></pre>
4111  * <p>
4112  
4113  * The config options listed for this class are options which may appear in each
4114  * individual column definition.
4115  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4116  * @constructor
4117  * @param {Object} config An Array of column config objects. See this class's
4118  * config objects for details.
4119 */
4120 Roo.grid.ColumnModel = function(config){
4121         /**
4122      * The config passed into the constructor
4123      */
4124     this.config = config;
4125     this.lookup = {};
4126
4127     // if no id, create one
4128     // if the column does not have a dataIndex mapping,
4129     // map it to the order it is in the config
4130     for(var i = 0, len = config.length; i < len; i++){
4131         var c = config[i];
4132         if(typeof c.dataIndex == "undefined"){
4133             c.dataIndex = i;
4134         }
4135         if(typeof c.renderer == "string"){
4136             c.renderer = Roo.util.Format[c.renderer];
4137         }
4138         if(typeof c.id == "undefined"){
4139             c.id = Roo.id();
4140         }
4141         if(c.editor && c.editor.xtype){
4142             c.editor  = Roo.factory(c.editor, Roo.grid);
4143         }
4144         if(c.editor && c.editor.isFormField){
4145             c.editor = new Roo.grid.GridEditor(c.editor);
4146         }
4147         this.lookup[c.id] = c;
4148     }
4149
4150     /**
4151      * The width of columns which have no width specified (defaults to 100)
4152      * @type Number
4153      */
4154     this.defaultWidth = 100;
4155
4156     /**
4157      * Default sortable of columns which have no sortable specified (defaults to false)
4158      * @type Boolean
4159      */
4160     this.defaultSortable = false;
4161
4162     this.addEvents({
4163         /**
4164              * @event widthchange
4165              * Fires when the width of a column changes.
4166              * @param {ColumnModel} this
4167              * @param {Number} columnIndex The column index
4168              * @param {Number} newWidth The new width
4169              */
4170             "widthchange": true,
4171         /**
4172              * @event headerchange
4173              * Fires when the text of a header changes.
4174              * @param {ColumnModel} this
4175              * @param {Number} columnIndex The column index
4176              * @param {Number} newText The new header text
4177              */
4178             "headerchange": true,
4179         /**
4180              * @event hiddenchange
4181              * Fires when a column is hidden or "unhidden".
4182              * @param {ColumnModel} this
4183              * @param {Number} columnIndex The column index
4184              * @param {Boolean} hidden true if hidden, false otherwise
4185              */
4186             "hiddenchange": true,
4187             /**
4188          * @event columnmoved
4189          * Fires when a column is moved.
4190          * @param {ColumnModel} this
4191          * @param {Number} oldIndex
4192          * @param {Number} newIndex
4193          */
4194         "columnmoved" : true,
4195         /**
4196          * @event columlockchange
4197          * Fires when a column's locked state is changed
4198          * @param {ColumnModel} this
4199          * @param {Number} colIndex
4200          * @param {Boolean} locked true if locked
4201          */
4202         "columnlockchange" : true
4203     });
4204     Roo.grid.ColumnModel.superclass.constructor.call(this);
4205 };
4206 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4207     /**
4208      * @cfg {String} header The header text to display in the Grid view.
4209      */
4210     /**
4211      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4212      * {@link Roo.data.Record} definition from which to draw the column's value. If not
4213      * specified, the column's index is used as an index into the Record's data Array.
4214      */
4215     /**
4216      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4217      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4218      */
4219     /**
4220      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4221      * Defaults to the value of the {@link #defaultSortable} property.
4222      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4223      */
4224     /**
4225      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
4226      */
4227     /**
4228      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
4229      */
4230     /**
4231      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4232      */
4233     /**
4234      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4235      */
4236     /**
4237      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4238      * given the cell's data value. See {@link #setRenderer}. If not specified, the
4239      * default renderer uses the raw data value. If an object is returned (bootstrap only)
4240      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4241      */
4242        /**
4243      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
4244      */
4245     /**
4246      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
4247      */
4248
4249     /**
4250      * Returns the id of the column at the specified index.
4251      * @param {Number} index The column index
4252      * @return {String} the id
4253      */
4254     getColumnId : function(index){
4255         return this.config[index].id;
4256     },
4257
4258     /**
4259      * Returns the column for a specified id.
4260      * @param {String} id The column id
4261      * @return {Object} the column
4262      */
4263     getColumnById : function(id){
4264         return this.lookup[id];
4265     },
4266
4267     
4268     /**
4269      * Returns the column for a specified dataIndex.
4270      * @param {String} dataIndex The column dataIndex
4271      * @return {Object|Boolean} the column or false if not found
4272      */
4273     getColumnByDataIndex: function(dataIndex){
4274         var index = this.findColumnIndex(dataIndex);
4275         return index > -1 ? this.config[index] : false;
4276     },
4277     
4278     /**
4279      * Returns the index for a specified column id.
4280      * @param {String} id The column id
4281      * @return {Number} the index, or -1 if not found
4282      */
4283     getIndexById : function(id){
4284         for(var i = 0, len = this.config.length; i < len; i++){
4285             if(this.config[i].id == id){
4286                 return i;
4287             }
4288         }
4289         return -1;
4290     },
4291     
4292     /**
4293      * Returns the index for a specified column dataIndex.
4294      * @param {String} dataIndex The column dataIndex
4295      * @return {Number} the index, or -1 if not found
4296      */
4297     
4298     findColumnIndex : function(dataIndex){
4299         for(var i = 0, len = this.config.length; i < len; i++){
4300             if(this.config[i].dataIndex == dataIndex){
4301                 return i;
4302             }
4303         }
4304         return -1;
4305     },
4306     
4307     
4308     moveColumn : function(oldIndex, newIndex){
4309         var c = this.config[oldIndex];
4310         this.config.splice(oldIndex, 1);
4311         this.config.splice(newIndex, 0, c);
4312         this.dataMap = null;
4313         this.fireEvent("columnmoved", this, oldIndex, newIndex);
4314     },
4315
4316     isLocked : function(colIndex){
4317         return this.config[colIndex].locked === true;
4318     },
4319
4320     setLocked : function(colIndex, value, suppressEvent){
4321         if(this.isLocked(colIndex) == value){
4322             return;
4323         }
4324         this.config[colIndex].locked = value;
4325         if(!suppressEvent){
4326             this.fireEvent("columnlockchange", this, colIndex, value);
4327         }
4328     },
4329
4330     getTotalLockedWidth : function(){
4331         var totalWidth = 0;
4332         for(var i = 0; i < this.config.length; i++){
4333             if(this.isLocked(i) && !this.isHidden(i)){
4334                 this.totalWidth += this.getColumnWidth(i);
4335             }
4336         }
4337         return totalWidth;
4338     },
4339
4340     getLockedCount : function(){
4341         for(var i = 0, len = this.config.length; i < len; i++){
4342             if(!this.isLocked(i)){
4343                 return i;
4344             }
4345         }
4346     },
4347
4348     /**
4349      * Returns the number of columns.
4350      * @return {Number}
4351      */
4352     getColumnCount : function(visibleOnly){
4353         if(visibleOnly === true){
4354             var c = 0;
4355             for(var i = 0, len = this.config.length; i < len; i++){
4356                 if(!this.isHidden(i)){
4357                     c++;
4358                 }
4359             }
4360             return c;
4361         }
4362         return this.config.length;
4363     },
4364
4365     /**
4366      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
4367      * @param {Function} fn
4368      * @param {Object} scope (optional)
4369      * @return {Array} result
4370      */
4371     getColumnsBy : function(fn, scope){
4372         var r = [];
4373         for(var i = 0, len = this.config.length; i < len; i++){
4374             var c = this.config[i];
4375             if(fn.call(scope||this, c, i) === true){
4376                 r[r.length] = c;
4377             }
4378         }
4379         return r;
4380     },
4381
4382     /**
4383      * Returns true if the specified column is sortable.
4384      * @param {Number} col The column index
4385      * @return {Boolean}
4386      */
4387     isSortable : function(col){
4388         if(typeof this.config[col].sortable == "undefined"){
4389             return this.defaultSortable;
4390         }
4391         return this.config[col].sortable;
4392     },
4393
4394     /**
4395      * Returns the rendering (formatting) function defined for the column.
4396      * @param {Number} col The column index.
4397      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
4398      */
4399     getRenderer : function(col){
4400         if(!this.config[col].renderer){
4401             return Roo.grid.ColumnModel.defaultRenderer;
4402         }
4403         return this.config[col].renderer;
4404     },
4405
4406     /**
4407      * Sets the rendering (formatting) function for a column.
4408      * @param {Number} col The column index
4409      * @param {Function} fn The function to use to process the cell's raw data
4410      * to return HTML markup for the grid view. The render function is called with
4411      * the following parameters:<ul>
4412      * <li>Data value.</li>
4413      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
4414      * <li>css A CSS style string to apply to the table cell.</li>
4415      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
4416      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
4417      * <li>Row index</li>
4418      * <li>Column index</li>
4419      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
4420      */
4421     setRenderer : function(col, fn){
4422         this.config[col].renderer = fn;
4423     },
4424
4425     /**
4426      * Returns the width for the specified column.
4427      * @param {Number} col The column index
4428      * @return {Number}
4429      */
4430     getColumnWidth : function(col){
4431         return this.config[col].width * 1 || this.defaultWidth;
4432     },
4433
4434     /**
4435      * Sets the width for a column.
4436      * @param {Number} col The column index
4437      * @param {Number} width The new width
4438      */
4439     setColumnWidth : function(col, width, suppressEvent){
4440         this.config[col].width = width;
4441         this.totalWidth = null;
4442         if(!suppressEvent){
4443              this.fireEvent("widthchange", this, col, width);
4444         }
4445     },
4446
4447     /**
4448      * Returns the total width of all columns.
4449      * @param {Boolean} includeHidden True to include hidden column widths
4450      * @return {Number}
4451      */
4452     getTotalWidth : function(includeHidden){
4453         if(!this.totalWidth){
4454             this.totalWidth = 0;
4455             for(var i = 0, len = this.config.length; i < len; i++){
4456                 if(includeHidden || !this.isHidden(i)){
4457                     this.totalWidth += this.getColumnWidth(i);
4458                 }
4459             }
4460         }
4461         return this.totalWidth;
4462     },
4463
4464     /**
4465      * Returns the header for the specified column.
4466      * @param {Number} col The column index
4467      * @return {String}
4468      */
4469     getColumnHeader : function(col){
4470         return this.config[col].header;
4471     },
4472
4473     /**
4474      * Sets the header for a column.
4475      * @param {Number} col The column index
4476      * @param {String} header The new header
4477      */
4478     setColumnHeader : function(col, header){
4479         this.config[col].header = header;
4480         this.fireEvent("headerchange", this, col, header);
4481     },
4482
4483     /**
4484      * Returns the tooltip for the specified column.
4485      * @param {Number} col The column index
4486      * @return {String}
4487      */
4488     getColumnTooltip : function(col){
4489             return this.config[col].tooltip;
4490     },
4491     /**
4492      * Sets the tooltip for a column.
4493      * @param {Number} col The column index
4494      * @param {String} tooltip The new tooltip
4495      */
4496     setColumnTooltip : function(col, tooltip){
4497             this.config[col].tooltip = tooltip;
4498     },
4499
4500     /**
4501      * Returns the dataIndex for the specified column.
4502      * @param {Number} col The column index
4503      * @return {Number}
4504      */
4505     getDataIndex : function(col){
4506         return this.config[col].dataIndex;
4507     },
4508
4509     /**
4510      * Sets the dataIndex for a column.
4511      * @param {Number} col The column index
4512      * @param {Number} dataIndex The new dataIndex
4513      */
4514     setDataIndex : function(col, dataIndex){
4515         this.config[col].dataIndex = dataIndex;
4516     },
4517
4518     
4519     
4520     /**
4521      * Returns true if the cell is editable.
4522      * @param {Number} colIndex The column index
4523      * @param {Number} rowIndex The row index
4524      * @return {Boolean}
4525      */
4526     isCellEditable : function(colIndex, rowIndex){
4527         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
4528     },
4529
4530     /**
4531      * Returns the editor defined for the cell/column.
4532      * return false or null to disable editing.
4533      * @param {Number} colIndex The column index
4534      * @param {Number} rowIndex The row index
4535      * @return {Object}
4536      */
4537     getCellEditor : function(colIndex, rowIndex){
4538         return this.config[colIndex].editor;
4539     },
4540
4541     /**
4542      * Sets if a column is editable.
4543      * @param {Number} col The column index
4544      * @param {Boolean} editable True if the column is editable
4545      */
4546     setEditable : function(col, editable){
4547         this.config[col].editable = editable;
4548     },
4549
4550
4551     /**
4552      * Returns true if the column is hidden.
4553      * @param {Number} colIndex The column index
4554      * @return {Boolean}
4555      */
4556     isHidden : function(colIndex){
4557         return this.config[colIndex].hidden;
4558     },
4559
4560
4561     /**
4562      * Returns true if the column width cannot be changed
4563      */
4564     isFixed : function(colIndex){
4565         return this.config[colIndex].fixed;
4566     },
4567
4568     /**
4569      * Returns true if the column can be resized
4570      * @return {Boolean}
4571      */
4572     isResizable : function(colIndex){
4573         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
4574     },
4575     /**
4576      * Sets if a column is hidden.
4577      * @param {Number} colIndex The column index
4578      * @param {Boolean} hidden True if the column is hidden
4579      */
4580     setHidden : function(colIndex, hidden){
4581         this.config[colIndex].hidden = hidden;
4582         this.totalWidth = null;
4583         this.fireEvent("hiddenchange", this, colIndex, hidden);
4584     },
4585
4586     /**
4587      * Sets the editor for a column.
4588      * @param {Number} col The column index
4589      * @param {Object} editor The editor object
4590      */
4591     setEditor : function(col, editor){
4592         this.config[col].editor = editor;
4593     }
4594 });
4595
4596 Roo.grid.ColumnModel.defaultRenderer = function(value){
4597         if(typeof value == "string" && value.length < 1){
4598             return "&#160;";
4599         }
4600         return value;
4601 };
4602
4603 // Alias for backwards compatibility
4604 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
4605 /*
4606  * Based on:
4607  * Ext JS Library 1.1.1
4608  * Copyright(c) 2006-2007, Ext JS, LLC.
4609  *
4610  * Originally Released Under LGPL - original licence link has changed is not relivant.
4611  *
4612  * Fork - LGPL
4613  * <script type="text/javascript">
4614  */
4615  
4616 /**
4617  * @class Roo.LoadMask
4618  * A simple utility class for generically masking elements while loading data.  If the element being masked has
4619  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
4620  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
4621  * element's UpdateManager load indicator and will be destroyed after the initial load.
4622  * @constructor
4623  * Create a new LoadMask
4624  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
4625  * @param {Object} config The config object
4626  */
4627 Roo.LoadMask = function(el, config){
4628     this.el = Roo.get(el);
4629     Roo.apply(this, config);
4630     if(this.store){
4631         this.store.on('beforeload', this.onBeforeLoad, this);
4632         this.store.on('load', this.onLoad, this);
4633         this.store.on('loadexception', this.onLoadException, this);
4634         this.removeMask = false;
4635     }else{
4636         var um = this.el.getUpdateManager();
4637         um.showLoadIndicator = false; // disable the default indicator
4638         um.on('beforeupdate', this.onBeforeLoad, this);
4639         um.on('update', this.onLoad, this);
4640         um.on('failure', this.onLoad, this);
4641         this.removeMask = true;
4642     }
4643 };
4644
4645 Roo.LoadMask.prototype = {
4646     /**
4647      * @cfg {Boolean} removeMask
4648      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
4649      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
4650      */
4651     /**
4652      * @cfg {String} msg
4653      * The text to display in a centered loading message box (defaults to 'Loading...')
4654      */
4655     msg : 'Loading...',
4656     /**
4657      * @cfg {String} msgCls
4658      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
4659      */
4660     msgCls : 'x-mask-loading',
4661
4662     /**
4663      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
4664      * @type Boolean
4665      */
4666     disabled: false,
4667
4668     /**
4669      * Disables the mask to prevent it from being displayed
4670      */
4671     disable : function(){
4672        this.disabled = true;
4673     },
4674
4675     /**
4676      * Enables the mask so that it can be displayed
4677      */
4678     enable : function(){
4679         this.disabled = false;
4680     },
4681     
4682     onLoadException : function()
4683     {
4684         Roo.log(arguments);
4685         
4686         if (typeof(arguments[3]) != 'undefined') {
4687             Roo.MessageBox.alert("Error loading",arguments[3]);
4688         } 
4689         /*
4690         try {
4691             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
4692                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
4693             }   
4694         } catch(e) {
4695             
4696         }
4697         */
4698     
4699         
4700         
4701         this.el.unmask(this.removeMask);
4702     },
4703     // private
4704     onLoad : function()
4705     {
4706         this.el.unmask(this.removeMask);
4707     },
4708
4709     // private
4710     onBeforeLoad : function(){
4711         if(!this.disabled){
4712             this.el.mask(this.msg, this.msgCls);
4713         }
4714     },
4715
4716     // private
4717     destroy : function(){
4718         if(this.store){
4719             this.store.un('beforeload', this.onBeforeLoad, this);
4720             this.store.un('load', this.onLoad, this);
4721             this.store.un('loadexception', this.onLoadException, this);
4722         }else{
4723             var um = this.el.getUpdateManager();
4724             um.un('beforeupdate', this.onBeforeLoad, this);
4725             um.un('update', this.onLoad, this);
4726             um.un('failure', this.onLoad, this);
4727         }
4728     }
4729 };/*
4730  * - LGPL
4731  *
4732  * table
4733  * 
4734  */
4735
4736 /**
4737  * @class Roo.bootstrap.Table
4738  * @extends Roo.bootstrap.Component
4739  * Bootstrap Table class
4740  * @cfg {String} cls table class
4741  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
4742  * @cfg {String} bgcolor Specifies the background color for a table
4743  * @cfg {Number} border Specifies whether the table cells should have borders or not
4744  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
4745  * @cfg {Number} cellspacing Specifies the space between cells
4746  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
4747  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
4748  * @cfg {String} sortable Specifies that the table should be sortable
4749  * @cfg {String} summary Specifies a summary of the content of a table
4750  * @cfg {Number} width Specifies the width of a table
4751  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
4752  * 
4753  * @cfg {boolean} striped Should the rows be alternative striped
4754  * @cfg {boolean} bordered Add borders to the table
4755  * @cfg {boolean} hover Add hover highlighting
4756  * @cfg {boolean} condensed Format condensed
4757  * @cfg {boolean} responsive Format condensed
4758  * @cfg {Boolean} loadMask (true|false) default false
4759  * @cfg {Boolean} tfoot (true|false) generate tfoot, default true
4760  * @cfg {Boolean} thead (true|false) generate thead, default true
4761  * @cfg {Boolean} RowSelection (true|false) default false
4762  * @cfg {Boolean} CellSelection (true|false) default false
4763  *
4764  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
4765  
4766  * 
4767  * @constructor
4768  * Create a new Table
4769  * @param {Object} config The config object
4770  */
4771
4772 Roo.bootstrap.Table = function(config){
4773     Roo.bootstrap.Table.superclass.constructor.call(this, config);
4774     
4775     if (this.sm) {
4776         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
4777         this.sm = this.selModel;
4778         this.sm.xmodule = this.xmodule || false;
4779     }
4780     if (this.cm && typeof(this.cm.config) == 'undefined') {
4781         this.colModel = new Roo.grid.ColumnModel(this.cm);
4782         this.cm = this.colModel;
4783         this.cm.xmodule = this.xmodule || false;
4784     }
4785     if (this.store) {
4786         this.store= Roo.factory(this.store, Roo.data);
4787         this.ds = this.store;
4788         this.ds.xmodule = this.xmodule || false;
4789          
4790     }
4791     if (this.footer && this.store) {
4792         this.footer.dataSource = this.ds;
4793         this.footer = Roo.factory(this.footer);
4794     }
4795     
4796     /** @private */
4797     this.addEvents({
4798         /**
4799          * @event cellclick
4800          * Fires when a cell is clicked
4801          * @param {Roo.bootstrap.Table} this
4802          * @param {Roo.Element} el
4803          * @param {Number} rowIndex
4804          * @param {Number} columnIndex
4805          * @param {Roo.EventObject} e
4806          */
4807         "cellclick" : true,
4808         /**
4809          * @event celldblclick
4810          * Fires when a cell is double clicked
4811          * @param {Roo.bootstrap.Table} this
4812          * @param {Roo.Element} el
4813          * @param {Number} rowIndex
4814          * @param {Number} columnIndex
4815          * @param {Roo.EventObject} e
4816          */
4817         "celldblclick" : true,
4818         /**
4819          * @event rowclick
4820          * Fires when a row is clicked
4821          * @param {Roo.bootstrap.Table} this
4822          * @param {Roo.Element} el
4823          * @param {Number} rowIndex
4824          * @param {Roo.EventObject} e
4825          */
4826         "rowclick" : true,
4827         /**
4828          * @event rowdblclick
4829          * Fires when a row is double clicked
4830          * @param {Roo.bootstrap.Table} this
4831          * @param {Roo.Element} el
4832          * @param {Number} rowIndex
4833          * @param {Roo.EventObject} e
4834          */
4835         "rowdblclick" : true,
4836         /**
4837          * @event mouseover
4838          * Fires when a mouseover occur
4839          * @param {Roo.bootstrap.Table} this
4840          * @param {Roo.Element} el
4841          * @param {Number} rowIndex
4842          * @param {Number} columnIndex
4843          * @param {Roo.EventObject} e
4844          */
4845         "mouseover" : true,
4846         /**
4847          * @event mouseout
4848          * Fires when a mouseout occur
4849          * @param {Roo.bootstrap.Table} this
4850          * @param {Roo.Element} el
4851          * @param {Number} rowIndex
4852          * @param {Number} columnIndex
4853          * @param {Roo.EventObject} e
4854          */
4855         "mouseout" : true,
4856         /**
4857          * @event rowclass
4858          * Fires when a row is rendered, so you can change add a style to it.
4859          * @param {Roo.bootstrap.Table} this
4860          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
4861          */
4862         'rowclass' : true
4863         
4864     });
4865 };
4866
4867 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
4868     
4869     cls: false,
4870     align: false,
4871     bgcolor: false,
4872     border: false,
4873     cellpadding: false,
4874     cellspacing: false,
4875     frame: false,
4876     rules: false,
4877     sortable: false,
4878     summary: false,
4879     width: false,
4880     striped : false,
4881     bordered: false,
4882     hover:  false,
4883     condensed : false,
4884     responsive : false,
4885     sm : false,
4886     cm : false,
4887     store : false,
4888     loadMask : false,
4889     tfoot : true,
4890     thead : true,
4891     RowSelection : false,
4892     CellSelection : false,
4893     layout : false,
4894     
4895     // Roo.Element - the tbody
4896     mainBody: false, 
4897     
4898     getAutoCreate : function(){
4899         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
4900         
4901         cfg = {
4902             tag: 'table',
4903             cls : 'table',
4904             cn : []
4905         }
4906             
4907         if (this.striped) {
4908             cfg.cls += ' table-striped';
4909         }
4910         
4911         if (this.hover) {
4912             cfg.cls += ' table-hover';
4913         }
4914         if (this.bordered) {
4915             cfg.cls += ' table-bordered';
4916         }
4917         if (this.condensed) {
4918             cfg.cls += ' table-condensed';
4919         }
4920         if (this.responsive) {
4921             cfg.cls += ' table-responsive';
4922         }
4923         
4924         if (this.cls) {
4925             cfg.cls+=  ' ' +this.cls;
4926         }
4927         
4928         // this lot should be simplifed...
4929         
4930         if (this.align) {
4931             cfg.align=this.align;
4932         }
4933         if (this.bgcolor) {
4934             cfg.bgcolor=this.bgcolor;
4935         }
4936         if (this.border) {
4937             cfg.border=this.border;
4938         }
4939         if (this.cellpadding) {
4940             cfg.cellpadding=this.cellpadding;
4941         }
4942         if (this.cellspacing) {
4943             cfg.cellspacing=this.cellspacing;
4944         }
4945         if (this.frame) {
4946             cfg.frame=this.frame;
4947         }
4948         if (this.rules) {
4949             cfg.rules=this.rules;
4950         }
4951         if (this.sortable) {
4952             cfg.sortable=this.sortable;
4953         }
4954         if (this.summary) {
4955             cfg.summary=this.summary;
4956         }
4957         if (this.width) {
4958             cfg.width=this.width;
4959         }
4960         if (this.layout) {
4961             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
4962         }
4963         
4964         if(this.store || this.cm){
4965             if(this.thead){
4966                 cfg.cn.push(this.renderHeader());
4967             }
4968             
4969             cfg.cn.push(this.renderBody());
4970             
4971             if(this.tfoot){
4972                 cfg.cn.push(this.renderFooter());
4973             }
4974             
4975             cfg.cls+=  ' TableGrid';
4976         }
4977         
4978         return { cn : [ cfg ] };
4979     },
4980     
4981     initEvents : function()
4982     {   
4983         if(!this.store || !this.cm){
4984             return;
4985         }
4986         
4987         //Roo.log('initEvents with ds!!!!');
4988         
4989         this.mainBody = this.el.select('tbody', true).first();
4990         
4991         
4992         var _this = this;
4993         
4994         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
4995             e.on('click', _this.sort, _this);
4996         });
4997         
4998         this.el.on("click", this.onClick, this);
4999         this.el.on("dblclick", this.onDblClick, this);
5000         
5001         this.parent().el.setStyle('position', 'relative');
5002         if (this.footer) {
5003             this.footer.parentId = this.id;
5004             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
5005         }
5006         
5007         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5008         
5009         this.store.on('load', this.onLoad, this);
5010         this.store.on('beforeload', this.onBeforeLoad, this);
5011         this.store.on('update', this.onUpdate, this);
5012         
5013     },
5014     
5015     onMouseover : function(e, el)
5016     {
5017         var cell = Roo.get(el);
5018         
5019         if(!cell){
5020             return;
5021         }
5022         
5023         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5024             cell = cell.findParent('td', false, true);
5025         }
5026         
5027         var row = cell.findParent('tr', false, true);
5028         var cellIndex = cell.dom.cellIndex;
5029         var rowIndex = row.dom.rowIndex - 1; // start from 0
5030         
5031         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5032         
5033     },
5034     
5035     onMouseout : function(e, el)
5036     {
5037         var cell = Roo.get(el);
5038         
5039         if(!cell){
5040             return;
5041         }
5042         
5043         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5044             cell = cell.findParent('td', false, true);
5045         }
5046         
5047         var row = cell.findParent('tr', false, true);
5048         var cellIndex = cell.dom.cellIndex;
5049         var rowIndex = row.dom.rowIndex - 1; // start from 0
5050         
5051         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5052         
5053     },
5054     
5055     onClick : function(e, el)
5056     {
5057         var cell = Roo.get(el);
5058         
5059         if(!cell || (!this.CellSelection && !this.RowSelection)){
5060             return;
5061         }
5062         
5063         
5064         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5065             cell = cell.findParent('td', false, true);
5066         }
5067         
5068         var row = cell.findParent('tr', false, true);
5069         var cellIndex = cell.dom.cellIndex;
5070         var rowIndex = row.dom.rowIndex - 1;
5071         
5072         if(this.CellSelection){
5073             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5074         }
5075         
5076         if(this.RowSelection){
5077             this.fireEvent('rowclick', this, row, rowIndex, e);
5078         }
5079         
5080         
5081     },
5082     
5083     onDblClick : function(e,el)
5084     {
5085         var cell = Roo.get(el);
5086         
5087         if(!cell || (!this.CellSelection && !this.RowSelection)){
5088             return;
5089         }
5090         
5091         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5092             cell = cell.findParent('td', false, true);
5093         }
5094         
5095         var row = cell.findParent('tr', false, true);
5096         var cellIndex = cell.dom.cellIndex;
5097         var rowIndex = row.dom.rowIndex - 1;
5098         
5099         if(this.CellSelection){
5100             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5101         }
5102         
5103         if(this.RowSelection){
5104             this.fireEvent('rowdblclick', this, row, rowIndex, e);
5105         }
5106     },
5107     
5108     sort : function(e,el)
5109     {
5110         var col = Roo.get(el)
5111         
5112         if(!col.hasClass('sortable')){
5113             return;
5114         }
5115         
5116         var sort = col.attr('sort');
5117         var dir = 'ASC';
5118         
5119         if(col.hasClass('glyphicon-arrow-up')){
5120             dir = 'DESC';
5121         }
5122         
5123         this.store.sortInfo = {field : sort, direction : dir};
5124         
5125         if (this.footer) {
5126             Roo.log("calling footer first");
5127             this.footer.onClick('first');
5128         } else {
5129         
5130             this.store.load({ params : { start : 0 } });
5131         }
5132     },
5133     
5134     renderHeader : function()
5135     {
5136         var header = {
5137             tag: 'thead',
5138             cn : []
5139         };
5140         
5141         var cm = this.cm;
5142         
5143         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5144             
5145             var config = cm.config[i];
5146                     
5147             var c = {
5148                 tag: 'th',
5149                 style : '',
5150                 html: cm.getColumnHeader(i)
5151             };
5152             
5153             if(typeof(config.hidden) != 'undefined' && config.hidden){
5154                 c.style += ' display:none;';
5155             }
5156             
5157             if(typeof(config.dataIndex) != 'undefined'){
5158                 c.sort = config.dataIndex;
5159             }
5160             
5161             if(typeof(config.sortable) != 'undefined' && config.sortable){
5162                 c.cls = 'sortable';
5163             }
5164             
5165             if(typeof(config.align) != 'undefined' && config.align.length){
5166                 c.style += ' text-align:' + config.align + ';';
5167             }
5168             
5169             if(typeof(config.width) != 'undefined'){
5170                 c.style += ' width:' + config.width + 'px;';
5171             }
5172             
5173             header.cn.push(c)
5174         }
5175         
5176         return header;
5177     },
5178     
5179     renderBody : function()
5180     {
5181         var body = {
5182             tag: 'tbody',
5183             cn : [
5184                 {
5185                     tag: 'tr',
5186                     cn : [
5187                         {
5188                             tag : 'td',
5189                             colspan :  this.cm.getColumnCount()
5190                         }
5191                     ]
5192                 }
5193             ]
5194         };
5195         
5196         return body;
5197     },
5198     
5199     renderFooter : function()
5200     {
5201         var footer = {
5202             tag: 'tfoot',
5203             cn : [
5204                 {
5205                     tag: 'tr',
5206                     cn : [
5207                         {
5208                             tag : 'td',
5209                             colspan :  this.cm.getColumnCount()
5210                         }
5211                     ]
5212                 }
5213             ]
5214         };
5215         
5216         return footer;
5217     },
5218     
5219     
5220     
5221     onLoad : function()
5222     {
5223         Roo.log('ds onload');
5224         this.clear();
5225         
5226         var _this = this;
5227         var cm = this.cm;
5228         var ds = this.store;
5229         
5230         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5231             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
5232             
5233             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
5234                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
5235             }
5236             
5237             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
5238                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
5239             }
5240         });
5241         
5242         var tbody =  this.mainBody;
5243               
5244         if(ds.getCount() > 0){
5245             ds.data.each(function(d,rowIndex){
5246                 var row =  this.renderRow(cm, ds, rowIndex);
5247                 
5248                 tbody.createChild(row);
5249                 
5250                 var _this = this;
5251                 
5252                 if(row.cellObjects.length){
5253                     Roo.each(row.cellObjects, function(r){
5254                         _this.renderCellObject(r);
5255                     })
5256                 }
5257                 
5258             }, this);
5259         }
5260         
5261         Roo.each(this.el.select('tbody td', true).elements, function(e){
5262             e.on('mouseover', _this.onMouseover, _this);
5263         });
5264         
5265         Roo.each(this.el.select('tbody td', true).elements, function(e){
5266             e.on('mouseout', _this.onMouseout, _this);
5267         });
5268
5269         //if(this.loadMask){
5270         //    this.maskEl.hide();
5271         //}
5272     },
5273     
5274     
5275     onUpdate : function(ds,record)
5276     {
5277         this.refreshRow(record);
5278     },
5279     onRemove : function(ds, record, index, isUpdate){
5280         if(isUpdate !== true){
5281             this.fireEvent("beforerowremoved", this, index, record);
5282         }
5283         var bt = this.mainBody.dom;
5284         if(bt.rows[index]){
5285             bt.removeChild(bt.rows[index]);
5286         }
5287         
5288         if(isUpdate !== true){
5289             //this.stripeRows(index);
5290             //this.syncRowHeights(index, index);
5291             //this.layout();
5292             this.fireEvent("rowremoved", this, index, record);
5293         }
5294     },
5295     
5296     
5297     refreshRow : function(record){
5298         var ds = this.store, index;
5299         if(typeof record == 'number'){
5300             index = record;
5301             record = ds.getAt(index);
5302         }else{
5303             index = ds.indexOf(record);
5304         }
5305         this.insertRow(ds, index, true);
5306         this.onRemove(ds, record, index+1, true);
5307         //this.syncRowHeights(index, index);
5308         //this.layout();
5309         this.fireEvent("rowupdated", this, index, record);
5310     },
5311     
5312     insertRow : function(dm, rowIndex, isUpdate){
5313         
5314         if(!isUpdate){
5315             this.fireEvent("beforerowsinserted", this, rowIndex);
5316         }
5317             //var s = this.getScrollState();
5318         var row = this.renderRow(this.cm, this.store, rowIndex);
5319         // insert before rowIndex..
5320         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
5321         
5322         var _this = this;
5323                 
5324         if(row.cellObjects.length){
5325             Roo.each(row.cellObjects, function(r){
5326                 _this.renderCellObject(r);
5327             })
5328         }
5329             
5330         if(!isUpdate){
5331             this.fireEvent("rowsinserted", this, rowIndex);
5332             //this.syncRowHeights(firstRow, lastRow);
5333             //this.stripeRows(firstRow);
5334             //this.layout();
5335         }
5336         
5337     },
5338     
5339     
5340     getRowDom : function(rowIndex)
5341     {
5342         // not sure if I need to check this.. but let's do it anyway..
5343         return (this.mainBody.dom.rows && (rowIndex-1) < this.mainBody.dom.rows.length ) ?
5344                 this.mainBody.dom.rows[rowIndex] : false
5345     },
5346     // returns the object tree for a tr..
5347   
5348     
5349     renderRow : function(cm, ds, rowIndex) {
5350         
5351         var d = ds.getAt(rowIndex);
5352         
5353         var row = {
5354             tag : 'tr',
5355             cn : []
5356         };
5357             
5358         var cellObjects = [];
5359         
5360         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5361             var config = cm.config[i];
5362             
5363             var renderer = cm.getRenderer(i);
5364             var value = '';
5365             var id = false;
5366             
5367             if(typeof(renderer) !== 'undefined'){
5368                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
5369             }
5370             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
5371             // and are rendered into the cells after the row is rendered - using the id for the element.
5372             
5373             if(typeof(value) === 'object'){
5374                 id = Roo.id();
5375                 cellObjects.push({
5376                     container : id,
5377                     cfg : value 
5378                 })
5379             }
5380             
5381             var rowcfg = {
5382                 record: d,
5383                 rowIndex : rowIndex,
5384                 colIndex : i,
5385                 rowClass : ''
5386             }
5387
5388             this.fireEvent('rowclass', this, rowcfg);
5389             
5390             var td = {
5391                 tag: 'td',
5392                 cls : rowcfg.rowClass,
5393                 style: '',
5394                 html: (typeof(value) === 'object') ? '' : value
5395             };
5396             
5397             if (id) {
5398                 td.id = id;
5399             }
5400             
5401             if(typeof(config.hidden) != 'undefined' && config.hidden){
5402                 td.style += ' display:none;';
5403             }
5404             
5405             if(typeof(config.align) != 'undefined' && config.align.length){
5406                 td.style += ' text-align:' + config.align + ';';
5407             }
5408             
5409             if(typeof(config.width) != 'undefined'){
5410                 td.style += ' width:' +  config.width + 'px;';
5411             }
5412              
5413             row.cn.push(td);
5414            
5415         }
5416         
5417         row.cellObjects = cellObjects;
5418         
5419         return row;
5420           
5421     },
5422     
5423     
5424     
5425     onBeforeLoad : function()
5426     {
5427         //Roo.log('ds onBeforeLoad');
5428         
5429         //this.clear();
5430         
5431         //if(this.loadMask){
5432         //    this.maskEl.show();
5433         //}
5434     },
5435     
5436     clear : function()
5437     {
5438         this.el.select('tbody', true).first().dom.innerHTML = '';
5439     },
5440     
5441     getSelectionModel : function(){
5442         if(!this.selModel){
5443             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
5444         }
5445         return this.selModel;
5446     },
5447     /*
5448      * Render the Roo.bootstrap object from renderder
5449      */
5450     renderCellObject : function(r)
5451     {
5452         var _this = this;
5453         
5454         var t = r.cfg.render(r.container);
5455         
5456         if(r.cfg.cn){
5457             Roo.each(r.cfg.cn, function(c){
5458                 var child = {
5459                     container: t.getChildContainer(),
5460                     cfg: c
5461                 }
5462                 _this.renderCellObject(child);
5463             })
5464         }
5465     }
5466    
5467 });
5468
5469  
5470
5471  /*
5472  * - LGPL
5473  *
5474  * table cell
5475  * 
5476  */
5477
5478 /**
5479  * @class Roo.bootstrap.TableCell
5480  * @extends Roo.bootstrap.Component
5481  * Bootstrap TableCell class
5482  * @cfg {String} html cell contain text
5483  * @cfg {String} cls cell class
5484  * @cfg {String} tag cell tag (td|th) default td
5485  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
5486  * @cfg {String} align Aligns the content in a cell
5487  * @cfg {String} axis Categorizes cells
5488  * @cfg {String} bgcolor Specifies the background color of a cell
5489  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5490  * @cfg {Number} colspan Specifies the number of columns a cell should span
5491  * @cfg {String} headers Specifies one or more header cells a cell is related to
5492  * @cfg {Number} height Sets the height of a cell
5493  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
5494  * @cfg {Number} rowspan Sets the number of rows a cell should span
5495  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
5496  * @cfg {String} valign Vertical aligns the content in a cell
5497  * @cfg {Number} width Specifies the width of a cell
5498  * 
5499  * @constructor
5500  * Create a new TableCell
5501  * @param {Object} config The config object
5502  */
5503
5504 Roo.bootstrap.TableCell = function(config){
5505     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
5506 };
5507
5508 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
5509     
5510     html: false,
5511     cls: false,
5512     tag: false,
5513     abbr: false,
5514     align: false,
5515     axis: false,
5516     bgcolor: false,
5517     charoff: false,
5518     colspan: false,
5519     headers: false,
5520     height: false,
5521     nowrap: false,
5522     rowspan: false,
5523     scope: false,
5524     valign: false,
5525     width: false,
5526     
5527     
5528     getAutoCreate : function(){
5529         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
5530         
5531         cfg = {
5532             tag: 'td'
5533         }
5534         
5535         if(this.tag){
5536             cfg.tag = this.tag;
5537         }
5538         
5539         if (this.html) {
5540             cfg.html=this.html
5541         }
5542         if (this.cls) {
5543             cfg.cls=this.cls
5544         }
5545         if (this.abbr) {
5546             cfg.abbr=this.abbr
5547         }
5548         if (this.align) {
5549             cfg.align=this.align
5550         }
5551         if (this.axis) {
5552             cfg.axis=this.axis
5553         }
5554         if (this.bgcolor) {
5555             cfg.bgcolor=this.bgcolor
5556         }
5557         if (this.charoff) {
5558             cfg.charoff=this.charoff
5559         }
5560         if (this.colspan) {
5561             cfg.colspan=this.colspan
5562         }
5563         if (this.headers) {
5564             cfg.headers=this.headers
5565         }
5566         if (this.height) {
5567             cfg.height=this.height
5568         }
5569         if (this.nowrap) {
5570             cfg.nowrap=this.nowrap
5571         }
5572         if (this.rowspan) {
5573             cfg.rowspan=this.rowspan
5574         }
5575         if (this.scope) {
5576             cfg.scope=this.scope
5577         }
5578         if (this.valign) {
5579             cfg.valign=this.valign
5580         }
5581         if (this.width) {
5582             cfg.width=this.width
5583         }
5584         
5585         
5586         return cfg;
5587     }
5588    
5589 });
5590
5591  
5592
5593  /*
5594  * - LGPL
5595  *
5596  * table row
5597  * 
5598  */
5599
5600 /**
5601  * @class Roo.bootstrap.TableRow
5602  * @extends Roo.bootstrap.Component
5603  * Bootstrap TableRow class
5604  * @cfg {String} cls row class
5605  * @cfg {String} align Aligns the content in a table row
5606  * @cfg {String} bgcolor Specifies a background color for a table row
5607  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5608  * @cfg {String} valign Vertical aligns the content in a table row
5609  * 
5610  * @constructor
5611  * Create a new TableRow
5612  * @param {Object} config The config object
5613  */
5614
5615 Roo.bootstrap.TableRow = function(config){
5616     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
5617 };
5618
5619 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
5620     
5621     cls: false,
5622     align: false,
5623     bgcolor: false,
5624     charoff: false,
5625     valign: false,
5626     
5627     getAutoCreate : function(){
5628         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
5629         
5630         cfg = {
5631             tag: 'tr'
5632         }
5633             
5634         if(this.cls){
5635             cfg.cls = this.cls;
5636         }
5637         if(this.align){
5638             cfg.align = this.align;
5639         }
5640         if(this.bgcolor){
5641             cfg.bgcolor = this.bgcolor;
5642         }
5643         if(this.charoff){
5644             cfg.charoff = this.charoff;
5645         }
5646         if(this.valign){
5647             cfg.valign = this.valign;
5648         }
5649         
5650         return cfg;
5651     }
5652    
5653 });
5654
5655  
5656
5657  /*
5658  * - LGPL
5659  *
5660  * table body
5661  * 
5662  */
5663
5664 /**
5665  * @class Roo.bootstrap.TableBody
5666  * @extends Roo.bootstrap.Component
5667  * Bootstrap TableBody class
5668  * @cfg {String} cls element class
5669  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
5670  * @cfg {String} align Aligns the content inside the element
5671  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
5672  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
5673  * 
5674  * @constructor
5675  * Create a new TableBody
5676  * @param {Object} config The config object
5677  */
5678
5679 Roo.bootstrap.TableBody = function(config){
5680     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
5681 };
5682
5683 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
5684     
5685     cls: false,
5686     tag: false,
5687     align: false,
5688     charoff: false,
5689     valign: false,
5690     
5691     getAutoCreate : function(){
5692         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
5693         
5694         cfg = {
5695             tag: 'tbody'
5696         }
5697             
5698         if (this.cls) {
5699             cfg.cls=this.cls
5700         }
5701         if(this.tag){
5702             cfg.tag = this.tag;
5703         }
5704         
5705         if(this.align){
5706             cfg.align = this.align;
5707         }
5708         if(this.charoff){
5709             cfg.charoff = this.charoff;
5710         }
5711         if(this.valign){
5712             cfg.valign = this.valign;
5713         }
5714         
5715         return cfg;
5716     }
5717     
5718     
5719 //    initEvents : function()
5720 //    {
5721 //        
5722 //        if(!this.store){
5723 //            return;
5724 //        }
5725 //        
5726 //        this.store = Roo.factory(this.store, Roo.data);
5727 //        this.store.on('load', this.onLoad, this);
5728 //        
5729 //        this.store.load();
5730 //        
5731 //    },
5732 //    
5733 //    onLoad: function () 
5734 //    {   
5735 //        this.fireEvent('load', this);
5736 //    }
5737 //    
5738 //   
5739 });
5740
5741  
5742
5743  /*
5744  * Based on:
5745  * Ext JS Library 1.1.1
5746  * Copyright(c) 2006-2007, Ext JS, LLC.
5747  *
5748  * Originally Released Under LGPL - original licence link has changed is not relivant.
5749  *
5750  * Fork - LGPL
5751  * <script type="text/javascript">
5752  */
5753
5754 // as we use this in bootstrap.
5755 Roo.namespace('Roo.form');
5756  /**
5757  * @class Roo.form.Action
5758  * Internal Class used to handle form actions
5759  * @constructor
5760  * @param {Roo.form.BasicForm} el The form element or its id
5761  * @param {Object} config Configuration options
5762  */
5763
5764  
5765  
5766 // define the action interface
5767 Roo.form.Action = function(form, options){
5768     this.form = form;
5769     this.options = options || {};
5770 };
5771 /**
5772  * Client Validation Failed
5773  * @const 
5774  */
5775 Roo.form.Action.CLIENT_INVALID = 'client';
5776 /**
5777  * Server Validation Failed
5778  * @const 
5779  */
5780 Roo.form.Action.SERVER_INVALID = 'server';
5781  /**
5782  * Connect to Server Failed
5783  * @const 
5784  */
5785 Roo.form.Action.CONNECT_FAILURE = 'connect';
5786 /**
5787  * Reading Data from Server Failed
5788  * @const 
5789  */
5790 Roo.form.Action.LOAD_FAILURE = 'load';
5791
5792 Roo.form.Action.prototype = {
5793     type : 'default',
5794     failureType : undefined,
5795     response : undefined,
5796     result : undefined,
5797
5798     // interface method
5799     run : function(options){
5800
5801     },
5802
5803     // interface method
5804     success : function(response){
5805
5806     },
5807
5808     // interface method
5809     handleResponse : function(response){
5810
5811     },
5812
5813     // default connection failure
5814     failure : function(response){
5815         
5816         this.response = response;
5817         this.failureType = Roo.form.Action.CONNECT_FAILURE;
5818         this.form.afterAction(this, false);
5819     },
5820
5821     processResponse : function(response){
5822         this.response = response;
5823         if(!response.responseText){
5824             return true;
5825         }
5826         this.result = this.handleResponse(response);
5827         return this.result;
5828     },
5829
5830     // utility functions used internally
5831     getUrl : function(appendParams){
5832         var url = this.options.url || this.form.url || this.form.el.dom.action;
5833         if(appendParams){
5834             var p = this.getParams();
5835             if(p){
5836                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
5837             }
5838         }
5839         return url;
5840     },
5841
5842     getMethod : function(){
5843         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
5844     },
5845
5846     getParams : function(){
5847         var bp = this.form.baseParams;
5848         var p = this.options.params;
5849         if(p){
5850             if(typeof p == "object"){
5851                 p = Roo.urlEncode(Roo.applyIf(p, bp));
5852             }else if(typeof p == 'string' && bp){
5853                 p += '&' + Roo.urlEncode(bp);
5854             }
5855         }else if(bp){
5856             p = Roo.urlEncode(bp);
5857         }
5858         return p;
5859     },
5860
5861     createCallback : function(){
5862         return {
5863             success: this.success,
5864             failure: this.failure,
5865             scope: this,
5866             timeout: (this.form.timeout*1000),
5867             upload: this.form.fileUpload ? this.success : undefined
5868         };
5869     }
5870 };
5871
5872 Roo.form.Action.Submit = function(form, options){
5873     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
5874 };
5875
5876 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
5877     type : 'submit',
5878
5879     haveProgress : false,
5880     uploadComplete : false,
5881     
5882     // uploadProgress indicator.
5883     uploadProgress : function()
5884     {
5885         if (!this.form.progressUrl) {
5886             return;
5887         }
5888         
5889         if (!this.haveProgress) {
5890             Roo.MessageBox.progress("Uploading", "Uploading");
5891         }
5892         if (this.uploadComplete) {
5893            Roo.MessageBox.hide();
5894            return;
5895         }
5896         
5897         this.haveProgress = true;
5898    
5899         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
5900         
5901         var c = new Roo.data.Connection();
5902         c.request({
5903             url : this.form.progressUrl,
5904             params: {
5905                 id : uid
5906             },
5907             method: 'GET',
5908             success : function(req){
5909                //console.log(data);
5910                 var rdata = false;
5911                 var edata;
5912                 try  {
5913                    rdata = Roo.decode(req.responseText)
5914                 } catch (e) {
5915                     Roo.log("Invalid data from server..");
5916                     Roo.log(edata);
5917                     return;
5918                 }
5919                 if (!rdata || !rdata.success) {
5920                     Roo.log(rdata);
5921                     Roo.MessageBox.alert(Roo.encode(rdata));
5922                     return;
5923                 }
5924                 var data = rdata.data;
5925                 
5926                 if (this.uploadComplete) {
5927                    Roo.MessageBox.hide();
5928                    return;
5929                 }
5930                    
5931                 if (data){
5932                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
5933                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
5934                     );
5935                 }
5936                 this.uploadProgress.defer(2000,this);
5937             },
5938        
5939             failure: function(data) {
5940                 Roo.log('progress url failed ');
5941                 Roo.log(data);
5942             },
5943             scope : this
5944         });
5945            
5946     },
5947     
5948     
5949     run : function()
5950     {
5951         // run get Values on the form, so it syncs any secondary forms.
5952         this.form.getValues();
5953         
5954         var o = this.options;
5955         var method = this.getMethod();
5956         var isPost = method == 'POST';
5957         if(o.clientValidation === false || this.form.isValid()){
5958             
5959             if (this.form.progressUrl) {
5960                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
5961                     (new Date() * 1) + '' + Math.random());
5962                     
5963             } 
5964             
5965             
5966             Roo.Ajax.request(Roo.apply(this.createCallback(), {
5967                 form:this.form.el.dom,
5968                 url:this.getUrl(!isPost),
5969                 method: method,
5970                 params:isPost ? this.getParams() : null,
5971                 isUpload: this.form.fileUpload
5972             }));
5973             
5974             this.uploadProgress();
5975
5976         }else if (o.clientValidation !== false){ // client validation failed
5977             this.failureType = Roo.form.Action.CLIENT_INVALID;
5978             this.form.afterAction(this, false);
5979         }
5980     },
5981
5982     success : function(response)
5983     {
5984         this.uploadComplete= true;
5985         if (this.haveProgress) {
5986             Roo.MessageBox.hide();
5987         }
5988         
5989         
5990         var result = this.processResponse(response);
5991         if(result === true || result.success){
5992             this.form.afterAction(this, true);
5993             return;
5994         }
5995         if(result.errors){
5996             this.form.markInvalid(result.errors);
5997             this.failureType = Roo.form.Action.SERVER_INVALID;
5998         }
5999         this.form.afterAction(this, false);
6000     },
6001     failure : function(response)
6002     {
6003         this.uploadComplete= true;
6004         if (this.haveProgress) {
6005             Roo.MessageBox.hide();
6006         }
6007         
6008         this.response = response;
6009         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6010         this.form.afterAction(this, false);
6011     },
6012     
6013     handleResponse : function(response){
6014         if(this.form.errorReader){
6015             var rs = this.form.errorReader.read(response);
6016             var errors = [];
6017             if(rs.records){
6018                 for(var i = 0, len = rs.records.length; i < len; i++) {
6019                     var r = rs.records[i];
6020                     errors[i] = r.data;
6021                 }
6022             }
6023             if(errors.length < 1){
6024                 errors = null;
6025             }
6026             return {
6027                 success : rs.success,
6028                 errors : errors
6029             };
6030         }
6031         var ret = false;
6032         try {
6033             ret = Roo.decode(response.responseText);
6034         } catch (e) {
6035             ret = {
6036                 success: false,
6037                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6038                 errors : []
6039             };
6040         }
6041         return ret;
6042         
6043     }
6044 });
6045
6046
6047 Roo.form.Action.Load = function(form, options){
6048     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6049     this.reader = this.form.reader;
6050 };
6051
6052 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6053     type : 'load',
6054
6055     run : function(){
6056         
6057         Roo.Ajax.request(Roo.apply(
6058                 this.createCallback(), {
6059                     method:this.getMethod(),
6060                     url:this.getUrl(false),
6061                     params:this.getParams()
6062         }));
6063     },
6064
6065     success : function(response){
6066         
6067         var result = this.processResponse(response);
6068         if(result === true || !result.success || !result.data){
6069             this.failureType = Roo.form.Action.LOAD_FAILURE;
6070             this.form.afterAction(this, false);
6071             return;
6072         }
6073         this.form.clearInvalid();
6074         this.form.setValues(result.data);
6075         this.form.afterAction(this, true);
6076     },
6077
6078     handleResponse : function(response){
6079         if(this.form.reader){
6080             var rs = this.form.reader.read(response);
6081             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
6082             return {
6083                 success : rs.success,
6084                 data : data
6085             };
6086         }
6087         return Roo.decode(response.responseText);
6088     }
6089 });
6090
6091 Roo.form.Action.ACTION_TYPES = {
6092     'load' : Roo.form.Action.Load,
6093     'submit' : Roo.form.Action.Submit
6094 };/*
6095  * - LGPL
6096  *
6097  * form
6098  * 
6099  */
6100
6101 /**
6102  * @class Roo.bootstrap.Form
6103  * @extends Roo.bootstrap.Component
6104  * Bootstrap Form class
6105  * @cfg {String} method  GET | POST (default POST)
6106  * @cfg {String} labelAlign top | left (default top)
6107   * @cfg {String} align left  | right - for navbars
6108
6109  * 
6110  * @constructor
6111  * Create a new Form
6112  * @param {Object} config The config object
6113  */
6114
6115
6116 Roo.bootstrap.Form = function(config){
6117     Roo.bootstrap.Form.superclass.constructor.call(this, config);
6118     this.addEvents({
6119         /**
6120          * @event clientvalidation
6121          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
6122          * @param {Form} this
6123          * @param {Boolean} valid true if the form has passed client-side validation
6124          */
6125         clientvalidation: true,
6126         /**
6127          * @event beforeaction
6128          * Fires before any action is performed. Return false to cancel the action.
6129          * @param {Form} this
6130          * @param {Action} action The action to be performed
6131          */
6132         beforeaction: true,
6133         /**
6134          * @event actionfailed
6135          * Fires when an action fails.
6136          * @param {Form} this
6137          * @param {Action} action The action that failed
6138          */
6139         actionfailed : true,
6140         /**
6141          * @event actioncomplete
6142          * Fires when an action is completed.
6143          * @param {Form} this
6144          * @param {Action} action The action that completed
6145          */
6146         actioncomplete : true
6147     });
6148     
6149 };
6150
6151 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
6152       
6153      /**
6154      * @cfg {String} method
6155      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
6156      */
6157     method : 'POST',
6158     /**
6159      * @cfg {String} url
6160      * The URL to use for form actions if one isn't supplied in the action options.
6161      */
6162     /**
6163      * @cfg {Boolean} fileUpload
6164      * Set to true if this form is a file upload.
6165      */
6166      
6167     /**
6168      * @cfg {Object} baseParams
6169      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
6170      */
6171       
6172     /**
6173      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
6174      */
6175     timeout: 30,
6176     /**
6177      * @cfg {Sting} align (left|right) for navbar forms
6178      */
6179     align : 'left',
6180
6181     // private
6182     activeAction : null,
6183  
6184     /**
6185      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
6186      * element by passing it or its id or mask the form itself by passing in true.
6187      * @type Mixed
6188      */
6189     waitMsgTarget : false,
6190     
6191      
6192     
6193     /**
6194      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
6195      * element by passing it or its id or mask the form itself by passing in true.
6196      * @type Mixed
6197      */
6198     
6199     getAutoCreate : function(){
6200         
6201         var cfg = {
6202             tag: 'form',
6203             method : this.method || 'POST',
6204             id : this.id || Roo.id(),
6205             cls : ''
6206         }
6207         if (this.parent().xtype.match(/^Nav/)) {
6208             cfg.cls = 'navbar-form navbar-' + this.align;
6209             
6210         }
6211         
6212         if (this.labelAlign == 'left' ) {
6213             cfg.cls += ' form-horizontal';
6214         }
6215         
6216         
6217         return cfg;
6218     },
6219     initEvents : function()
6220     {
6221         this.el.on('submit', this.onSubmit, this);
6222         // this was added as random key presses on the form where triggering form submit.
6223         this.el.on('keypress', function(e) {
6224             if (e.getCharCode() != 13) {
6225                 return true;
6226             }
6227             // we might need to allow it for textareas.. and some other items.
6228             // check e.getTarget().
6229             
6230             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
6231                 return true;
6232             }
6233         
6234             Roo.log("keypress blocked");
6235             
6236             e.preventDefault();
6237             return false;
6238         });
6239         
6240     },
6241     // private
6242     onSubmit : function(e){
6243         e.stopEvent();
6244     },
6245     
6246      /**
6247      * Returns true if client-side validation on the form is successful.
6248      * @return Boolean
6249      */
6250     isValid : function(){
6251         var items = this.getItems();
6252         var valid = true;
6253         items.each(function(f){
6254            if(!f.validate()){
6255                valid = false;
6256                
6257            }
6258         });
6259         return valid;
6260     },
6261     /**
6262      * Returns true if any fields in this form have changed since their original load.
6263      * @return Boolean
6264      */
6265     isDirty : function(){
6266         var dirty = false;
6267         var items = this.getItems();
6268         items.each(function(f){
6269            if(f.isDirty()){
6270                dirty = true;
6271                return false;
6272            }
6273            return true;
6274         });
6275         return dirty;
6276     },
6277      /**
6278      * Performs a predefined action (submit or load) or custom actions you define on this form.
6279      * @param {String} actionName The name of the action type
6280      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
6281      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
6282      * accept other config options):
6283      * <pre>
6284 Property          Type             Description
6285 ----------------  ---------------  ----------------------------------------------------------------------------------
6286 url               String           The url for the action (defaults to the form's url)
6287 method            String           The form method to use (defaults to the form's method, or POST if not defined)
6288 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
6289 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
6290                                    validate the form on the client (defaults to false)
6291      * </pre>
6292      * @return {BasicForm} this
6293      */
6294     doAction : function(action, options){
6295         if(typeof action == 'string'){
6296             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
6297         }
6298         if(this.fireEvent('beforeaction', this, action) !== false){
6299             this.beforeAction(action);
6300             action.run.defer(100, action);
6301         }
6302         return this;
6303     },
6304     
6305     // private
6306     beforeAction : function(action){
6307         var o = action.options;
6308         
6309         // not really supported yet.. ??
6310         
6311         //if(this.waitMsgTarget === true){
6312             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6313         //}else if(this.waitMsgTarget){
6314         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
6315         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
6316         //}else {
6317         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
6318        // }
6319          
6320     },
6321
6322     // private
6323     afterAction : function(action, success){
6324         this.activeAction = null;
6325         var o = action.options;
6326         
6327         //if(this.waitMsgTarget === true){
6328             this.el.unmask();
6329         //}else if(this.waitMsgTarget){
6330         //    this.waitMsgTarget.unmask();
6331         //}else{
6332         //    Roo.MessageBox.updateProgress(1);
6333         //    Roo.MessageBox.hide();
6334        // }
6335         // 
6336         if(success){
6337             if(o.reset){
6338                 this.reset();
6339             }
6340             Roo.callback(o.success, o.scope, [this, action]);
6341             this.fireEvent('actioncomplete', this, action);
6342             
6343         }else{
6344             
6345             // failure condition..
6346             // we have a scenario where updates need confirming.
6347             // eg. if a locking scenario exists..
6348             // we look for { errors : { needs_confirm : true }} in the response.
6349             if (
6350                 (typeof(action.result) != 'undefined')  &&
6351                 (typeof(action.result.errors) != 'undefined')  &&
6352                 (typeof(action.result.errors.needs_confirm) != 'undefined')
6353            ){
6354                 var _t = this;
6355                 Roo.log("not supported yet");
6356                  /*
6357                 
6358                 Roo.MessageBox.confirm(
6359                     "Change requires confirmation",
6360                     action.result.errorMsg,
6361                     function(r) {
6362                         if (r != 'yes') {
6363                             return;
6364                         }
6365                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
6366                     }
6367                     
6368                 );
6369                 */
6370                 
6371                 
6372                 return;
6373             }
6374             
6375             Roo.callback(o.failure, o.scope, [this, action]);
6376             // show an error message if no failed handler is set..
6377             if (!this.hasListener('actionfailed')) {
6378                 Roo.log("need to add dialog support");
6379                 /*
6380                 Roo.MessageBox.alert("Error",
6381                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
6382                         action.result.errorMsg :
6383                         "Saving Failed, please check your entries or try again"
6384                 );
6385                 */
6386             }
6387             
6388             this.fireEvent('actionfailed', this, action);
6389         }
6390         
6391     },
6392     /**
6393      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
6394      * @param {String} id The value to search for
6395      * @return Field
6396      */
6397     findField : function(id){
6398         var items = this.getItems();
6399         var field = items.get(id);
6400         if(!field){
6401              items.each(function(f){
6402                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
6403                     field = f;
6404                     return false;
6405                 }
6406                 return true;
6407             });
6408         }
6409         return field || null;
6410     },
6411      /**
6412      * Mark fields in this form invalid in bulk.
6413      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
6414      * @return {BasicForm} this
6415      */
6416     markInvalid : function(errors){
6417         if(errors instanceof Array){
6418             for(var i = 0, len = errors.length; i < len; i++){
6419                 var fieldError = errors[i];
6420                 var f = this.findField(fieldError.id);
6421                 if(f){
6422                     f.markInvalid(fieldError.msg);
6423                 }
6424             }
6425         }else{
6426             var field, id;
6427             for(id in errors){
6428                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
6429                     field.markInvalid(errors[id]);
6430                 }
6431             }
6432         }
6433         //Roo.each(this.childForms || [], function (f) {
6434         //    f.markInvalid(errors);
6435         //});
6436         
6437         return this;
6438     },
6439
6440     /**
6441      * Set values for fields in this form in bulk.
6442      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
6443      * @return {BasicForm} this
6444      */
6445     setValues : function(values){
6446         if(values instanceof Array){ // array of objects
6447             for(var i = 0, len = values.length; i < len; i++){
6448                 var v = values[i];
6449                 var f = this.findField(v.id);
6450                 if(f){
6451                     f.setValue(v.value);
6452                     if(this.trackResetOnLoad){
6453                         f.originalValue = f.getValue();
6454                     }
6455                 }
6456             }
6457         }else{ // object hash
6458             var field, id;
6459             for(id in values){
6460                 if(typeof values[id] != 'function' && (field = this.findField(id))){
6461                     
6462                     if (field.setFromData && 
6463                         field.valueField && 
6464                         field.displayField &&
6465                         // combos' with local stores can 
6466                         // be queried via setValue()
6467                         // to set their value..
6468                         (field.store && !field.store.isLocal)
6469                         ) {
6470                         // it's a combo
6471                         var sd = { };
6472                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
6473                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
6474                         field.setFromData(sd);
6475                         
6476                     } else {
6477                         field.setValue(values[id]);
6478                     }
6479                     
6480                     
6481                     if(this.trackResetOnLoad){
6482                         field.originalValue = field.getValue();
6483                     }
6484                 }
6485             }
6486         }
6487          
6488         //Roo.each(this.childForms || [], function (f) {
6489         //    f.setValues(values);
6490         //});
6491                 
6492         return this;
6493     },
6494
6495     /**
6496      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
6497      * they are returned as an array.
6498      * @param {Boolean} asString
6499      * @return {Object}
6500      */
6501     getValues : function(asString){
6502         //if (this.childForms) {
6503             // copy values from the child forms
6504         //    Roo.each(this.childForms, function (f) {
6505         //        this.setValues(f.getValues());
6506         //    }, this);
6507         //}
6508         
6509         
6510         
6511         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
6512         if(asString === true){
6513             return fs;
6514         }
6515         return Roo.urlDecode(fs);
6516     },
6517     
6518     /**
6519      * Returns the fields in this form as an object with key/value pairs. 
6520      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
6521      * @return {Object}
6522      */
6523     getFieldValues : function(with_hidden)
6524     {
6525         var items = this.getItems();
6526         var ret = {};
6527         items.each(function(f){
6528             if (!f.getName()) {
6529                 return;
6530             }
6531             var v = f.getValue();
6532             if (f.inputType =='radio') {
6533                 if (typeof(ret[f.getName()]) == 'undefined') {
6534                     ret[f.getName()] = ''; // empty..
6535                 }
6536                 
6537                 if (!f.el.dom.checked) {
6538                     return;
6539                     
6540                 }
6541                 v = f.el.dom.value;
6542                 
6543             }
6544             
6545             // not sure if this supported any more..
6546             if ((typeof(v) == 'object') && f.getRawValue) {
6547                 v = f.getRawValue() ; // dates..
6548             }
6549             // combo boxes where name != hiddenName...
6550             if (f.name != f.getName()) {
6551                 ret[f.name] = f.getRawValue();
6552             }
6553             ret[f.getName()] = v;
6554         });
6555         
6556         return ret;
6557     },
6558
6559     /**
6560      * Clears all invalid messages in this form.
6561      * @return {BasicForm} this
6562      */
6563     clearInvalid : function(){
6564         var items = this.getItems();
6565         
6566         items.each(function(f){
6567            f.clearInvalid();
6568         });
6569         
6570         
6571         
6572         return this;
6573     },
6574
6575     /**
6576      * Resets this form.
6577      * @return {BasicForm} this
6578      */
6579     reset : function(){
6580         var items = this.getItems();
6581         items.each(function(f){
6582             f.reset();
6583         });
6584         
6585         Roo.each(this.childForms || [], function (f) {
6586             f.reset();
6587         });
6588        
6589         
6590         return this;
6591     },
6592     getItems : function()
6593     {
6594         var r=new Roo.util.MixedCollection(false, function(o){
6595             return o.id || (o.id = Roo.id());
6596         });
6597         var iter = function(el) {
6598             if (el.inputEl) {
6599                 r.add(el);
6600             }
6601             if (!el.items) {
6602                 return;
6603             }
6604             Roo.each(el.items,function(e) {
6605                 iter(e);
6606             });
6607             
6608             
6609         };
6610         iter(this);
6611         return r;
6612         
6613         
6614         
6615         
6616     }
6617     
6618 });
6619
6620  
6621 /*
6622  * Based on:
6623  * Ext JS Library 1.1.1
6624  * Copyright(c) 2006-2007, Ext JS, LLC.
6625  *
6626  * Originally Released Under LGPL - original licence link has changed is not relivant.
6627  *
6628  * Fork - LGPL
6629  * <script type="text/javascript">
6630  */
6631 /**
6632  * @class Roo.form.VTypes
6633  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
6634  * @singleton
6635  */
6636 Roo.form.VTypes = function(){
6637     // closure these in so they are only created once.
6638     var alpha = /^[a-zA-Z_]+$/;
6639     var alphanum = /^[a-zA-Z0-9_]+$/;
6640     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
6641     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
6642
6643     // All these messages and functions are configurable
6644     return {
6645         /**
6646          * The function used to validate email addresses
6647          * @param {String} value The email address
6648          */
6649         'email' : function(v){
6650             return email.test(v);
6651         },
6652         /**
6653          * The error text to display when the email validation function returns false
6654          * @type String
6655          */
6656         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
6657         /**
6658          * The keystroke filter mask to be applied on email input
6659          * @type RegExp
6660          */
6661         'emailMask' : /[a-z0-9_\.\-@]/i,
6662
6663         /**
6664          * The function used to validate URLs
6665          * @param {String} value The URL
6666          */
6667         'url' : function(v){
6668             return url.test(v);
6669         },
6670         /**
6671          * The error text to display when the url validation function returns false
6672          * @type String
6673          */
6674         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
6675         
6676         /**
6677          * The function used to validate alpha values
6678          * @param {String} value The value
6679          */
6680         'alpha' : function(v){
6681             return alpha.test(v);
6682         },
6683         /**
6684          * The error text to display when the alpha validation function returns false
6685          * @type String
6686          */
6687         'alphaText' : 'This field should only contain letters and _',
6688         /**
6689          * The keystroke filter mask to be applied on alpha input
6690          * @type RegExp
6691          */
6692         'alphaMask' : /[a-z_]/i,
6693
6694         /**
6695          * The function used to validate alphanumeric values
6696          * @param {String} value The value
6697          */
6698         'alphanum' : function(v){
6699             return alphanum.test(v);
6700         },
6701         /**
6702          * The error text to display when the alphanumeric validation function returns false
6703          * @type String
6704          */
6705         'alphanumText' : 'This field should only contain letters, numbers and _',
6706         /**
6707          * The keystroke filter mask to be applied on alphanumeric input
6708          * @type RegExp
6709          */
6710         'alphanumMask' : /[a-z0-9_]/i
6711     };
6712 }();/*
6713  * - LGPL
6714  *
6715  * Input
6716  * 
6717  */
6718
6719 /**
6720  * @class Roo.bootstrap.Input
6721  * @extends Roo.bootstrap.Component
6722  * Bootstrap Input class
6723  * @cfg {Boolean} disabled is it disabled
6724  * @cfg {String} fieldLabel - the label associated
6725  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
6726  * @cfg {String} name name of the input
6727  * @cfg {string} fieldLabel - the label associated
6728  * @cfg {string}  inputType - input / file submit ...
6729  * @cfg {string} placeholder - placeholder to put in text.
6730  * @cfg {string}  before - input group add on before
6731  * @cfg {string} after - input group add on after
6732  * @cfg {string} size - (lg|sm) or leave empty..
6733  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
6734  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
6735  * @cfg {Number} md colspan out of 12 for computer-sized screens
6736  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
6737  * @cfg {string} value default value of the input
6738  * @cfg {Number} labelWidth set the width of label (0-12)
6739  * @cfg {String} labelAlign (top|left)
6740  * @cfg {Boolean} readOnly Specifies that the field should be read-only
6741  * @cfg {String} align (left|center|right) Default left
6742  * 
6743  * 
6744  * @constructor
6745  * Create a new Input
6746  * @param {Object} config The config object
6747  */
6748
6749 Roo.bootstrap.Input = function(config){
6750     Roo.bootstrap.Input.superclass.constructor.call(this, config);
6751    
6752         this.addEvents({
6753             /**
6754              * @event focus
6755              * Fires when this field receives input focus.
6756              * @param {Roo.form.Field} this
6757              */
6758             focus : true,
6759             /**
6760              * @event blur
6761              * Fires when this field loses input focus.
6762              * @param {Roo.form.Field} this
6763              */
6764             blur : true,
6765             /**
6766              * @event specialkey
6767              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
6768              * {@link Roo.EventObject#getKey} to determine which key was pressed.
6769              * @param {Roo.form.Field} this
6770              * @param {Roo.EventObject} e The event object
6771              */
6772             specialkey : true,
6773             /**
6774              * @event change
6775              * Fires just before the field blurs if the field value has changed.
6776              * @param {Roo.form.Field} this
6777              * @param {Mixed} newValue The new value
6778              * @param {Mixed} oldValue The original value
6779              */
6780             change : true,
6781             /**
6782              * @event invalid
6783              * Fires after the field has been marked as invalid.
6784              * @param {Roo.form.Field} this
6785              * @param {String} msg The validation message
6786              */
6787             invalid : true,
6788             /**
6789              * @event valid
6790              * Fires after the field has been validated with no errors.
6791              * @param {Roo.form.Field} this
6792              */
6793             valid : true,
6794              /**
6795              * @event keyup
6796              * Fires after the key up
6797              * @param {Roo.form.Field} this
6798              * @param {Roo.EventObject}  e The event Object
6799              */
6800             keyup : true
6801         });
6802 };
6803
6804 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
6805      /**
6806      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
6807       automatic validation (defaults to "keyup").
6808      */
6809     validationEvent : "keyup",
6810      /**
6811      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
6812      */
6813     validateOnBlur : true,
6814     /**
6815      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
6816      */
6817     validationDelay : 250,
6818      /**
6819      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
6820      */
6821     focusClass : "x-form-focus",  // not needed???
6822     
6823        
6824     /**
6825      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
6826      */
6827     invalidClass : "has-error",
6828     
6829     /**
6830      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
6831      */
6832     selectOnFocus : false,
6833     
6834      /**
6835      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
6836      */
6837     maskRe : null,
6838        /**
6839      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
6840      */
6841     vtype : null,
6842     
6843       /**
6844      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
6845      */
6846     disableKeyFilter : false,
6847     
6848        /**
6849      * @cfg {Boolean} disabled True to disable the field (defaults to false).
6850      */
6851     disabled : false,
6852      /**
6853      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
6854      */
6855     allowBlank : true,
6856     /**
6857      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
6858      */
6859     blankText : "This field is required",
6860     
6861      /**
6862      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
6863      */
6864     minLength : 0,
6865     /**
6866      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
6867      */
6868     maxLength : Number.MAX_VALUE,
6869     /**
6870      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
6871      */
6872     minLengthText : "The minimum length for this field is {0}",
6873     /**
6874      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
6875      */
6876     maxLengthText : "The maximum length for this field is {0}",
6877   
6878     
6879     /**
6880      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
6881      * If available, this function will be called only after the basic validators all return true, and will be passed the
6882      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
6883      */
6884     validator : null,
6885     /**
6886      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
6887      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
6888      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
6889      */
6890     regex : null,
6891     /**
6892      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
6893      */
6894     regexText : "",
6895     
6896     
6897     
6898     fieldLabel : '',
6899     inputType : 'text',
6900     
6901     name : false,
6902     placeholder: false,
6903     before : false,
6904     after : false,
6905     size : false,
6906     // private
6907     hasFocus : false,
6908     preventMark: false,
6909     isFormField : true,
6910     value : '',
6911     labelWidth : 2,
6912     labelAlign : false,
6913     readOnly : false,
6914     align : false,
6915     formatedValue : false,
6916     
6917     parentLabelAlign : function()
6918     {
6919         var parent = this;
6920         while (parent.parent()) {
6921             parent = parent.parent();
6922             if (typeof(parent.labelAlign) !='undefined') {
6923                 return parent.labelAlign;
6924             }
6925         }
6926         return 'left';
6927         
6928     },
6929     
6930     getAutoCreate : function(){
6931         
6932         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
6933         
6934         var id = Roo.id();
6935         
6936         var cfg = {};
6937         
6938         if(this.inputType != 'hidden'){
6939             cfg.cls = 'form-group' //input-group
6940         }
6941         
6942         var input =  {
6943             tag: 'input',
6944             id : id,
6945             type : this.inputType,
6946             value : this.value,
6947             cls : 'form-control',
6948             placeholder : this.placeholder || ''
6949             
6950         };
6951         
6952         if(this.align){
6953             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
6954         }
6955         
6956         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
6957             input.maxLength = this.maxLength;
6958         }
6959         
6960         if (this.disabled) {
6961             input.disabled=true;
6962         }
6963         
6964         if (this.readOnly) {
6965             input.readonly=true;
6966         }
6967         
6968         if (this.name) {
6969             input.name = this.name;
6970         }
6971         if (this.size) {
6972             input.cls += ' input-' + this.size;
6973         }
6974         var settings=this;
6975         ['xs','sm','md','lg'].map(function(size){
6976             if (settings[size]) {
6977                 cfg.cls += ' col-' + size + '-' + settings[size];
6978             }
6979         });
6980         
6981         var inputblock = input;
6982         
6983         if (this.before || this.after) {
6984             
6985             inputblock = {
6986                 cls : 'input-group',
6987                 cn :  [] 
6988             };
6989             if (this.before && typeof(this.before) == 'string') {
6990                 
6991                 inputblock.cn.push({
6992                     tag :'span',
6993                     cls : 'roo-input-before input-group-addon',
6994                     html : this.before
6995                 });
6996             }
6997             if (this.before && typeof(this.before) == 'object') {
6998                 this.before = Roo.factory(this.before);
6999                 Roo.log(this.before);
7000                 inputblock.cn.push({
7001                     tag :'span',
7002                     cls : 'roo-input-before input-group-' +
7003                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7004                 });
7005             }
7006             
7007             inputblock.cn.push(input);
7008             
7009             if (this.after && typeof(this.after) == 'string') {
7010                 inputblock.cn.push({
7011                     tag :'span',
7012                     cls : 'roo-input-after input-group-addon',
7013                     html : this.after
7014                 });
7015             }
7016             if (this.after && typeof(this.after) == 'object') {
7017                 this.after = Roo.factory(this.after);
7018                 Roo.log(this.after);
7019                 inputblock.cn.push({
7020                     tag :'span',
7021                     cls : 'roo-input-after input-group-' +
7022                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7023                 });
7024             }
7025         };
7026         
7027         if (align ==='left' && this.fieldLabel.length) {
7028                 Roo.log("left and has label");
7029                 cfg.cn = [
7030                     
7031                     {
7032                         tag: 'label',
7033                         'for' :  id,
7034                         cls : 'control-label col-sm-' + this.labelWidth,
7035                         html : this.fieldLabel
7036                         
7037                     },
7038                     {
7039                         cls : "col-sm-" + (12 - this.labelWidth), 
7040                         cn: [
7041                             inputblock
7042                         ]
7043                     }
7044                     
7045                 ];
7046         } else if ( this.fieldLabel.length) {
7047                 Roo.log(" label");
7048                  cfg.cn = [
7049                    
7050                     {
7051                         tag: 'label',
7052                         //cls : 'input-group-addon',
7053                         html : this.fieldLabel
7054                         
7055                     },
7056                     
7057                     inputblock
7058                     
7059                 ];
7060
7061         } else {
7062             
7063                 Roo.log(" no label && no align");
7064                 cfg.cn = [
7065                     
7066                         inputblock
7067                     
7068                 ];
7069                 
7070                 
7071         };
7072         Roo.log('input-parentType: ' + this.parentType);
7073         
7074         if (this.parentType === 'Navbar' &&  this.parent().bar) {
7075            cfg.cls += ' navbar-form';
7076            Roo.log(cfg);
7077         }
7078         
7079         return cfg;
7080         
7081     },
7082     /**
7083      * return the real input element.
7084      */
7085     inputEl: function ()
7086     {
7087         return this.el.select('input.form-control',true).first();
7088     },
7089     setDisabled : function(v)
7090     {
7091         var i  = this.inputEl().dom;
7092         if (!v) {
7093             i.removeAttribute('disabled');
7094             return;
7095             
7096         }
7097         i.setAttribute('disabled','true');
7098     },
7099     initEvents : function()
7100     {
7101         
7102         this.inputEl().on("keydown" , this.fireKey,  this);
7103         this.inputEl().on("focus", this.onFocus,  this);
7104         this.inputEl().on("blur", this.onBlur,  this);
7105         
7106         this.inputEl().relayEvent('keyup', this);
7107
7108         // reference to original value for reset
7109         this.originalValue = this.getValue();
7110         //Roo.form.TextField.superclass.initEvents.call(this);
7111         if(this.validationEvent == 'keyup'){
7112             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
7113             this.inputEl().on('keyup', this.filterValidation, this);
7114         }
7115         else if(this.validationEvent !== false){
7116             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
7117         }
7118         
7119         if(this.selectOnFocus){
7120             this.on("focus", this.preFocus, this);
7121             
7122         }
7123         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
7124             this.inputEl().on("keypress", this.filterKeys, this);
7125         }
7126        /* if(this.grow){
7127             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
7128             this.el.on("click", this.autoSize,  this);
7129         }
7130         */
7131         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
7132             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
7133         }
7134         
7135         if (typeof(this.before) == 'object') {
7136             this.before.render(this.el.select('.roo-input-before',true).first());
7137         }
7138         if (typeof(this.after) == 'object') {
7139             this.after.render(this.el.select('.roo-input-after',true).first());
7140         }
7141         
7142         
7143     },
7144     filterValidation : function(e){
7145         if(!e.isNavKeyPress()){
7146             this.validationTask.delay(this.validationDelay);
7147         }
7148     },
7149      /**
7150      * Validates the field value
7151      * @return {Boolean} True if the value is valid, else false
7152      */
7153     validate : function(){
7154         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
7155         if(this.disabled || this.validateValue(this.getRawValue())){
7156             this.clearInvalid();
7157             return true;
7158         }
7159         return false;
7160     },
7161     
7162     
7163     /**
7164      * Validates a value according to the field's validation rules and marks the field as invalid
7165      * if the validation fails
7166      * @param {Mixed} value The value to validate
7167      * @return {Boolean} True if the value is valid, else false
7168      */
7169     validateValue : function(value){
7170         if(value.length < 1)  { // if it's blank
7171              if(this.allowBlank){
7172                 this.clearInvalid();
7173                 return true;
7174              }else{
7175                 this.markInvalid(this.blankText);
7176                 return false;
7177              }
7178         }
7179         if(value.length < this.minLength){
7180             this.markInvalid(String.format(this.minLengthText, this.minLength));
7181             return false;
7182         }
7183         if(value.length > this.maxLength){
7184             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
7185             return false;
7186         }
7187         if(this.vtype){
7188             var vt = Roo.form.VTypes;
7189             if(!vt[this.vtype](value, this)){
7190                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
7191                 return false;
7192             }
7193         }
7194         if(typeof this.validator == "function"){
7195             var msg = this.validator(value);
7196             if(msg !== true){
7197                 this.markInvalid(msg);
7198                 return false;
7199             }
7200         }
7201         if(this.regex && !this.regex.test(value)){
7202             this.markInvalid(this.regexText);
7203             return false;
7204         }
7205         return true;
7206     },
7207
7208     
7209     
7210      // private
7211     fireKey : function(e){
7212         //Roo.log('field ' + e.getKey());
7213         if(e.isNavKeyPress()){
7214             this.fireEvent("specialkey", this, e);
7215         }
7216     },
7217     focus : function (selectText){
7218         if(this.rendered){
7219             this.inputEl().focus();
7220             if(selectText === true){
7221                 this.inputEl().dom.select();
7222             }
7223         }
7224         return this;
7225     } ,
7226     
7227     onFocus : function(){
7228         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7229            // this.el.addClass(this.focusClass);
7230         }
7231         if(!this.hasFocus){
7232             this.hasFocus = true;
7233             this.startValue = this.getValue();
7234             this.fireEvent("focus", this);
7235         }
7236     },
7237     
7238     beforeBlur : Roo.emptyFn,
7239
7240     
7241     // private
7242     onBlur : function(){
7243         this.beforeBlur();
7244         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7245             //this.el.removeClass(this.focusClass);
7246         }
7247         this.hasFocus = false;
7248         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
7249             this.validate();
7250         }
7251         var v = this.getValue();
7252         if(String(v) !== String(this.startValue)){
7253             this.fireEvent('change', this, v, this.startValue);
7254         }
7255         this.fireEvent("blur", this);
7256     },
7257     
7258     /**
7259      * Resets the current field value to the originally loaded value and clears any validation messages
7260      */
7261     reset : function(){
7262         this.setValue(this.originalValue);
7263         this.clearInvalid();
7264     },
7265      /**
7266      * Returns the name of the field
7267      * @return {Mixed} name The name field
7268      */
7269     getName: function(){
7270         return this.name;
7271     },
7272      /**
7273      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
7274      * @return {Mixed} value The field value
7275      */
7276     getValue : function(){
7277         
7278         var v = this.inputEl().getValue();
7279         
7280         return v;
7281     },
7282     /**
7283      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
7284      * @return {Mixed} value The field value
7285      */
7286     getRawValue : function(){
7287         var v = this.inputEl().getValue();
7288         
7289         return v;
7290     },
7291     
7292     /**
7293      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
7294      * @param {Mixed} value The value to set
7295      */
7296     setRawValue : function(v){
7297         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7298     },
7299     
7300     selectText : function(start, end){
7301         var v = this.getRawValue();
7302         if(v.length > 0){
7303             start = start === undefined ? 0 : start;
7304             end = end === undefined ? v.length : end;
7305             var d = this.inputEl().dom;
7306             if(d.setSelectionRange){
7307                 d.setSelectionRange(start, end);
7308             }else if(d.createTextRange){
7309                 var range = d.createTextRange();
7310                 range.moveStart("character", start);
7311                 range.moveEnd("character", v.length-end);
7312                 range.select();
7313             }
7314         }
7315     },
7316     
7317     /**
7318      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
7319      * @param {Mixed} value The value to set
7320      */
7321     setValue : function(v){
7322         this.value = v;
7323         if(this.rendered){
7324             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7325             this.validate();
7326         }
7327     },
7328     
7329     /*
7330     processValue : function(value){
7331         if(this.stripCharsRe){
7332             var newValue = value.replace(this.stripCharsRe, '');
7333             if(newValue !== value){
7334                 this.setRawValue(newValue);
7335                 return newValue;
7336             }
7337         }
7338         return value;
7339     },
7340   */
7341     preFocus : function(){
7342         
7343         if(this.selectOnFocus){
7344             this.inputEl().dom.select();
7345         }
7346     },
7347     filterKeys : function(e){
7348         var k = e.getKey();
7349         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
7350             return;
7351         }
7352         var c = e.getCharCode(), cc = String.fromCharCode(c);
7353         if(Roo.isIE && (e.isSpecialKey() || !cc)){
7354             return;
7355         }
7356         if(!this.maskRe.test(cc)){
7357             e.stopEvent();
7358         }
7359     },
7360      /**
7361      * Clear any invalid styles/messages for this field
7362      */
7363     clearInvalid : function(){
7364         
7365         if(!this.el || this.preventMark){ // not rendered
7366             return;
7367         }
7368         this.el.removeClass(this.invalidClass);
7369         /*
7370         switch(this.msgTarget){
7371             case 'qtip':
7372                 this.el.dom.qtip = '';
7373                 break;
7374             case 'title':
7375                 this.el.dom.title = '';
7376                 break;
7377             case 'under':
7378                 if(this.errorEl){
7379                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
7380                 }
7381                 break;
7382             case 'side':
7383                 if(this.errorIcon){
7384                     this.errorIcon.dom.qtip = '';
7385                     this.errorIcon.hide();
7386                     this.un('resize', this.alignErrorIcon, this);
7387                 }
7388                 break;
7389             default:
7390                 var t = Roo.getDom(this.msgTarget);
7391                 t.innerHTML = '';
7392                 t.style.display = 'none';
7393                 break;
7394         }
7395         */
7396         this.fireEvent('valid', this);
7397     },
7398      /**
7399      * Mark this field as invalid
7400      * @param {String} msg The validation message
7401      */
7402     markInvalid : function(msg){
7403         if(!this.el  || this.preventMark){ // not rendered
7404             return;
7405         }
7406         this.el.addClass(this.invalidClass);
7407         /*
7408         msg = msg || this.invalidText;
7409         switch(this.msgTarget){
7410             case 'qtip':
7411                 this.el.dom.qtip = msg;
7412                 this.el.dom.qclass = 'x-form-invalid-tip';
7413                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
7414                     Roo.QuickTips.enable();
7415                 }
7416                 break;
7417             case 'title':
7418                 this.el.dom.title = msg;
7419                 break;
7420             case 'under':
7421                 if(!this.errorEl){
7422                     var elp = this.el.findParent('.x-form-element', 5, true);
7423                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
7424                     this.errorEl.setWidth(elp.getWidth(true)-20);
7425                 }
7426                 this.errorEl.update(msg);
7427                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
7428                 break;
7429             case 'side':
7430                 if(!this.errorIcon){
7431                     var elp = this.el.findParent('.x-form-element', 5, true);
7432                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
7433                 }
7434                 this.alignErrorIcon();
7435                 this.errorIcon.dom.qtip = msg;
7436                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
7437                 this.errorIcon.show();
7438                 this.on('resize', this.alignErrorIcon, this);
7439                 break;
7440             default:
7441                 var t = Roo.getDom(this.msgTarget);
7442                 t.innerHTML = msg;
7443                 t.style.display = this.msgDisplay;
7444                 break;
7445         }
7446         */
7447         this.fireEvent('invalid', this, msg);
7448     },
7449     // private
7450     SafariOnKeyDown : function(event)
7451     {
7452         // this is a workaround for a password hang bug on chrome/ webkit.
7453         
7454         var isSelectAll = false;
7455         
7456         if(this.inputEl().dom.selectionEnd > 0){
7457             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
7458         }
7459         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
7460             event.preventDefault();
7461             this.setValue('');
7462             return;
7463         }
7464         
7465         if(isSelectAll){ // backspace and delete key
7466             
7467             event.preventDefault();
7468             // this is very hacky as keydown always get's upper case.
7469             //
7470             var cc = String.fromCharCode(event.getCharCode());
7471             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
7472             
7473         }
7474     },
7475     adjustWidth : function(tag, w){
7476         tag = tag.toLowerCase();
7477         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
7478             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
7479                 if(tag == 'input'){
7480                     return w + 2;
7481                 }
7482                 if(tag == 'textarea'){
7483                     return w-2;
7484                 }
7485             }else if(Roo.isOpera){
7486                 if(tag == 'input'){
7487                     return w + 2;
7488                 }
7489                 if(tag == 'textarea'){
7490                     return w-2;
7491                 }
7492             }
7493         }
7494         return w;
7495     }
7496     
7497 });
7498
7499  
7500 /*
7501  * - LGPL
7502  *
7503  * Input
7504  * 
7505  */
7506
7507 /**
7508  * @class Roo.bootstrap.TextArea
7509  * @extends Roo.bootstrap.Input
7510  * Bootstrap TextArea class
7511  * @cfg {Number} cols Specifies the visible width of a text area
7512  * @cfg {Number} rows Specifies the visible number of lines in a text area
7513  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
7514  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
7515  * @cfg {string} html text
7516  * 
7517  * @constructor
7518  * Create a new TextArea
7519  * @param {Object} config The config object
7520  */
7521
7522 Roo.bootstrap.TextArea = function(config){
7523     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
7524    
7525 };
7526
7527 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
7528      
7529     cols : false,
7530     rows : 5,
7531     readOnly : false,
7532     warp : 'soft',
7533     resize : false,
7534     value: false,
7535     html: false,
7536     
7537     getAutoCreate : function(){
7538         
7539         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7540         
7541         var id = Roo.id();
7542         
7543         var cfg = {};
7544         
7545         var input =  {
7546             tag: 'textarea',
7547             id : id,
7548             warp : this.warp,
7549             rows : this.rows,
7550             value : this.value || '',
7551             html: this.html || '',
7552             cls : 'form-control',
7553             placeholder : this.placeholder || '' 
7554             
7555         };
7556         
7557         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7558             input.maxLength = this.maxLength;
7559         }
7560         
7561         if(this.resize){
7562             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
7563         }
7564         
7565         if(this.cols){
7566             input.cols = this.cols;
7567         }
7568         
7569         if (this.readOnly) {
7570             input.readonly = true;
7571         }
7572         
7573         if (this.name) {
7574             input.name = this.name;
7575         }
7576         
7577         if (this.size) {
7578             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
7579         }
7580         
7581         var settings=this;
7582         ['xs','sm','md','lg'].map(function(size){
7583             if (settings[size]) {
7584                 cfg.cls += ' col-' + size + '-' + settings[size];
7585             }
7586         });
7587         
7588         var inputblock = input;
7589         
7590         if (this.before || this.after) {
7591             
7592             inputblock = {
7593                 cls : 'input-group',
7594                 cn :  [] 
7595             };
7596             if (this.before) {
7597                 inputblock.cn.push({
7598                     tag :'span',
7599                     cls : 'input-group-addon',
7600                     html : this.before
7601                 });
7602             }
7603             inputblock.cn.push(input);
7604             if (this.after) {
7605                 inputblock.cn.push({
7606                     tag :'span',
7607                     cls : 'input-group-addon',
7608                     html : this.after
7609                 });
7610             }
7611             
7612         }
7613         
7614         if (align ==='left' && this.fieldLabel.length) {
7615                 Roo.log("left and has label");
7616                 cfg.cn = [
7617                     
7618                     {
7619                         tag: 'label',
7620                         'for' :  id,
7621                         cls : 'control-label col-sm-' + this.labelWidth,
7622                         html : this.fieldLabel
7623                         
7624                     },
7625                     {
7626                         cls : "col-sm-" + (12 - this.labelWidth), 
7627                         cn: [
7628                             inputblock
7629                         ]
7630                     }
7631                     
7632                 ];
7633         } else if ( this.fieldLabel.length) {
7634                 Roo.log(" label");
7635                  cfg.cn = [
7636                    
7637                     {
7638                         tag: 'label',
7639                         //cls : 'input-group-addon',
7640                         html : this.fieldLabel
7641                         
7642                     },
7643                     
7644                     inputblock
7645                     
7646                 ];
7647
7648         } else {
7649             
7650                    Roo.log(" no label && no align");
7651                 cfg.cn = [
7652                     
7653                         inputblock
7654                     
7655                 ];
7656                 
7657                 
7658         }
7659         
7660         if (this.disabled) {
7661             input.disabled=true;
7662         }
7663         
7664         return cfg;
7665         
7666     },
7667     /**
7668      * return the real textarea element.
7669      */
7670     inputEl: function ()
7671     {
7672         return this.el.select('textarea.form-control',true).first();
7673     }
7674 });
7675
7676  
7677 /*
7678  * - LGPL
7679  *
7680  * trigger field - base class for combo..
7681  * 
7682  */
7683  
7684 /**
7685  * @class Roo.bootstrap.TriggerField
7686  * @extends Roo.bootstrap.Input
7687  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
7688  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
7689  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
7690  * for which you can provide a custom implementation.  For example:
7691  * <pre><code>
7692 var trigger = new Roo.bootstrap.TriggerField();
7693 trigger.onTriggerClick = myTriggerFn;
7694 trigger.applyTo('my-field');
7695 </code></pre>
7696  *
7697  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
7698  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
7699  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
7700  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
7701  * @constructor
7702  * Create a new TriggerField.
7703  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
7704  * to the base TextField)
7705  */
7706 Roo.bootstrap.TriggerField = function(config){
7707     this.mimicing = false;
7708     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
7709 };
7710
7711 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
7712     /**
7713      * @cfg {String} triggerClass A CSS class to apply to the trigger
7714      */
7715      /**
7716      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
7717      */
7718     hideTrigger:false,
7719
7720     /** @cfg {Boolean} grow @hide */
7721     /** @cfg {Number} growMin @hide */
7722     /** @cfg {Number} growMax @hide */
7723
7724     /**
7725      * @hide 
7726      * @method
7727      */
7728     autoSize: Roo.emptyFn,
7729     // private
7730     monitorTab : true,
7731     // private
7732     deferHeight : true,
7733
7734     
7735     actionMode : 'wrap',
7736     
7737     
7738     
7739     getAutoCreate : function(){
7740        
7741         var align = this.labelAlign || this.parentLabelAlign();
7742         
7743         var id = Roo.id();
7744         
7745         var cfg = {
7746             cls: 'form-group' //input-group
7747         };
7748         
7749         
7750         var input =  {
7751             tag: 'input',
7752             id : id,
7753             type : this.inputType,
7754             cls : 'form-control',
7755             autocomplete: 'off',
7756             placeholder : this.placeholder || '' 
7757             
7758         };
7759         if (this.name) {
7760             input.name = this.name;
7761         }
7762         if (this.size) {
7763             input.cls += ' input-' + this.size;
7764         }
7765         
7766         if (this.disabled) {
7767             input.disabled=true;
7768         }
7769         
7770         var inputblock = input;
7771         
7772         if (this.before || this.after) {
7773             
7774             inputblock = {
7775                 cls : 'input-group',
7776                 cn :  [] 
7777             };
7778             if (this.before) {
7779                 inputblock.cn.push({
7780                     tag :'span',
7781                     cls : 'input-group-addon',
7782                     html : this.before
7783                 });
7784             }
7785             inputblock.cn.push(input);
7786             if (this.after) {
7787                 inputblock.cn.push({
7788                     tag :'span',
7789                     cls : 'input-group-addon',
7790                     html : this.after
7791                 });
7792             }
7793             
7794         };
7795         
7796         var box = {
7797             tag: 'div',
7798             cn: [
7799                 {
7800                     tag: 'input',
7801                     type : 'hidden',
7802                     cls: 'form-hidden-field'
7803                 },
7804                 inputblock
7805             ]
7806             
7807         };
7808         
7809         if(this.multiple){
7810             Roo.log('multiple');
7811             
7812             box = {
7813                 tag: 'div',
7814                 cn: [
7815                     {
7816                         tag: 'input',
7817                         type : 'hidden',
7818                         cls: 'form-hidden-field'
7819                     },
7820                     {
7821                         tag: 'ul',
7822                         cls: 'select2-choices',
7823                         cn:[
7824                             {
7825                                 tag: 'li',
7826                                 cls: 'select2-search-field',
7827                                 cn: [
7828
7829                                     inputblock
7830                                 ]
7831                             }
7832                         ]
7833                     }
7834                 ]
7835             }
7836         };
7837         
7838         var combobox = {
7839             cls: 'select2-container input-group',
7840             cn: [
7841                 box
7842 //                {
7843 //                    tag: 'ul',
7844 //                    cls: 'typeahead typeahead-long dropdown-menu',
7845 //                    style: 'display:none'
7846 //                }
7847             ]
7848         };
7849         
7850         if(!this.multiple && this.showToggleBtn){
7851             combobox.cn.push({
7852                 tag :'span',
7853                 cls : 'input-group-addon btn dropdown-toggle',
7854                 cn : [
7855                     {
7856                         tag: 'span',
7857                         cls: 'caret'
7858                     },
7859                     {
7860                         tag: 'span',
7861                         cls: 'combobox-clear',
7862                         cn  : [
7863                             {
7864                                 tag : 'i',
7865                                 cls: 'icon-remove'
7866                             }
7867                         ]
7868                     }
7869                 ]
7870
7871             })
7872         }
7873         
7874         if(this.multiple){
7875             combobox.cls += ' select2-container-multi';
7876         }
7877         
7878         if (align ==='left' && this.fieldLabel.length) {
7879             
7880                 Roo.log("left and has label");
7881                 cfg.cn = [
7882                     
7883                     {
7884                         tag: 'label',
7885                         'for' :  id,
7886                         cls : 'control-label col-sm-' + this.labelWidth,
7887                         html : this.fieldLabel
7888                         
7889                     },
7890                     {
7891                         cls : "col-sm-" + (12 - this.labelWidth), 
7892                         cn: [
7893                             combobox
7894                         ]
7895                     }
7896                     
7897                 ];
7898         } else if ( this.fieldLabel.length) {
7899                 Roo.log(" label");
7900                  cfg.cn = [
7901                    
7902                     {
7903                         tag: 'label',
7904                         //cls : 'input-group-addon',
7905                         html : this.fieldLabel
7906                         
7907                     },
7908                     
7909                     combobox
7910                     
7911                 ];
7912
7913         } else {
7914             
7915                 Roo.log(" no label && no align");
7916                 cfg = combobox
7917                      
7918                 
7919         }
7920          
7921         var settings=this;
7922         ['xs','sm','md','lg'].map(function(size){
7923             if (settings[size]) {
7924                 cfg.cls += ' col-' + size + '-' + settings[size];
7925             }
7926         });
7927         
7928         return cfg;
7929         
7930     },
7931     
7932     
7933     
7934     // private
7935     onResize : function(w, h){
7936 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
7937 //        if(typeof w == 'number'){
7938 //            var x = w - this.trigger.getWidth();
7939 //            this.inputEl().setWidth(this.adjustWidth('input', x));
7940 //            this.trigger.setStyle('left', x+'px');
7941 //        }
7942     },
7943
7944     // private
7945     adjustSize : Roo.BoxComponent.prototype.adjustSize,
7946
7947     // private
7948     getResizeEl : function(){
7949         return this.inputEl();
7950     },
7951
7952     // private
7953     getPositionEl : function(){
7954         return this.inputEl();
7955     },
7956
7957     // private
7958     alignErrorIcon : function(){
7959         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
7960     },
7961
7962     // private
7963     initEvents : function(){
7964         
7965         this.createList();
7966         
7967         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
7968         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
7969         if(!this.multiple && this.showToggleBtn){
7970             this.trigger = this.el.select('span.dropdown-toggle',true).first();
7971             if(this.hideTrigger){
7972                 this.trigger.setDisplayed(false);
7973             }
7974             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
7975         }
7976         
7977         if(this.multiple){
7978             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
7979         }
7980         
7981         //this.trigger.addClassOnOver('x-form-trigger-over');
7982         //this.trigger.addClassOnClick('x-form-trigger-click');
7983         
7984         //if(!this.width){
7985         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
7986         //}
7987     },
7988     
7989     createList : function()
7990     {
7991         this.list = Roo.get(document.body).createChild({
7992             tag: 'ul',
7993             cls: 'typeahead typeahead-long dropdown-menu',
7994             style: 'display:none'
7995         });
7996         
7997         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
7998         
7999     },
8000
8001     // private
8002     initTrigger : function(){
8003        
8004     },
8005
8006     // private
8007     onDestroy : function(){
8008         if(this.trigger){
8009             this.trigger.removeAllListeners();
8010           //  this.trigger.remove();
8011         }
8012         //if(this.wrap){
8013         //    this.wrap.remove();
8014         //}
8015         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
8016     },
8017
8018     // private
8019     onFocus : function(){
8020         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
8021         /*
8022         if(!this.mimicing){
8023             this.wrap.addClass('x-trigger-wrap-focus');
8024             this.mimicing = true;
8025             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
8026             if(this.monitorTab){
8027                 this.el.on("keydown", this.checkTab, this);
8028             }
8029         }
8030         */
8031     },
8032
8033     // private
8034     checkTab : function(e){
8035         if(e.getKey() == e.TAB){
8036             this.triggerBlur();
8037         }
8038     },
8039
8040     // private
8041     onBlur : function(){
8042         // do nothing
8043     },
8044
8045     // private
8046     mimicBlur : function(e, t){
8047         /*
8048         if(!this.wrap.contains(t) && this.validateBlur()){
8049             this.triggerBlur();
8050         }
8051         */
8052     },
8053
8054     // private
8055     triggerBlur : function(){
8056         this.mimicing = false;
8057         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
8058         if(this.monitorTab){
8059             this.el.un("keydown", this.checkTab, this);
8060         }
8061         //this.wrap.removeClass('x-trigger-wrap-focus');
8062         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
8063     },
8064
8065     // private
8066     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
8067     validateBlur : function(e, t){
8068         return true;
8069     },
8070
8071     // private
8072     onDisable : function(){
8073         this.inputEl().dom.disabled = true;
8074         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
8075         //if(this.wrap){
8076         //    this.wrap.addClass('x-item-disabled');
8077         //}
8078     },
8079
8080     // private
8081     onEnable : function(){
8082         this.inputEl().dom.disabled = false;
8083         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
8084         //if(this.wrap){
8085         //    this.el.removeClass('x-item-disabled');
8086         //}
8087     },
8088
8089     // private
8090     onShow : function(){
8091         var ae = this.getActionEl();
8092         
8093         if(ae){
8094             ae.dom.style.display = '';
8095             ae.dom.style.visibility = 'visible';
8096         }
8097     },
8098
8099     // private
8100     
8101     onHide : function(){
8102         var ae = this.getActionEl();
8103         ae.dom.style.display = 'none';
8104     },
8105
8106     /**
8107      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
8108      * by an implementing function.
8109      * @method
8110      * @param {EventObject} e
8111      */
8112     onTriggerClick : Roo.emptyFn
8113 });
8114  /*
8115  * Based on:
8116  * Ext JS Library 1.1.1
8117  * Copyright(c) 2006-2007, Ext JS, LLC.
8118  *
8119  * Originally Released Under LGPL - original licence link has changed is not relivant.
8120  *
8121  * Fork - LGPL
8122  * <script type="text/javascript">
8123  */
8124
8125
8126 /**
8127  * @class Roo.data.SortTypes
8128  * @singleton
8129  * Defines the default sorting (casting?) comparison functions used when sorting data.
8130  */
8131 Roo.data.SortTypes = {
8132     /**
8133      * Default sort that does nothing
8134      * @param {Mixed} s The value being converted
8135      * @return {Mixed} The comparison value
8136      */
8137     none : function(s){
8138         return s;
8139     },
8140     
8141     /**
8142      * The regular expression used to strip tags
8143      * @type {RegExp}
8144      * @property
8145      */
8146     stripTagsRE : /<\/?[^>]+>/gi,
8147     
8148     /**
8149      * Strips all HTML tags to sort on text only
8150      * @param {Mixed} s The value being converted
8151      * @return {String} The comparison value
8152      */
8153     asText : function(s){
8154         return String(s).replace(this.stripTagsRE, "");
8155     },
8156     
8157     /**
8158      * Strips all HTML tags to sort on text only - Case insensitive
8159      * @param {Mixed} s The value being converted
8160      * @return {String} The comparison value
8161      */
8162     asUCText : function(s){
8163         return String(s).toUpperCase().replace(this.stripTagsRE, "");
8164     },
8165     
8166     /**
8167      * Case insensitive string
8168      * @param {Mixed} s The value being converted
8169      * @return {String} The comparison value
8170      */
8171     asUCString : function(s) {
8172         return String(s).toUpperCase();
8173     },
8174     
8175     /**
8176      * Date sorting
8177      * @param {Mixed} s The value being converted
8178      * @return {Number} The comparison value
8179      */
8180     asDate : function(s) {
8181         if(!s){
8182             return 0;
8183         }
8184         if(s instanceof Date){
8185             return s.getTime();
8186         }
8187         return Date.parse(String(s));
8188     },
8189     
8190     /**
8191      * Float sorting
8192      * @param {Mixed} s The value being converted
8193      * @return {Float} The comparison value
8194      */
8195     asFloat : function(s) {
8196         var val = parseFloat(String(s).replace(/,/g, ""));
8197         if(isNaN(val)) val = 0;
8198         return val;
8199     },
8200     
8201     /**
8202      * Integer sorting
8203      * @param {Mixed} s The value being converted
8204      * @return {Number} The comparison value
8205      */
8206     asInt : function(s) {
8207         var val = parseInt(String(s).replace(/,/g, ""));
8208         if(isNaN(val)) val = 0;
8209         return val;
8210     }
8211 };/*
8212  * Based on:
8213  * Ext JS Library 1.1.1
8214  * Copyright(c) 2006-2007, Ext JS, LLC.
8215  *
8216  * Originally Released Under LGPL - original licence link has changed is not relivant.
8217  *
8218  * Fork - LGPL
8219  * <script type="text/javascript">
8220  */
8221
8222 /**
8223 * @class Roo.data.Record
8224  * Instances of this class encapsulate both record <em>definition</em> information, and record
8225  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
8226  * to access Records cached in an {@link Roo.data.Store} object.<br>
8227  * <p>
8228  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
8229  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
8230  * objects.<br>
8231  * <p>
8232  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
8233  * @constructor
8234  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
8235  * {@link #create}. The parameters are the same.
8236  * @param {Array} data An associative Array of data values keyed by the field name.
8237  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
8238  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
8239  * not specified an integer id is generated.
8240  */
8241 Roo.data.Record = function(data, id){
8242     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
8243     this.data = data;
8244 };
8245
8246 /**
8247  * Generate a constructor for a specific record layout.
8248  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
8249  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
8250  * Each field definition object may contain the following properties: <ul>
8251  * <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,
8252  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
8253  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
8254  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
8255  * is being used, then this is a string containing the javascript expression to reference the data relative to 
8256  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
8257  * to the data item relative to the record element. If the mapping expression is the same as the field name,
8258  * this may be omitted.</p></li>
8259  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
8260  * <ul><li>auto (Default, implies no conversion)</li>
8261  * <li>string</li>
8262  * <li>int</li>
8263  * <li>float</li>
8264  * <li>boolean</li>
8265  * <li>date</li></ul></p></li>
8266  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
8267  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
8268  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
8269  * by the Reader into an object that will be stored in the Record. It is passed the
8270  * following parameters:<ul>
8271  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
8272  * </ul></p></li>
8273  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
8274  * </ul>
8275  * <br>usage:<br><pre><code>
8276 var TopicRecord = Roo.data.Record.create(
8277     {name: 'title', mapping: 'topic_title'},
8278     {name: 'author', mapping: 'username'},
8279     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
8280     {name: 'lastPost', mapping: 'post_time', type: 'date'},
8281     {name: 'lastPoster', mapping: 'user2'},
8282     {name: 'excerpt', mapping: 'post_text'}
8283 );
8284
8285 var myNewRecord = new TopicRecord({
8286     title: 'Do my job please',
8287     author: 'noobie',
8288     totalPosts: 1,
8289     lastPost: new Date(),
8290     lastPoster: 'Animal',
8291     excerpt: 'No way dude!'
8292 });
8293 myStore.add(myNewRecord);
8294 </code></pre>
8295  * @method create
8296  * @static
8297  */
8298 Roo.data.Record.create = function(o){
8299     var f = function(){
8300         f.superclass.constructor.apply(this, arguments);
8301     };
8302     Roo.extend(f, Roo.data.Record);
8303     var p = f.prototype;
8304     p.fields = new Roo.util.MixedCollection(false, function(field){
8305         return field.name;
8306     });
8307     for(var i = 0, len = o.length; i < len; i++){
8308         p.fields.add(new Roo.data.Field(o[i]));
8309     }
8310     f.getField = function(name){
8311         return p.fields.get(name);  
8312     };
8313     return f;
8314 };
8315
8316 Roo.data.Record.AUTO_ID = 1000;
8317 Roo.data.Record.EDIT = 'edit';
8318 Roo.data.Record.REJECT = 'reject';
8319 Roo.data.Record.COMMIT = 'commit';
8320
8321 Roo.data.Record.prototype = {
8322     /**
8323      * Readonly flag - true if this record has been modified.
8324      * @type Boolean
8325      */
8326     dirty : false,
8327     editing : false,
8328     error: null,
8329     modified: null,
8330
8331     // private
8332     join : function(store){
8333         this.store = store;
8334     },
8335
8336     /**
8337      * Set the named field to the specified value.
8338      * @param {String} name The name of the field to set.
8339      * @param {Object} value The value to set the field to.
8340      */
8341     set : function(name, value){
8342         if(this.data[name] == value){
8343             return;
8344         }
8345         this.dirty = true;
8346         if(!this.modified){
8347             this.modified = {};
8348         }
8349         if(typeof this.modified[name] == 'undefined'){
8350             this.modified[name] = this.data[name];
8351         }
8352         this.data[name] = value;
8353         if(!this.editing && this.store){
8354             this.store.afterEdit(this);
8355         }       
8356     },
8357
8358     /**
8359      * Get the value of the named field.
8360      * @param {String} name The name of the field to get the value of.
8361      * @return {Object} The value of the field.
8362      */
8363     get : function(name){
8364         return this.data[name]; 
8365     },
8366
8367     // private
8368     beginEdit : function(){
8369         this.editing = true;
8370         this.modified = {}; 
8371     },
8372
8373     // private
8374     cancelEdit : function(){
8375         this.editing = false;
8376         delete this.modified;
8377     },
8378
8379     // private
8380     endEdit : function(){
8381         this.editing = false;
8382         if(this.dirty && this.store){
8383             this.store.afterEdit(this);
8384         }
8385     },
8386
8387     /**
8388      * Usually called by the {@link Roo.data.Store} which owns the Record.
8389      * Rejects all changes made to the Record since either creation, or the last commit operation.
8390      * Modified fields are reverted to their original values.
8391      * <p>
8392      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8393      * of reject operations.
8394      */
8395     reject : function(){
8396         var m = this.modified;
8397         for(var n in m){
8398             if(typeof m[n] != "function"){
8399                 this.data[n] = m[n];
8400             }
8401         }
8402         this.dirty = false;
8403         delete this.modified;
8404         this.editing = false;
8405         if(this.store){
8406             this.store.afterReject(this);
8407         }
8408     },
8409
8410     /**
8411      * Usually called by the {@link Roo.data.Store} which owns the Record.
8412      * Commits all changes made to the Record since either creation, or the last commit operation.
8413      * <p>
8414      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8415      * of commit operations.
8416      */
8417     commit : function(){
8418         this.dirty = false;
8419         delete this.modified;
8420         this.editing = false;
8421         if(this.store){
8422             this.store.afterCommit(this);
8423         }
8424     },
8425
8426     // private
8427     hasError : function(){
8428         return this.error != null;
8429     },
8430
8431     // private
8432     clearError : function(){
8433         this.error = null;
8434     },
8435
8436     /**
8437      * Creates a copy of this record.
8438      * @param {String} id (optional) A new record id if you don't want to use this record's id
8439      * @return {Record}
8440      */
8441     copy : function(newId) {
8442         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
8443     }
8444 };/*
8445  * Based on:
8446  * Ext JS Library 1.1.1
8447  * Copyright(c) 2006-2007, Ext JS, LLC.
8448  *
8449  * Originally Released Under LGPL - original licence link has changed is not relivant.
8450  *
8451  * Fork - LGPL
8452  * <script type="text/javascript">
8453  */
8454
8455
8456
8457 /**
8458  * @class Roo.data.Store
8459  * @extends Roo.util.Observable
8460  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
8461  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
8462  * <p>
8463  * 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
8464  * has no knowledge of the format of the data returned by the Proxy.<br>
8465  * <p>
8466  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
8467  * instances from the data object. These records are cached and made available through accessor functions.
8468  * @constructor
8469  * Creates a new Store.
8470  * @param {Object} config A config object containing the objects needed for the Store to access data,
8471  * and read the data into Records.
8472  */
8473 Roo.data.Store = function(config){
8474     this.data = new Roo.util.MixedCollection(false);
8475     this.data.getKey = function(o){
8476         return o.id;
8477     };
8478     this.baseParams = {};
8479     // private
8480     this.paramNames = {
8481         "start" : "start",
8482         "limit" : "limit",
8483         "sort" : "sort",
8484         "dir" : "dir",
8485         "multisort" : "_multisort"
8486     };
8487
8488     if(config && config.data){
8489         this.inlineData = config.data;
8490         delete config.data;
8491     }
8492
8493     Roo.apply(this, config);
8494     
8495     if(this.reader){ // reader passed
8496         this.reader = Roo.factory(this.reader, Roo.data);
8497         this.reader.xmodule = this.xmodule || false;
8498         if(!this.recordType){
8499             this.recordType = this.reader.recordType;
8500         }
8501         if(this.reader.onMetaChange){
8502             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
8503         }
8504     }
8505
8506     if(this.recordType){
8507         this.fields = this.recordType.prototype.fields;
8508     }
8509     this.modified = [];
8510
8511     this.addEvents({
8512         /**
8513          * @event datachanged
8514          * Fires when the data cache has changed, and a widget which is using this Store
8515          * as a Record cache should refresh its view.
8516          * @param {Store} this
8517          */
8518         datachanged : true,
8519         /**
8520          * @event metachange
8521          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
8522          * @param {Store} this
8523          * @param {Object} meta The JSON metadata
8524          */
8525         metachange : true,
8526         /**
8527          * @event add
8528          * Fires when Records have been added to the Store
8529          * @param {Store} this
8530          * @param {Roo.data.Record[]} records The array of Records added
8531          * @param {Number} index The index at which the record(s) were added
8532          */
8533         add : true,
8534         /**
8535          * @event remove
8536          * Fires when a Record has been removed from the Store
8537          * @param {Store} this
8538          * @param {Roo.data.Record} record The Record that was removed
8539          * @param {Number} index The index at which the record was removed
8540          */
8541         remove : true,
8542         /**
8543          * @event update
8544          * Fires when a Record has been updated
8545          * @param {Store} this
8546          * @param {Roo.data.Record} record The Record that was updated
8547          * @param {String} operation The update operation being performed.  Value may be one of:
8548          * <pre><code>
8549  Roo.data.Record.EDIT
8550  Roo.data.Record.REJECT
8551  Roo.data.Record.COMMIT
8552          * </code></pre>
8553          */
8554         update : true,
8555         /**
8556          * @event clear
8557          * Fires when the data cache has been cleared.
8558          * @param {Store} this
8559          */
8560         clear : true,
8561         /**
8562          * @event beforeload
8563          * Fires before a request is made for a new data object.  If the beforeload handler returns false
8564          * the load action will be canceled.
8565          * @param {Store} this
8566          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8567          */
8568         beforeload : true,
8569         /**
8570          * @event beforeloadadd
8571          * Fires after a new set of Records has been loaded.
8572          * @param {Store} this
8573          * @param {Roo.data.Record[]} records The Records that were loaded
8574          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8575          */
8576         beforeloadadd : true,
8577         /**
8578          * @event load
8579          * Fires after a new set of Records has been loaded, before they are added to the store.
8580          * @param {Store} this
8581          * @param {Roo.data.Record[]} records The Records that were loaded
8582          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8583          * @params {Object} return from reader
8584          */
8585         load : true,
8586         /**
8587          * @event loadexception
8588          * Fires if an exception occurs in the Proxy during loading.
8589          * Called with the signature of the Proxy's "loadexception" event.
8590          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
8591          * 
8592          * @param {Proxy} 
8593          * @param {Object} return from JsonData.reader() - success, totalRecords, records
8594          * @param {Object} load options 
8595          * @param {Object} jsonData from your request (normally this contains the Exception)
8596          */
8597         loadexception : true
8598     });
8599     
8600     if(this.proxy){
8601         this.proxy = Roo.factory(this.proxy, Roo.data);
8602         this.proxy.xmodule = this.xmodule || false;
8603         this.relayEvents(this.proxy,  ["loadexception"]);
8604     }
8605     this.sortToggle = {};
8606     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
8607
8608     Roo.data.Store.superclass.constructor.call(this);
8609
8610     if(this.inlineData){
8611         this.loadData(this.inlineData);
8612         delete this.inlineData;
8613     }
8614 };
8615
8616 Roo.extend(Roo.data.Store, Roo.util.Observable, {
8617      /**
8618     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
8619     * without a remote query - used by combo/forms at present.
8620     */
8621     
8622     /**
8623     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
8624     */
8625     /**
8626     * @cfg {Array} data Inline data to be loaded when the store is initialized.
8627     */
8628     /**
8629     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
8630     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
8631     */
8632     /**
8633     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
8634     * on any HTTP request
8635     */
8636     /**
8637     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
8638     */
8639     /**
8640     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
8641     */
8642     multiSort: false,
8643     /**
8644     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
8645     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
8646     */
8647     remoteSort : false,
8648
8649     /**
8650     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
8651      * loaded or when a record is removed. (defaults to false).
8652     */
8653     pruneModifiedRecords : false,
8654
8655     // private
8656     lastOptions : null,
8657
8658     /**
8659      * Add Records to the Store and fires the add event.
8660      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8661      */
8662     add : function(records){
8663         records = [].concat(records);
8664         for(var i = 0, len = records.length; i < len; i++){
8665             records[i].join(this);
8666         }
8667         var index = this.data.length;
8668         this.data.addAll(records);
8669         this.fireEvent("add", this, records, index);
8670     },
8671
8672     /**
8673      * Remove a Record from the Store and fires the remove event.
8674      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
8675      */
8676     remove : function(record){
8677         var index = this.data.indexOf(record);
8678         this.data.removeAt(index);
8679         if(this.pruneModifiedRecords){
8680             this.modified.remove(record);
8681         }
8682         this.fireEvent("remove", this, record, index);
8683     },
8684
8685     /**
8686      * Remove all Records from the Store and fires the clear event.
8687      */
8688     removeAll : function(){
8689         this.data.clear();
8690         if(this.pruneModifiedRecords){
8691             this.modified = [];
8692         }
8693         this.fireEvent("clear", this);
8694     },
8695
8696     /**
8697      * Inserts Records to the Store at the given index and fires the add event.
8698      * @param {Number} index The start index at which to insert the passed Records.
8699      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8700      */
8701     insert : function(index, records){
8702         records = [].concat(records);
8703         for(var i = 0, len = records.length; i < len; i++){
8704             this.data.insert(index, records[i]);
8705             records[i].join(this);
8706         }
8707         this.fireEvent("add", this, records, index);
8708     },
8709
8710     /**
8711      * Get the index within the cache of the passed Record.
8712      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
8713      * @return {Number} The index of the passed Record. Returns -1 if not found.
8714      */
8715     indexOf : function(record){
8716         return this.data.indexOf(record);
8717     },
8718
8719     /**
8720      * Get the index within the cache of the Record with the passed id.
8721      * @param {String} id The id of the Record to find.
8722      * @return {Number} The index of the Record. Returns -1 if not found.
8723      */
8724     indexOfId : function(id){
8725         return this.data.indexOfKey(id);
8726     },
8727
8728     /**
8729      * Get the Record with the specified id.
8730      * @param {String} id The id of the Record to find.
8731      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
8732      */
8733     getById : function(id){
8734         return this.data.key(id);
8735     },
8736
8737     /**
8738      * Get the Record at the specified index.
8739      * @param {Number} index The index of the Record to find.
8740      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
8741      */
8742     getAt : function(index){
8743         return this.data.itemAt(index);
8744     },
8745
8746     /**
8747      * Returns a range of Records between specified indices.
8748      * @param {Number} startIndex (optional) The starting index (defaults to 0)
8749      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
8750      * @return {Roo.data.Record[]} An array of Records
8751      */
8752     getRange : function(start, end){
8753         return this.data.getRange(start, end);
8754     },
8755
8756     // private
8757     storeOptions : function(o){
8758         o = Roo.apply({}, o);
8759         delete o.callback;
8760         delete o.scope;
8761         this.lastOptions = o;
8762     },
8763
8764     /**
8765      * Loads the Record cache from the configured Proxy using the configured Reader.
8766      * <p>
8767      * If using remote paging, then the first load call must specify the <em>start</em>
8768      * and <em>limit</em> properties in the options.params property to establish the initial
8769      * position within the dataset, and the number of Records to cache on each read from the Proxy.
8770      * <p>
8771      * <strong>It is important to note that for remote data sources, loading is asynchronous,
8772      * and this call will return before the new data has been loaded. Perform any post-processing
8773      * in a callback function, or in a "load" event handler.</strong>
8774      * <p>
8775      * @param {Object} options An object containing properties which control loading options:<ul>
8776      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
8777      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
8778      * passed the following arguments:<ul>
8779      * <li>r : Roo.data.Record[]</li>
8780      * <li>options: Options object from the load call</li>
8781      * <li>success: Boolean success indicator</li></ul></li>
8782      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
8783      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
8784      * </ul>
8785      */
8786     load : function(options){
8787         options = options || {};
8788         if(this.fireEvent("beforeload", this, options) !== false){
8789             this.storeOptions(options);
8790             var p = Roo.apply(options.params || {}, this.baseParams);
8791             // if meta was not loaded from remote source.. try requesting it.
8792             if (!this.reader.metaFromRemote) {
8793                 p._requestMeta = 1;
8794             }
8795             if(this.sortInfo && this.remoteSort){
8796                 var pn = this.paramNames;
8797                 p[pn["sort"]] = this.sortInfo.field;
8798                 p[pn["dir"]] = this.sortInfo.direction;
8799             }
8800             if (this.multiSort) {
8801                 var pn = this.paramNames;
8802                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
8803             }
8804             
8805             this.proxy.load(p, this.reader, this.loadRecords, this, options);
8806         }
8807     },
8808
8809     /**
8810      * Reloads the Record cache from the configured Proxy using the configured Reader and
8811      * the options from the last load operation performed.
8812      * @param {Object} options (optional) An object containing properties which may override the options
8813      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
8814      * the most recently used options are reused).
8815      */
8816     reload : function(options){
8817         this.load(Roo.applyIf(options||{}, this.lastOptions));
8818     },
8819
8820     // private
8821     // Called as a callback by the Reader during a load operation.
8822     loadRecords : function(o, options, success){
8823         if(!o || success === false){
8824             if(success !== false){
8825                 this.fireEvent("load", this, [], options, o);
8826             }
8827             if(options.callback){
8828                 options.callback.call(options.scope || this, [], options, false);
8829             }
8830             return;
8831         }
8832         // if data returned failure - throw an exception.
8833         if (o.success === false) {
8834             // show a message if no listener is registered.
8835             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
8836                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
8837             }
8838             // loadmask wil be hooked into this..
8839             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
8840             return;
8841         }
8842         var r = o.records, t = o.totalRecords || r.length;
8843         
8844         this.fireEvent("beforeloadadd", this, r, options, o);
8845         
8846         if(!options || options.add !== true){
8847             if(this.pruneModifiedRecords){
8848                 this.modified = [];
8849             }
8850             for(var i = 0, len = r.length; i < len; i++){
8851                 r[i].join(this);
8852             }
8853             if(this.snapshot){
8854                 this.data = this.snapshot;
8855                 delete this.snapshot;
8856             }
8857             this.data.clear();
8858             this.data.addAll(r);
8859             this.totalLength = t;
8860             this.applySort();
8861             this.fireEvent("datachanged", this);
8862         }else{
8863             this.totalLength = Math.max(t, this.data.length+r.length);
8864             this.add(r);
8865         }
8866         this.fireEvent("load", this, r, options, o);
8867         if(options.callback){
8868             options.callback.call(options.scope || this, r, options, true);
8869         }
8870     },
8871
8872
8873     /**
8874      * Loads data from a passed data block. A Reader which understands the format of the data
8875      * must have been configured in the constructor.
8876      * @param {Object} data The data block from which to read the Records.  The format of the data expected
8877      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
8878      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
8879      */
8880     loadData : function(o, append){
8881         var r = this.reader.readRecords(o);
8882         this.loadRecords(r, {add: append}, true);
8883     },
8884
8885     /**
8886      * Gets the number of cached records.
8887      * <p>
8888      * <em>If using paging, this may not be the total size of the dataset. If the data object
8889      * used by the Reader contains the dataset size, then the getTotalCount() function returns
8890      * the data set size</em>
8891      */
8892     getCount : function(){
8893         return this.data.length || 0;
8894     },
8895
8896     /**
8897      * Gets the total number of records in the dataset as returned by the server.
8898      * <p>
8899      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
8900      * the dataset size</em>
8901      */
8902     getTotalCount : function(){
8903         return this.totalLength || 0;
8904     },
8905
8906     /**
8907      * Returns the sort state of the Store as an object with two properties:
8908      * <pre><code>
8909  field {String} The name of the field by which the Records are sorted
8910  direction {String} The sort order, "ASC" or "DESC"
8911      * </code></pre>
8912      */
8913     getSortState : function(){
8914         return this.sortInfo;
8915     },
8916
8917     // private
8918     applySort : function(){
8919         if(this.sortInfo && !this.remoteSort){
8920             var s = this.sortInfo, f = s.field;
8921             var st = this.fields.get(f).sortType;
8922             var fn = function(r1, r2){
8923                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
8924                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
8925             };
8926             this.data.sort(s.direction, fn);
8927             if(this.snapshot && this.snapshot != this.data){
8928                 this.snapshot.sort(s.direction, fn);
8929             }
8930         }
8931     },
8932
8933     /**
8934      * Sets the default sort column and order to be used by the next load operation.
8935      * @param {String} fieldName The name of the field to sort by.
8936      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
8937      */
8938     setDefaultSort : function(field, dir){
8939         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
8940     },
8941
8942     /**
8943      * Sort the Records.
8944      * If remote sorting is used, the sort is performed on the server, and the cache is
8945      * reloaded. If local sorting is used, the cache is sorted internally.
8946      * @param {String} fieldName The name of the field to sort by.
8947      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
8948      */
8949     sort : function(fieldName, dir){
8950         var f = this.fields.get(fieldName);
8951         if(!dir){
8952             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
8953             
8954             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
8955                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
8956             }else{
8957                 dir = f.sortDir;
8958             }
8959         }
8960         this.sortToggle[f.name] = dir;
8961         this.sortInfo = {field: f.name, direction: dir};
8962         if(!this.remoteSort){
8963             this.applySort();
8964             this.fireEvent("datachanged", this);
8965         }else{
8966             this.load(this.lastOptions);
8967         }
8968     },
8969
8970     /**
8971      * Calls the specified function for each of the Records in the cache.
8972      * @param {Function} fn The function to call. The Record is passed as the first parameter.
8973      * Returning <em>false</em> aborts and exits the iteration.
8974      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
8975      */
8976     each : function(fn, scope){
8977         this.data.each(fn, scope);
8978     },
8979
8980     /**
8981      * Gets all records modified since the last commit.  Modified records are persisted across load operations
8982      * (e.g., during paging).
8983      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
8984      */
8985     getModifiedRecords : function(){
8986         return this.modified;
8987     },
8988
8989     // private
8990     createFilterFn : function(property, value, anyMatch){
8991         if(!value.exec){ // not a regex
8992             value = String(value);
8993             if(value.length == 0){
8994                 return false;
8995             }
8996             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
8997         }
8998         return function(r){
8999             return value.test(r.data[property]);
9000         };
9001     },
9002
9003     /**
9004      * Sums the value of <i>property</i> for each record between start and end and returns the result.
9005      * @param {String} property A field on your records
9006      * @param {Number} start The record index to start at (defaults to 0)
9007      * @param {Number} end The last record index to include (defaults to length - 1)
9008      * @return {Number} The sum
9009      */
9010     sum : function(property, start, end){
9011         var rs = this.data.items, v = 0;
9012         start = start || 0;
9013         end = (end || end === 0) ? end : rs.length-1;
9014
9015         for(var i = start; i <= end; i++){
9016             v += (rs[i].data[property] || 0);
9017         }
9018         return v;
9019     },
9020
9021     /**
9022      * Filter the records by a specified property.
9023      * @param {String} field A field on your records
9024      * @param {String/RegExp} value Either a string that the field
9025      * should start with or a RegExp to test against the field
9026      * @param {Boolean} anyMatch True to match any part not just the beginning
9027      */
9028     filter : function(property, value, anyMatch){
9029         var fn = this.createFilterFn(property, value, anyMatch);
9030         return fn ? this.filterBy(fn) : this.clearFilter();
9031     },
9032
9033     /**
9034      * Filter by a function. The specified function will be called with each
9035      * record in this data source. If the function returns true the record is included,
9036      * otherwise it is filtered.
9037      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9038      * @param {Object} scope (optional) The scope of the function (defaults to this)
9039      */
9040     filterBy : function(fn, scope){
9041         this.snapshot = this.snapshot || this.data;
9042         this.data = this.queryBy(fn, scope||this);
9043         this.fireEvent("datachanged", this);
9044     },
9045
9046     /**
9047      * Query the records by a specified property.
9048      * @param {String} field A field on your records
9049      * @param {String/RegExp} value Either a string that the field
9050      * should start with or a RegExp to test against the field
9051      * @param {Boolean} anyMatch True to match any part not just the beginning
9052      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9053      */
9054     query : function(property, value, anyMatch){
9055         var fn = this.createFilterFn(property, value, anyMatch);
9056         return fn ? this.queryBy(fn) : this.data.clone();
9057     },
9058
9059     /**
9060      * Query by a function. The specified function will be called with each
9061      * record in this data source. If the function returns true the record is included
9062      * in the results.
9063      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9064      * @param {Object} scope (optional) The scope of the function (defaults to this)
9065       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9066      **/
9067     queryBy : function(fn, scope){
9068         var data = this.snapshot || this.data;
9069         return data.filterBy(fn, scope||this);
9070     },
9071
9072     /**
9073      * Collects unique values for a particular dataIndex from this store.
9074      * @param {String} dataIndex The property to collect
9075      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
9076      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
9077      * @return {Array} An array of the unique values
9078      **/
9079     collect : function(dataIndex, allowNull, bypassFilter){
9080         var d = (bypassFilter === true && this.snapshot) ?
9081                 this.snapshot.items : this.data.items;
9082         var v, sv, r = [], l = {};
9083         for(var i = 0, len = d.length; i < len; i++){
9084             v = d[i].data[dataIndex];
9085             sv = String(v);
9086             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
9087                 l[sv] = true;
9088                 r[r.length] = v;
9089             }
9090         }
9091         return r;
9092     },
9093
9094     /**
9095      * Revert to a view of the Record cache with no filtering applied.
9096      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
9097      */
9098     clearFilter : function(suppressEvent){
9099         if(this.snapshot && this.snapshot != this.data){
9100             this.data = this.snapshot;
9101             delete this.snapshot;
9102             if(suppressEvent !== true){
9103                 this.fireEvent("datachanged", this);
9104             }
9105         }
9106     },
9107
9108     // private
9109     afterEdit : function(record){
9110         if(this.modified.indexOf(record) == -1){
9111             this.modified.push(record);
9112         }
9113         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
9114     },
9115     
9116     // private
9117     afterReject : function(record){
9118         this.modified.remove(record);
9119         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
9120     },
9121
9122     // private
9123     afterCommit : function(record){
9124         this.modified.remove(record);
9125         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
9126     },
9127
9128     /**
9129      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
9130      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
9131      */
9132     commitChanges : function(){
9133         var m = this.modified.slice(0);
9134         this.modified = [];
9135         for(var i = 0, len = m.length; i < len; i++){
9136             m[i].commit();
9137         }
9138     },
9139
9140     /**
9141      * Cancel outstanding changes on all changed records.
9142      */
9143     rejectChanges : function(){
9144         var m = this.modified.slice(0);
9145         this.modified = [];
9146         for(var i = 0, len = m.length; i < len; i++){
9147             m[i].reject();
9148         }
9149     },
9150
9151     onMetaChange : function(meta, rtype, o){
9152         this.recordType = rtype;
9153         this.fields = rtype.prototype.fields;
9154         delete this.snapshot;
9155         this.sortInfo = meta.sortInfo || this.sortInfo;
9156         this.modified = [];
9157         this.fireEvent('metachange', this, this.reader.meta);
9158     },
9159     
9160     moveIndex : function(data, type)
9161     {
9162         var index = this.indexOf(data);
9163         
9164         var newIndex = index + type;
9165         
9166         this.remove(data);
9167         
9168         this.insert(newIndex, data);
9169         
9170     }
9171 });/*
9172  * Based on:
9173  * Ext JS Library 1.1.1
9174  * Copyright(c) 2006-2007, Ext JS, LLC.
9175  *
9176  * Originally Released Under LGPL - original licence link has changed is not relivant.
9177  *
9178  * Fork - LGPL
9179  * <script type="text/javascript">
9180  */
9181
9182 /**
9183  * @class Roo.data.SimpleStore
9184  * @extends Roo.data.Store
9185  * Small helper class to make creating Stores from Array data easier.
9186  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
9187  * @cfg {Array} fields An array of field definition objects, or field name strings.
9188  * @cfg {Array} data The multi-dimensional array of data
9189  * @constructor
9190  * @param {Object} config
9191  */
9192 Roo.data.SimpleStore = function(config){
9193     Roo.data.SimpleStore.superclass.constructor.call(this, {
9194         isLocal : true,
9195         reader: new Roo.data.ArrayReader({
9196                 id: config.id
9197             },
9198             Roo.data.Record.create(config.fields)
9199         ),
9200         proxy : new Roo.data.MemoryProxy(config.data)
9201     });
9202     this.load();
9203 };
9204 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
9205  * Based on:
9206  * Ext JS Library 1.1.1
9207  * Copyright(c) 2006-2007, Ext JS, LLC.
9208  *
9209  * Originally Released Under LGPL - original licence link has changed is not relivant.
9210  *
9211  * Fork - LGPL
9212  * <script type="text/javascript">
9213  */
9214
9215 /**
9216 /**
9217  * @extends Roo.data.Store
9218  * @class Roo.data.JsonStore
9219  * Small helper class to make creating Stores for JSON data easier. <br/>
9220 <pre><code>
9221 var store = new Roo.data.JsonStore({
9222     url: 'get-images.php',
9223     root: 'images',
9224     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
9225 });
9226 </code></pre>
9227  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
9228  * JsonReader and HttpProxy (unless inline data is provided).</b>
9229  * @cfg {Array} fields An array of field definition objects, or field name strings.
9230  * @constructor
9231  * @param {Object} config
9232  */
9233 Roo.data.JsonStore = function(c){
9234     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
9235         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
9236         reader: new Roo.data.JsonReader(c, c.fields)
9237     }));
9238 };
9239 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
9240  * Based on:
9241  * Ext JS Library 1.1.1
9242  * Copyright(c) 2006-2007, Ext JS, LLC.
9243  *
9244  * Originally Released Under LGPL - original licence link has changed is not relivant.
9245  *
9246  * Fork - LGPL
9247  * <script type="text/javascript">
9248  */
9249
9250  
9251 Roo.data.Field = function(config){
9252     if(typeof config == "string"){
9253         config = {name: config};
9254     }
9255     Roo.apply(this, config);
9256     
9257     if(!this.type){
9258         this.type = "auto";
9259     }
9260     
9261     var st = Roo.data.SortTypes;
9262     // named sortTypes are supported, here we look them up
9263     if(typeof this.sortType == "string"){
9264         this.sortType = st[this.sortType];
9265     }
9266     
9267     // set default sortType for strings and dates
9268     if(!this.sortType){
9269         switch(this.type){
9270             case "string":
9271                 this.sortType = st.asUCString;
9272                 break;
9273             case "date":
9274                 this.sortType = st.asDate;
9275                 break;
9276             default:
9277                 this.sortType = st.none;
9278         }
9279     }
9280
9281     // define once
9282     var stripRe = /[\$,%]/g;
9283
9284     // prebuilt conversion function for this field, instead of
9285     // switching every time we're reading a value
9286     if(!this.convert){
9287         var cv, dateFormat = this.dateFormat;
9288         switch(this.type){
9289             case "":
9290             case "auto":
9291             case undefined:
9292                 cv = function(v){ return v; };
9293                 break;
9294             case "string":
9295                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
9296                 break;
9297             case "int":
9298                 cv = function(v){
9299                     return v !== undefined && v !== null && v !== '' ?
9300                            parseInt(String(v).replace(stripRe, ""), 10) : '';
9301                     };
9302                 break;
9303             case "float":
9304                 cv = function(v){
9305                     return v !== undefined && v !== null && v !== '' ?
9306                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
9307                     };
9308                 break;
9309             case "bool":
9310             case "boolean":
9311                 cv = function(v){ return v === true || v === "true" || v == 1; };
9312                 break;
9313             case "date":
9314                 cv = function(v){
9315                     if(!v){
9316                         return '';
9317                     }
9318                     if(v instanceof Date){
9319                         return v;
9320                     }
9321                     if(dateFormat){
9322                         if(dateFormat == "timestamp"){
9323                             return new Date(v*1000);
9324                         }
9325                         return Date.parseDate(v, dateFormat);
9326                     }
9327                     var parsed = Date.parse(v);
9328                     return parsed ? new Date(parsed) : null;
9329                 };
9330              break;
9331             
9332         }
9333         this.convert = cv;
9334     }
9335 };
9336
9337 Roo.data.Field.prototype = {
9338     dateFormat: null,
9339     defaultValue: "",
9340     mapping: null,
9341     sortType : null,
9342     sortDir : "ASC"
9343 };/*
9344  * Based on:
9345  * Ext JS Library 1.1.1
9346  * Copyright(c) 2006-2007, Ext JS, LLC.
9347  *
9348  * Originally Released Under LGPL - original licence link has changed is not relivant.
9349  *
9350  * Fork - LGPL
9351  * <script type="text/javascript">
9352  */
9353  
9354 // Base class for reading structured data from a data source.  This class is intended to be
9355 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
9356
9357 /**
9358  * @class Roo.data.DataReader
9359  * Base class for reading structured data from a data source.  This class is intended to be
9360  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
9361  */
9362
9363 Roo.data.DataReader = function(meta, recordType){
9364     
9365     this.meta = meta;
9366     
9367     this.recordType = recordType instanceof Array ? 
9368         Roo.data.Record.create(recordType) : recordType;
9369 };
9370
9371 Roo.data.DataReader.prototype = {
9372      /**
9373      * Create an empty record
9374      * @param {Object} data (optional) - overlay some values
9375      * @return {Roo.data.Record} record created.
9376      */
9377     newRow :  function(d) {
9378         var da =  {};
9379         this.recordType.prototype.fields.each(function(c) {
9380             switch( c.type) {
9381                 case 'int' : da[c.name] = 0; break;
9382                 case 'date' : da[c.name] = new Date(); break;
9383                 case 'float' : da[c.name] = 0.0; break;
9384                 case 'boolean' : da[c.name] = false; break;
9385                 default : da[c.name] = ""; break;
9386             }
9387             
9388         });
9389         return new this.recordType(Roo.apply(da, d));
9390     }
9391     
9392 };/*
9393  * Based on:
9394  * Ext JS Library 1.1.1
9395  * Copyright(c) 2006-2007, Ext JS, LLC.
9396  *
9397  * Originally Released Under LGPL - original licence link has changed is not relivant.
9398  *
9399  * Fork - LGPL
9400  * <script type="text/javascript">
9401  */
9402
9403 /**
9404  * @class Roo.data.DataProxy
9405  * @extends Roo.data.Observable
9406  * This class is an abstract base class for implementations which provide retrieval of
9407  * unformatted data objects.<br>
9408  * <p>
9409  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
9410  * (of the appropriate type which knows how to parse the data object) to provide a block of
9411  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
9412  * <p>
9413  * Custom implementations must implement the load method as described in
9414  * {@link Roo.data.HttpProxy#load}.
9415  */
9416 Roo.data.DataProxy = function(){
9417     this.addEvents({
9418         /**
9419          * @event beforeload
9420          * Fires before a network request is made to retrieve a data object.
9421          * @param {Object} This DataProxy object.
9422          * @param {Object} params The params parameter to the load function.
9423          */
9424         beforeload : true,
9425         /**
9426          * @event load
9427          * Fires before the load method's callback is called.
9428          * @param {Object} This DataProxy object.
9429          * @param {Object} o The data object.
9430          * @param {Object} arg The callback argument object passed to the load function.
9431          */
9432         load : true,
9433         /**
9434          * @event loadexception
9435          * Fires if an Exception occurs during data retrieval.
9436          * @param {Object} This DataProxy object.
9437          * @param {Object} o The data object.
9438          * @param {Object} arg The callback argument object passed to the load function.
9439          * @param {Object} e The Exception.
9440          */
9441         loadexception : true
9442     });
9443     Roo.data.DataProxy.superclass.constructor.call(this);
9444 };
9445
9446 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
9447
9448     /**
9449      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
9450      */
9451 /*
9452  * Based on:
9453  * Ext JS Library 1.1.1
9454  * Copyright(c) 2006-2007, Ext JS, LLC.
9455  *
9456  * Originally Released Under LGPL - original licence link has changed is not relivant.
9457  *
9458  * Fork - LGPL
9459  * <script type="text/javascript">
9460  */
9461 /**
9462  * @class Roo.data.MemoryProxy
9463  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
9464  * to the Reader when its load method is called.
9465  * @constructor
9466  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
9467  */
9468 Roo.data.MemoryProxy = function(data){
9469     if (data.data) {
9470         data = data.data;
9471     }
9472     Roo.data.MemoryProxy.superclass.constructor.call(this);
9473     this.data = data;
9474 };
9475
9476 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
9477     /**
9478      * Load data from the requested source (in this case an in-memory
9479      * data object passed to the constructor), read the data object into
9480      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9481      * process that block using the passed callback.
9482      * @param {Object} params This parameter is not used by the MemoryProxy class.
9483      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9484      * object into a block of Roo.data.Records.
9485      * @param {Function} callback The function into which to pass the block of Roo.data.records.
9486      * The function must be passed <ul>
9487      * <li>The Record block object</li>
9488      * <li>The "arg" argument from the load function</li>
9489      * <li>A boolean success indicator</li>
9490      * </ul>
9491      * @param {Object} scope The scope in which to call the callback
9492      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9493      */
9494     load : function(params, reader, callback, scope, arg){
9495         params = params || {};
9496         var result;
9497         try {
9498             result = reader.readRecords(this.data);
9499         }catch(e){
9500             this.fireEvent("loadexception", this, arg, null, e);
9501             callback.call(scope, null, arg, false);
9502             return;
9503         }
9504         callback.call(scope, result, arg, true);
9505     },
9506     
9507     // private
9508     update : function(params, records){
9509         
9510     }
9511 });/*
9512  * Based on:
9513  * Ext JS Library 1.1.1
9514  * Copyright(c) 2006-2007, Ext JS, LLC.
9515  *
9516  * Originally Released Under LGPL - original licence link has changed is not relivant.
9517  *
9518  * Fork - LGPL
9519  * <script type="text/javascript">
9520  */
9521 /**
9522  * @class Roo.data.HttpProxy
9523  * @extends Roo.data.DataProxy
9524  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
9525  * configured to reference a certain URL.<br><br>
9526  * <p>
9527  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
9528  * from which the running page was served.<br><br>
9529  * <p>
9530  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
9531  * <p>
9532  * Be aware that to enable the browser to parse an XML document, the server must set
9533  * the Content-Type header in the HTTP response to "text/xml".
9534  * @constructor
9535  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
9536  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
9537  * will be used to make the request.
9538  */
9539 Roo.data.HttpProxy = function(conn){
9540     Roo.data.HttpProxy.superclass.constructor.call(this);
9541     // is conn a conn config or a real conn?
9542     this.conn = conn;
9543     this.useAjax = !conn || !conn.events;
9544   
9545 };
9546
9547 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
9548     // thse are take from connection...
9549     
9550     /**
9551      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
9552      */
9553     /**
9554      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
9555      * extra parameters to each request made by this object. (defaults to undefined)
9556      */
9557     /**
9558      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
9559      *  to each request made by this object. (defaults to undefined)
9560      */
9561     /**
9562      * @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)
9563      */
9564     /**
9565      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
9566      */
9567      /**
9568      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
9569      * @type Boolean
9570      */
9571   
9572
9573     /**
9574      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
9575      * @type Boolean
9576      */
9577     /**
9578      * Return the {@link Roo.data.Connection} object being used by this Proxy.
9579      * @return {Connection} The Connection object. This object may be used to subscribe to events on
9580      * a finer-grained basis than the DataProxy events.
9581      */
9582     getConnection : function(){
9583         return this.useAjax ? Roo.Ajax : this.conn;
9584     },
9585
9586     /**
9587      * Load data from the configured {@link Roo.data.Connection}, read the data object into
9588      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
9589      * process that block using the passed callback.
9590      * @param {Object} params An object containing properties which are to be used as HTTP parameters
9591      * for the request to the remote server.
9592      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9593      * object into a block of Roo.data.Records.
9594      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9595      * The function must be passed <ul>
9596      * <li>The Record block object</li>
9597      * <li>The "arg" argument from the load function</li>
9598      * <li>A boolean success indicator</li>
9599      * </ul>
9600      * @param {Object} scope The scope in which to call the callback
9601      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9602      */
9603     load : function(params, reader, callback, scope, arg){
9604         if(this.fireEvent("beforeload", this, params) !== false){
9605             var  o = {
9606                 params : params || {},
9607                 request: {
9608                     callback : callback,
9609                     scope : scope,
9610                     arg : arg
9611                 },
9612                 reader: reader,
9613                 callback : this.loadResponse,
9614                 scope: this
9615             };
9616             if(this.useAjax){
9617                 Roo.applyIf(o, this.conn);
9618                 if(this.activeRequest){
9619                     Roo.Ajax.abort(this.activeRequest);
9620                 }
9621                 this.activeRequest = Roo.Ajax.request(o);
9622             }else{
9623                 this.conn.request(o);
9624             }
9625         }else{
9626             callback.call(scope||this, null, arg, false);
9627         }
9628     },
9629
9630     // private
9631     loadResponse : function(o, success, response){
9632         delete this.activeRequest;
9633         if(!success){
9634             this.fireEvent("loadexception", this, o, response);
9635             o.request.callback.call(o.request.scope, null, o.request.arg, false);
9636             return;
9637         }
9638         var result;
9639         try {
9640             result = o.reader.read(response);
9641         }catch(e){
9642             this.fireEvent("loadexception", this, o, response, e);
9643             o.request.callback.call(o.request.scope, null, o.request.arg, false);
9644             return;
9645         }
9646         
9647         this.fireEvent("load", this, o, o.request.arg);
9648         o.request.callback.call(o.request.scope, result, o.request.arg, true);
9649     },
9650
9651     // private
9652     update : function(dataSet){
9653
9654     },
9655
9656     // private
9657     updateResponse : function(dataSet){
9658
9659     }
9660 });/*
9661  * Based on:
9662  * Ext JS Library 1.1.1
9663  * Copyright(c) 2006-2007, Ext JS, LLC.
9664  *
9665  * Originally Released Under LGPL - original licence link has changed is not relivant.
9666  *
9667  * Fork - LGPL
9668  * <script type="text/javascript">
9669  */
9670
9671 /**
9672  * @class Roo.data.ScriptTagProxy
9673  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
9674  * other than the originating domain of the running page.<br><br>
9675  * <p>
9676  * <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
9677  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
9678  * <p>
9679  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
9680  * source code that is used as the source inside a &lt;script> tag.<br><br>
9681  * <p>
9682  * In order for the browser to process the returned data, the server must wrap the data object
9683  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
9684  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
9685  * depending on whether the callback name was passed:
9686  * <p>
9687  * <pre><code>
9688 boolean scriptTag = false;
9689 String cb = request.getParameter("callback");
9690 if (cb != null) {
9691     scriptTag = true;
9692     response.setContentType("text/javascript");
9693 } else {
9694     response.setContentType("application/x-json");
9695 }
9696 Writer out = response.getWriter();
9697 if (scriptTag) {
9698     out.write(cb + "(");
9699 }
9700 out.print(dataBlock.toJsonString());
9701 if (scriptTag) {
9702     out.write(");");
9703 }
9704 </pre></code>
9705  *
9706  * @constructor
9707  * @param {Object} config A configuration object.
9708  */
9709 Roo.data.ScriptTagProxy = function(config){
9710     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
9711     Roo.apply(this, config);
9712     this.head = document.getElementsByTagName("head")[0];
9713 };
9714
9715 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
9716
9717 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
9718     /**
9719      * @cfg {String} url The URL from which to request the data object.
9720      */
9721     /**
9722      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
9723      */
9724     timeout : 30000,
9725     /**
9726      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
9727      * the server the name of the callback function set up by the load call to process the returned data object.
9728      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
9729      * javascript output which calls this named function passing the data object as its only parameter.
9730      */
9731     callbackParam : "callback",
9732     /**
9733      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
9734      * name to the request.
9735      */
9736     nocache : true,
9737
9738     /**
9739      * Load data from the configured URL, read the data object into
9740      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9741      * process that block using the passed callback.
9742      * @param {Object} params An object containing properties which are to be used as HTTP parameters
9743      * for the request to the remote server.
9744      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9745      * object into a block of Roo.data.Records.
9746      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9747      * The function must be passed <ul>
9748      * <li>The Record block object</li>
9749      * <li>The "arg" argument from the load function</li>
9750      * <li>A boolean success indicator</li>
9751      * </ul>
9752      * @param {Object} scope The scope in which to call the callback
9753      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9754      */
9755     load : function(params, reader, callback, scope, arg){
9756         if(this.fireEvent("beforeload", this, params) !== false){
9757
9758             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
9759
9760             var url = this.url;
9761             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
9762             if(this.nocache){
9763                 url += "&_dc=" + (new Date().getTime());
9764             }
9765             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
9766             var trans = {
9767                 id : transId,
9768                 cb : "stcCallback"+transId,
9769                 scriptId : "stcScript"+transId,
9770                 params : params,
9771                 arg : arg,
9772                 url : url,
9773                 callback : callback,
9774                 scope : scope,
9775                 reader : reader
9776             };
9777             var conn = this;
9778
9779             window[trans.cb] = function(o){
9780                 conn.handleResponse(o, trans);
9781             };
9782
9783             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
9784
9785             if(this.autoAbort !== false){
9786                 this.abort();
9787             }
9788
9789             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
9790
9791             var script = document.createElement("script");
9792             script.setAttribute("src", url);
9793             script.setAttribute("type", "text/javascript");
9794             script.setAttribute("id", trans.scriptId);
9795             this.head.appendChild(script);
9796
9797             this.trans = trans;
9798         }else{
9799             callback.call(scope||this, null, arg, false);
9800         }
9801     },
9802
9803     // private
9804     isLoading : function(){
9805         return this.trans ? true : false;
9806     },
9807
9808     /**
9809      * Abort the current server request.
9810      */
9811     abort : function(){
9812         if(this.isLoading()){
9813             this.destroyTrans(this.trans);
9814         }
9815     },
9816
9817     // private
9818     destroyTrans : function(trans, isLoaded){
9819         this.head.removeChild(document.getElementById(trans.scriptId));
9820         clearTimeout(trans.timeoutId);
9821         if(isLoaded){
9822             window[trans.cb] = undefined;
9823             try{
9824                 delete window[trans.cb];
9825             }catch(e){}
9826         }else{
9827             // if hasn't been loaded, wait for load to remove it to prevent script error
9828             window[trans.cb] = function(){
9829                 window[trans.cb] = undefined;
9830                 try{
9831                     delete window[trans.cb];
9832                 }catch(e){}
9833             };
9834         }
9835     },
9836
9837     // private
9838     handleResponse : function(o, trans){
9839         this.trans = false;
9840         this.destroyTrans(trans, true);
9841         var result;
9842         try {
9843             result = trans.reader.readRecords(o);
9844         }catch(e){
9845             this.fireEvent("loadexception", this, o, trans.arg, e);
9846             trans.callback.call(trans.scope||window, null, trans.arg, false);
9847             return;
9848         }
9849         this.fireEvent("load", this, o, trans.arg);
9850         trans.callback.call(trans.scope||window, result, trans.arg, true);
9851     },
9852
9853     // private
9854     handleFailure : function(trans){
9855         this.trans = false;
9856         this.destroyTrans(trans, false);
9857         this.fireEvent("loadexception", this, null, trans.arg);
9858         trans.callback.call(trans.scope||window, null, trans.arg, false);
9859     }
9860 });/*
9861  * Based on:
9862  * Ext JS Library 1.1.1
9863  * Copyright(c) 2006-2007, Ext JS, LLC.
9864  *
9865  * Originally Released Under LGPL - original licence link has changed is not relivant.
9866  *
9867  * Fork - LGPL
9868  * <script type="text/javascript">
9869  */
9870
9871 /**
9872  * @class Roo.data.JsonReader
9873  * @extends Roo.data.DataReader
9874  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
9875  * based on mappings in a provided Roo.data.Record constructor.
9876  * 
9877  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
9878  * in the reply previously. 
9879  * 
9880  * <p>
9881  * Example code:
9882  * <pre><code>
9883 var RecordDef = Roo.data.Record.create([
9884     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
9885     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
9886 ]);
9887 var myReader = new Roo.data.JsonReader({
9888     totalProperty: "results",    // The property which contains the total dataset size (optional)
9889     root: "rows",                // The property which contains an Array of row objects
9890     id: "id"                     // The property within each row object that provides an ID for the record (optional)
9891 }, RecordDef);
9892 </code></pre>
9893  * <p>
9894  * This would consume a JSON file like this:
9895  * <pre><code>
9896 { 'results': 2, 'rows': [
9897     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
9898     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
9899 }
9900 </code></pre>
9901  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
9902  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
9903  * paged from the remote server.
9904  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
9905  * @cfg {String} root name of the property which contains the Array of row objects.
9906  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
9907  * @constructor
9908  * Create a new JsonReader
9909  * @param {Object} meta Metadata configuration options
9910  * @param {Object} recordType Either an Array of field definition objects,
9911  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
9912  */
9913 Roo.data.JsonReader = function(meta, recordType){
9914     
9915     meta = meta || {};
9916     // set some defaults:
9917     Roo.applyIf(meta, {
9918         totalProperty: 'total',
9919         successProperty : 'success',
9920         root : 'data',
9921         id : 'id'
9922     });
9923     
9924     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
9925 };
9926 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
9927     
9928     /**
9929      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
9930      * Used by Store query builder to append _requestMeta to params.
9931      * 
9932      */
9933     metaFromRemote : false,
9934     /**
9935      * This method is only used by a DataProxy which has retrieved data from a remote server.
9936      * @param {Object} response The XHR object which contains the JSON data in its responseText.
9937      * @return {Object} data A data block which is used by an Roo.data.Store object as
9938      * a cache of Roo.data.Records.
9939      */
9940     read : function(response){
9941         var json = response.responseText;
9942        
9943         var o = /* eval:var:o */ eval("("+json+")");
9944         if(!o) {
9945             throw {message: "JsonReader.read: Json object not found"};
9946         }
9947         
9948         if(o.metaData){
9949             
9950             delete this.ef;
9951             this.metaFromRemote = true;
9952             this.meta = o.metaData;
9953             this.recordType = Roo.data.Record.create(o.metaData.fields);
9954             this.onMetaChange(this.meta, this.recordType, o);
9955         }
9956         return this.readRecords(o);
9957     },
9958
9959     // private function a store will implement
9960     onMetaChange : function(meta, recordType, o){
9961
9962     },
9963
9964     /**
9965          * @ignore
9966          */
9967     simpleAccess: function(obj, subsc) {
9968         return obj[subsc];
9969     },
9970
9971         /**
9972          * @ignore
9973          */
9974     getJsonAccessor: function(){
9975         var re = /[\[\.]/;
9976         return function(expr) {
9977             try {
9978                 return(re.test(expr))
9979                     ? new Function("obj", "return obj." + expr)
9980                     : function(obj){
9981                         return obj[expr];
9982                     };
9983             } catch(e){}
9984             return Roo.emptyFn;
9985         };
9986     }(),
9987
9988     /**
9989      * Create a data block containing Roo.data.Records from an XML document.
9990      * @param {Object} o An object which contains an Array of row objects in the property specified
9991      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
9992      * which contains the total size of the dataset.
9993      * @return {Object} data A data block which is used by an Roo.data.Store object as
9994      * a cache of Roo.data.Records.
9995      */
9996     readRecords : function(o){
9997         /**
9998          * After any data loads, the raw JSON data is available for further custom processing.
9999          * @type Object
10000          */
10001         this.o = o;
10002         var s = this.meta, Record = this.recordType,
10003             f = Record.prototype.fields, fi = f.items, fl = f.length;
10004
10005 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
10006         if (!this.ef) {
10007             if(s.totalProperty) {
10008                     this.getTotal = this.getJsonAccessor(s.totalProperty);
10009                 }
10010                 if(s.successProperty) {
10011                     this.getSuccess = this.getJsonAccessor(s.successProperty);
10012                 }
10013                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
10014                 if (s.id) {
10015                         var g = this.getJsonAccessor(s.id);
10016                         this.getId = function(rec) {
10017                                 var r = g(rec);
10018                                 return (r === undefined || r === "") ? null : r;
10019                         };
10020                 } else {
10021                         this.getId = function(){return null;};
10022                 }
10023             this.ef = [];
10024             for(var jj = 0; jj < fl; jj++){
10025                 f = fi[jj];
10026                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
10027                 this.ef[jj] = this.getJsonAccessor(map);
10028             }
10029         }
10030
10031         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
10032         if(s.totalProperty){
10033             var vt = parseInt(this.getTotal(o), 10);
10034             if(!isNaN(vt)){
10035                 totalRecords = vt;
10036             }
10037         }
10038         if(s.successProperty){
10039             var vs = this.getSuccess(o);
10040             if(vs === false || vs === 'false'){
10041                 success = false;
10042             }
10043         }
10044         var records = [];
10045             for(var i = 0; i < c; i++){
10046                     var n = root[i];
10047                 var values = {};
10048                 var id = this.getId(n);
10049                 for(var j = 0; j < fl; j++){
10050                     f = fi[j];
10051                 var v = this.ef[j](n);
10052                 if (!f.convert) {
10053                     Roo.log('missing convert for ' + f.name);
10054                     Roo.log(f);
10055                     continue;
10056                 }
10057                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
10058                 }
10059                 var record = new Record(values, id);
10060                 record.json = n;
10061                 records[i] = record;
10062             }
10063             return {
10064             raw : o,
10065                 success : success,
10066                 records : records,
10067                 totalRecords : totalRecords
10068             };
10069     }
10070 });/*
10071  * Based on:
10072  * Ext JS Library 1.1.1
10073  * Copyright(c) 2006-2007, Ext JS, LLC.
10074  *
10075  * Originally Released Under LGPL - original licence link has changed is not relivant.
10076  *
10077  * Fork - LGPL
10078  * <script type="text/javascript">
10079  */
10080
10081 /**
10082  * @class Roo.data.ArrayReader
10083  * @extends Roo.data.DataReader
10084  * Data reader class to create an Array of Roo.data.Record objects from an Array.
10085  * Each element of that Array represents a row of data fields. The
10086  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
10087  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
10088  * <p>
10089  * Example code:.
10090  * <pre><code>
10091 var RecordDef = Roo.data.Record.create([
10092     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
10093     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
10094 ]);
10095 var myReader = new Roo.data.ArrayReader({
10096     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
10097 }, RecordDef);
10098 </code></pre>
10099  * <p>
10100  * This would consume an Array like this:
10101  * <pre><code>
10102 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
10103   </code></pre>
10104  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
10105  * @constructor
10106  * Create a new JsonReader
10107  * @param {Object} meta Metadata configuration options.
10108  * @param {Object} recordType Either an Array of field definition objects
10109  * as specified to {@link Roo.data.Record#create},
10110  * or an {@link Roo.data.Record} object
10111  * created using {@link Roo.data.Record#create}.
10112  */
10113 Roo.data.ArrayReader = function(meta, recordType){
10114     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
10115 };
10116
10117 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
10118     /**
10119      * Create a data block containing Roo.data.Records from an XML document.
10120      * @param {Object} o An Array of row objects which represents the dataset.
10121      * @return {Object} data A data block which is used by an Roo.data.Store object as
10122      * a cache of Roo.data.Records.
10123      */
10124     readRecords : function(o){
10125         var sid = this.meta ? this.meta.id : null;
10126         var recordType = this.recordType, fields = recordType.prototype.fields;
10127         var records = [];
10128         var root = o;
10129             for(var i = 0; i < root.length; i++){
10130                     var n = root[i];
10131                 var values = {};
10132                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
10133                 for(var j = 0, jlen = fields.length; j < jlen; j++){
10134                 var f = fields.items[j];
10135                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
10136                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
10137                 v = f.convert(v);
10138                 values[f.name] = v;
10139             }
10140                 var record = new recordType(values, id);
10141                 record.json = n;
10142                 records[records.length] = record;
10143             }
10144             return {
10145                 records : records,
10146                 totalRecords : records.length
10147             };
10148     }
10149 });/*
10150  * - LGPL
10151  * * 
10152  */
10153
10154 /**
10155  * @class Roo.bootstrap.ComboBox
10156  * @extends Roo.bootstrap.TriggerField
10157  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
10158  * @cfg {Boolean} append (true|false) default false
10159  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
10160  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
10161  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
10162  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
10163  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
10164  * @constructor
10165  * Create a new ComboBox.
10166  * @param {Object} config Configuration options
10167  */
10168 Roo.bootstrap.ComboBox = function(config){
10169     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
10170     this.addEvents({
10171         /**
10172          * @event expand
10173          * Fires when the dropdown list is expanded
10174              * @param {Roo.bootstrap.ComboBox} combo This combo box
10175              */
10176         'expand' : true,
10177         /**
10178          * @event collapse
10179          * Fires when the dropdown list is collapsed
10180              * @param {Roo.bootstrap.ComboBox} combo This combo box
10181              */
10182         'collapse' : true,
10183         /**
10184          * @event beforeselect
10185          * Fires before a list item is selected. Return false to cancel the selection.
10186              * @param {Roo.bootstrap.ComboBox} combo This combo box
10187              * @param {Roo.data.Record} record The data record returned from the underlying store
10188              * @param {Number} index The index of the selected item in the dropdown list
10189              */
10190         'beforeselect' : true,
10191         /**
10192          * @event select
10193          * Fires when a list item is selected
10194              * @param {Roo.bootstrap.ComboBox} combo This combo box
10195              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
10196              * @param {Number} index The index of the selected item in the dropdown list
10197              */
10198         'select' : true,
10199         /**
10200          * @event beforequery
10201          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
10202          * The event object passed has these properties:
10203              * @param {Roo.bootstrap.ComboBox} combo This combo box
10204              * @param {String} query The query
10205              * @param {Boolean} forceAll true to force "all" query
10206              * @param {Boolean} cancel true to cancel the query
10207              * @param {Object} e The query event object
10208              */
10209         'beforequery': true,
10210          /**
10211          * @event add
10212          * Fires when the 'add' icon is pressed (add a listener to enable add button)
10213              * @param {Roo.bootstrap.ComboBox} combo This combo box
10214              */
10215         'add' : true,
10216         /**
10217          * @event edit
10218          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
10219              * @param {Roo.bootstrap.ComboBox} combo This combo box
10220              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
10221              */
10222         'edit' : true,
10223         /**
10224          * @event remove
10225          * Fires when the remove value from the combobox array
10226              * @param {Roo.bootstrap.ComboBox} combo This combo box
10227              */
10228         'remove' : true
10229         
10230     });
10231     
10232     this.item = [];
10233     this.tickItems = [];
10234     
10235     this.selectedIndex = -1;
10236     if(this.mode == 'local'){
10237         if(config.queryDelay === undefined){
10238             this.queryDelay = 10;
10239         }
10240         if(config.minChars === undefined){
10241             this.minChars = 0;
10242         }
10243     }
10244 };
10245
10246 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
10247      
10248     /**
10249      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
10250      * rendering into an Roo.Editor, defaults to false)
10251      */
10252     /**
10253      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
10254      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
10255      */
10256     /**
10257      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
10258      */
10259     /**
10260      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
10261      * the dropdown list (defaults to undefined, with no header element)
10262      */
10263
10264      /**
10265      * @cfg {String/Roo.Template} tpl The template to use to render the output
10266      */
10267      
10268      /**
10269      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
10270      */
10271     listWidth: undefined,
10272     /**
10273      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
10274      * mode = 'remote' or 'text' if mode = 'local')
10275      */
10276     displayField: undefined,
10277     /**
10278      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
10279      * mode = 'remote' or 'value' if mode = 'local'). 
10280      * Note: use of a valueField requires the user make a selection
10281      * in order for a value to be mapped.
10282      */
10283     valueField: undefined,
10284     
10285     
10286     /**
10287      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
10288      * field's data value (defaults to the underlying DOM element's name)
10289      */
10290     hiddenName: undefined,
10291     /**
10292      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
10293      */
10294     listClass: '',
10295     /**
10296      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
10297      */
10298     selectedClass: 'active',
10299     
10300     /**
10301      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
10302      */
10303     shadow:'sides',
10304     /**
10305      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
10306      * anchor positions (defaults to 'tl-bl')
10307      */
10308     listAlign: 'tl-bl?',
10309     /**
10310      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
10311      */
10312     maxHeight: 300,
10313     /**
10314      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
10315      * query specified by the allQuery config option (defaults to 'query')
10316      */
10317     triggerAction: 'query',
10318     /**
10319      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
10320      * (defaults to 4, does not apply if editable = false)
10321      */
10322     minChars : 4,
10323     /**
10324      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
10325      * delay (typeAheadDelay) if it matches a known value (defaults to false)
10326      */
10327     typeAhead: false,
10328     /**
10329      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
10330      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
10331      */
10332     queryDelay: 500,
10333     /**
10334      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
10335      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
10336      */
10337     pageSize: 0,
10338     /**
10339      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
10340      * when editable = true (defaults to false)
10341      */
10342     selectOnFocus:false,
10343     /**
10344      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
10345      */
10346     queryParam: 'query',
10347     /**
10348      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
10349      * when mode = 'remote' (defaults to 'Loading...')
10350      */
10351     loadingText: 'Loading...',
10352     /**
10353      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
10354      */
10355     resizable: false,
10356     /**
10357      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
10358      */
10359     handleHeight : 8,
10360     /**
10361      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
10362      * traditional select (defaults to true)
10363      */
10364     editable: true,
10365     /**
10366      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
10367      */
10368     allQuery: '',
10369     /**
10370      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
10371      */
10372     mode: 'remote',
10373     /**
10374      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
10375      * listWidth has a higher value)
10376      */
10377     minListWidth : 70,
10378     /**
10379      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
10380      * allow the user to set arbitrary text into the field (defaults to false)
10381      */
10382     forceSelection:false,
10383     /**
10384      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
10385      * if typeAhead = true (defaults to 250)
10386      */
10387     typeAheadDelay : 250,
10388     /**
10389      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
10390      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
10391      */
10392     valueNotFoundText : undefined,
10393     /**
10394      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
10395      */
10396     blockFocus : false,
10397     
10398     /**
10399      * @cfg {Boolean} disableClear Disable showing of clear button.
10400      */
10401     disableClear : false,
10402     /**
10403      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
10404      */
10405     alwaysQuery : false,
10406     
10407     /**
10408      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
10409      */
10410     multiple : false,
10411     
10412     //private
10413     addicon : false,
10414     editicon: false,
10415     
10416     page: 0,
10417     hasQuery: false,
10418     append: false,
10419     loadNext: false,
10420     autoFocus : true,
10421     tickable : false,
10422     btnPosition : 'right',
10423     triggerList : true,
10424     showToggleBtn : true,
10425     // element that contains real text value.. (when hidden is used..)
10426     
10427     getAutoCreate : function()
10428     {
10429         var cfg = false;
10430         
10431         /*
10432          *  Normal ComboBox
10433          */
10434         if(!this.tickable){
10435             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
10436             return cfg;
10437         }
10438         
10439         /*
10440          *  ComboBox with tickable selections
10441          */
10442              
10443         var align = this.labelAlign || this.parentLabelAlign();
10444         
10445         cfg = {
10446             cls : 'form-group roo-combobox-tickable' //input-group
10447         };
10448         
10449         
10450         var buttons = {
10451             tag : 'div',
10452             cls : 'tickable-buttons',
10453             cn : [
10454                 {
10455                     tag : 'button',
10456                     type : 'button',
10457                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
10458                     html : 'Edit'
10459                 },
10460                 {
10461                     tag : 'button',
10462                     type : 'button',
10463                     name : 'ok',
10464                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
10465                     html : 'Done'
10466                 },
10467                 {
10468                     tag : 'button',
10469                     type : 'button',
10470                     name : 'cancel',
10471                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
10472                     html : 'Cancel'
10473                 }
10474             ]
10475         };
10476         
10477         var _this = this;
10478         Roo.each(buttons.cn, function(c){
10479             if (_this.size) {
10480                 c.cls += ' btn-' + _this.size;
10481             }
10482
10483             if (_this.disabled) {
10484                 c.disabled = true;
10485             }
10486         });
10487         
10488         var box = {
10489             tag: 'div',
10490             cn: [
10491                 {
10492                     tag: 'input',
10493                     type : 'hidden',
10494                     cls: 'form-hidden-field'
10495                 },
10496                 {
10497                     tag: 'ul',
10498                     cls: 'select2-choices',
10499                     cn:[
10500                         {
10501                             tag: 'li',
10502                             cls: 'select2-search-field',
10503                             cn: [
10504
10505                                 buttons
10506                             ]
10507                         }
10508                     ]
10509                 }
10510             ]
10511         }
10512         
10513         var combobox = {
10514             cls: 'select2-container input-group select2-container-multi',
10515             cn: [
10516                 box
10517 //                {
10518 //                    tag: 'ul',
10519 //                    cls: 'typeahead typeahead-long dropdown-menu',
10520 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
10521 //                }
10522             ]
10523         };
10524         
10525         if (align ==='left' && this.fieldLabel.length) {
10526             
10527                 Roo.log("left and has label");
10528                 cfg.cn = [
10529                     
10530                     {
10531                         tag: 'label',
10532                         'for' :  id,
10533                         cls : 'control-label col-sm-' + this.labelWidth,
10534                         html : this.fieldLabel
10535                         
10536                     },
10537                     {
10538                         cls : "col-sm-" + (12 - this.labelWidth), 
10539                         cn: [
10540                             combobox
10541                         ]
10542                     }
10543                     
10544                 ];
10545         } else if ( this.fieldLabel.length) {
10546                 Roo.log(" label");
10547                  cfg.cn = [
10548                    
10549                     {
10550                         tag: 'label',
10551                         //cls : 'input-group-addon',
10552                         html : this.fieldLabel
10553                         
10554                     },
10555                     
10556                     combobox
10557                     
10558                 ];
10559
10560         } else {
10561             
10562                 Roo.log(" no label && no align");
10563                 cfg = combobox
10564                      
10565                 
10566         }
10567          
10568         var settings=this;
10569         ['xs','sm','md','lg'].map(function(size){
10570             if (settings[size]) {
10571                 cfg.cls += ' col-' + size + '-' + settings[size];
10572             }
10573         });
10574         
10575         return cfg;
10576         
10577     },
10578     
10579     // private
10580     initEvents: function()
10581     {
10582         
10583         if (!this.store) {
10584             throw "can not find store for combo";
10585         }
10586         this.store = Roo.factory(this.store, Roo.data);
10587         
10588         if(this.tickable){
10589             this.initTickableEvents();
10590             return;
10591         }
10592         
10593         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
10594         
10595         if(this.hiddenName){
10596             
10597             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10598             
10599             this.hiddenField.dom.value =
10600                 this.hiddenValue !== undefined ? this.hiddenValue :
10601                 this.value !== undefined ? this.value : '';
10602
10603             // prevent input submission
10604             this.el.dom.removeAttribute('name');
10605             this.hiddenField.dom.setAttribute('name', this.hiddenName);
10606              
10607              
10608         }
10609         //if(Roo.isGecko){
10610         //    this.el.dom.setAttribute('autocomplete', 'off');
10611         //}
10612         
10613         var cls = 'x-combo-list';
10614         
10615         //this.list = new Roo.Layer({
10616         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
10617         //});
10618         
10619         var _this = this;
10620         
10621         (function(){
10622             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
10623             _this.list.setWidth(lw);
10624         }).defer(100);
10625         
10626         this.list.on('mouseover', this.onViewOver, this);
10627         this.list.on('mousemove', this.onViewMove, this);
10628         
10629         this.list.on('scroll', this.onViewScroll, this);
10630         
10631         /*
10632         this.list.swallowEvent('mousewheel');
10633         this.assetHeight = 0;
10634
10635         if(this.title){
10636             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
10637             this.assetHeight += this.header.getHeight();
10638         }
10639
10640         this.innerList = this.list.createChild({cls:cls+'-inner'});
10641         this.innerList.on('mouseover', this.onViewOver, this);
10642         this.innerList.on('mousemove', this.onViewMove, this);
10643         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
10644         
10645         if(this.allowBlank && !this.pageSize && !this.disableClear){
10646             this.footer = this.list.createChild({cls:cls+'-ft'});
10647             this.pageTb = new Roo.Toolbar(this.footer);
10648            
10649         }
10650         if(this.pageSize){
10651             this.footer = this.list.createChild({cls:cls+'-ft'});
10652             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
10653                     {pageSize: this.pageSize});
10654             
10655         }
10656         
10657         if (this.pageTb && this.allowBlank && !this.disableClear) {
10658             var _this = this;
10659             this.pageTb.add(new Roo.Toolbar.Fill(), {
10660                 cls: 'x-btn-icon x-btn-clear',
10661                 text: '&#160;',
10662                 handler: function()
10663                 {
10664                     _this.collapse();
10665                     _this.clearValue();
10666                     _this.onSelect(false, -1);
10667                 }
10668             });
10669         }
10670         if (this.footer) {
10671             this.assetHeight += this.footer.getHeight();
10672         }
10673         */
10674             
10675         if(!this.tpl){
10676             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
10677         }
10678
10679         this.view = new Roo.View(this.list, this.tpl, {
10680             singleSelect:true, store: this.store, selectedClass: this.selectedClass
10681         });
10682         //this.view.wrapEl.setDisplayed(false);
10683         this.view.on('click', this.onViewClick, this);
10684         
10685         
10686         
10687         this.store.on('beforeload', this.onBeforeLoad, this);
10688         this.store.on('load', this.onLoad, this);
10689         this.store.on('loadexception', this.onLoadException, this);
10690         /*
10691         if(this.resizable){
10692             this.resizer = new Roo.Resizable(this.list,  {
10693                pinned:true, handles:'se'
10694             });
10695             this.resizer.on('resize', function(r, w, h){
10696                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
10697                 this.listWidth = w;
10698                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
10699                 this.restrictHeight();
10700             }, this);
10701             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
10702         }
10703         */
10704         if(!this.editable){
10705             this.editable = true;
10706             this.setEditable(false);
10707         }
10708         
10709         /*
10710         
10711         if (typeof(this.events.add.listeners) != 'undefined') {
10712             
10713             this.addicon = this.wrap.createChild(
10714                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
10715        
10716             this.addicon.on('click', function(e) {
10717                 this.fireEvent('add', this);
10718             }, this);
10719         }
10720         if (typeof(this.events.edit.listeners) != 'undefined') {
10721             
10722             this.editicon = this.wrap.createChild(
10723                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
10724             if (this.addicon) {
10725                 this.editicon.setStyle('margin-left', '40px');
10726             }
10727             this.editicon.on('click', function(e) {
10728                 
10729                 // we fire even  if inothing is selected..
10730                 this.fireEvent('edit', this, this.lastData );
10731                 
10732             }, this);
10733         }
10734         */
10735         
10736         this.keyNav = new Roo.KeyNav(this.inputEl(), {
10737             "up" : function(e){
10738                 this.inKeyMode = true;
10739                 this.selectPrev();
10740             },
10741
10742             "down" : function(e){
10743                 if(!this.isExpanded()){
10744                     this.onTriggerClick();
10745                 }else{
10746                     this.inKeyMode = true;
10747                     this.selectNext();
10748                 }
10749             },
10750
10751             "enter" : function(e){
10752 //                this.onViewClick();
10753                 //return true;
10754                 this.collapse();
10755                 
10756                 if(this.fireEvent("specialkey", this, e)){
10757                     this.onViewClick(false);
10758                 }
10759                 
10760                 return true;
10761             },
10762
10763             "esc" : function(e){
10764                 this.collapse();
10765             },
10766
10767             "tab" : function(e){
10768                 this.collapse();
10769                 
10770                 if(this.fireEvent("specialkey", this, e)){
10771                     this.onViewClick(false);
10772                 }
10773                 
10774                 return true;
10775             },
10776
10777             scope : this,
10778
10779             doRelay : function(foo, bar, hname){
10780                 if(hname == 'down' || this.scope.isExpanded()){
10781                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
10782                 }
10783                 return true;
10784             },
10785
10786             forceKeyDown: true
10787         });
10788         
10789         
10790         this.queryDelay = Math.max(this.queryDelay || 10,
10791                 this.mode == 'local' ? 10 : 250);
10792         
10793         
10794         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
10795         
10796         if(this.typeAhead){
10797             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
10798         }
10799         if(this.editable !== false){
10800             this.inputEl().on("keyup", this.onKeyUp, this);
10801         }
10802         if(this.forceSelection){
10803             this.inputEl().on('blur', this.doForce, this);
10804         }
10805         
10806         if(this.multiple){
10807             this.choices = this.el.select('ul.select2-choices', true).first();
10808             this.searchField = this.el.select('ul li.select2-search-field', true).first();
10809         }
10810     },
10811     
10812     initTickableEvents: function()
10813     {   
10814         this.createList();
10815         
10816         if(this.hiddenName){
10817             
10818             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10819             
10820             this.hiddenField.dom.value =
10821                 this.hiddenValue !== undefined ? this.hiddenValue :
10822                 this.value !== undefined ? this.value : '';
10823
10824             // prevent input submission
10825             this.el.dom.removeAttribute('name');
10826             this.hiddenField.dom.setAttribute('name', this.hiddenName);
10827              
10828              
10829         }
10830         
10831 //        this.list = this.el.select('ul.dropdown-menu',true).first();
10832         
10833         this.choices = this.el.select('ul.select2-choices', true).first();
10834         this.searchField = this.el.select('ul li.select2-search-field', true).first();
10835         if(this.triggerList){
10836             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
10837         }
10838          
10839         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
10840         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
10841         
10842         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
10843         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
10844         
10845         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
10846         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
10847         
10848         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
10849         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
10850         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
10851         
10852         this.okBtn.hide();
10853         this.cancelBtn.hide();
10854         
10855         var _this = this;
10856         
10857         (function(){
10858             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
10859             _this.list.setWidth(lw);
10860         }).defer(100);
10861         
10862         this.list.on('mouseover', this.onViewOver, this);
10863         this.list.on('mousemove', this.onViewMove, this);
10864         
10865         this.list.on('scroll', this.onViewScroll, this);
10866         
10867         if(!this.tpl){
10868             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>';
10869         }
10870
10871         this.view = new Roo.View(this.list, this.tpl, {
10872             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
10873         });
10874         
10875         //this.view.wrapEl.setDisplayed(false);
10876         this.view.on('click', this.onViewClick, this);
10877         
10878         
10879         
10880         this.store.on('beforeload', this.onBeforeLoad, this);
10881         this.store.on('load', this.onLoad, this);
10882         this.store.on('loadexception', this.onLoadException, this);
10883         
10884 //        this.keyNav = new Roo.KeyNav(this.inputEl(), {
10885 //            "up" : function(e){
10886 //                this.inKeyMode = true;
10887 //                this.selectPrev();
10888 //            },
10889 //
10890 //            "down" : function(e){
10891 //                if(!this.isExpanded()){
10892 //                    this.onTriggerClick();
10893 //                }else{
10894 //                    this.inKeyMode = true;
10895 //                    this.selectNext();
10896 //                }
10897 //            },
10898 //
10899 //            "enter" : function(e){
10900 ////                this.onViewClick();
10901 //                //return true;
10902 //                this.collapse();
10903 //                
10904 //                if(this.fireEvent("specialkey", this, e)){
10905 //                    this.onViewClick(false);
10906 //                }
10907 //                
10908 //                return true;
10909 //            },
10910 //
10911 //            "esc" : function(e){
10912 //                this.collapse();
10913 //            },
10914 //
10915 //            "tab" : function(e){
10916 //                this.collapse();
10917 //                
10918 //                if(this.fireEvent("specialkey", this, e)){
10919 //                    this.onViewClick(false);
10920 //                }
10921 //                
10922 //                return true;
10923 //            },
10924 //
10925 //            scope : this,
10926 //
10927 //            doRelay : function(foo, bar, hname){
10928 //                if(hname == 'down' || this.scope.isExpanded()){
10929 //                   return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
10930 //                }
10931 //                return true;
10932 //            },
10933 //
10934 //            forceKeyDown: true
10935 //        });
10936         
10937         
10938         this.queryDelay = Math.max(this.queryDelay || 10,
10939                 this.mode == 'local' ? 10 : 250);
10940         
10941         
10942         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
10943         
10944         if(this.typeAhead){
10945             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
10946         }
10947     },
10948
10949     onDestroy : function(){
10950         if(this.view){
10951             this.view.setStore(null);
10952             this.view.el.removeAllListeners();
10953             this.view.el.remove();
10954             this.view.purgeListeners();
10955         }
10956         if(this.list){
10957             this.list.dom.innerHTML  = '';
10958         }
10959         
10960         if(this.store){
10961             this.store.un('beforeload', this.onBeforeLoad, this);
10962             this.store.un('load', this.onLoad, this);
10963             this.store.un('loadexception', this.onLoadException, this);
10964         }
10965         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
10966     },
10967
10968     // private
10969     fireKey : function(e){
10970         if(e.isNavKeyPress() && !this.list.isVisible()){
10971             this.fireEvent("specialkey", this, e);
10972         }
10973     },
10974
10975     // private
10976     onResize: function(w, h){
10977 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
10978 //        
10979 //        if(typeof w != 'number'){
10980 //            // we do not handle it!?!?
10981 //            return;
10982 //        }
10983 //        var tw = this.trigger.getWidth();
10984 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
10985 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
10986 //        var x = w - tw;
10987 //        this.inputEl().setWidth( this.adjustWidth('input', x));
10988 //            
10989 //        //this.trigger.setStyle('left', x+'px');
10990 //        
10991 //        if(this.list && this.listWidth === undefined){
10992 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
10993 //            this.list.setWidth(lw);
10994 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
10995 //        }
10996         
10997     
10998         
10999     },
11000
11001     /**
11002      * Allow or prevent the user from directly editing the field text.  If false is passed,
11003      * the user will only be able to select from the items defined in the dropdown list.  This method
11004      * is the runtime equivalent of setting the 'editable' config option at config time.
11005      * @param {Boolean} value True to allow the user to directly edit the field text
11006      */
11007     setEditable : function(value){
11008         if(value == this.editable){
11009             return;
11010         }
11011         this.editable = value;
11012         if(!value){
11013             this.inputEl().dom.setAttribute('readOnly', true);
11014             this.inputEl().on('mousedown', this.onTriggerClick,  this);
11015             this.inputEl().addClass('x-combo-noedit');
11016         }else{
11017             this.inputEl().dom.setAttribute('readOnly', false);
11018             this.inputEl().un('mousedown', this.onTriggerClick,  this);
11019             this.inputEl().removeClass('x-combo-noedit');
11020         }
11021     },
11022
11023     // private
11024     
11025     onBeforeLoad : function(combo,opts){
11026         if(!this.hasFocus){
11027             return;
11028         }
11029          if (!opts.add) {
11030             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
11031          }
11032 //        this.restrictHeight();
11033         this.selectedIndex = -1;
11034     },
11035
11036     // private
11037     onLoad : function(){
11038         
11039         this.hasQuery = false;
11040         
11041         if(!this.hasFocus){
11042             return;
11043         }
11044         
11045         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11046             this.loading.hide();
11047         }
11048         
11049         if(this.store.getCount() > 0){
11050             this.expand();
11051 //            this.restrictHeight();
11052             if(this.lastQuery == this.allQuery){
11053                 if(this.editable && !this.tickable){
11054                     this.inputEl().dom.select();
11055                 }
11056                 
11057                 if(!this.selectByValue(this.value, true) && this.autoFocus && (typeof(this.store.lastOptions.add) == 'undefined' || this.store.lastOptions.add != true)){
11058                     this.select(0, true);
11059                 }
11060             }else{
11061                 if(this.autoFocus){
11062                     this.selectNext();
11063                 }
11064                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
11065                     this.taTask.delay(this.typeAheadDelay);
11066                 }
11067             }
11068         }else{
11069             this.onEmptyResults();
11070         }
11071         
11072         //this.el.focus();
11073     },
11074     // private
11075     onLoadException : function()
11076     {
11077         this.hasQuery = false;
11078         
11079         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11080             this.loading.hide();
11081         }
11082         
11083         this.collapse();
11084         Roo.log(this.store.reader.jsonData);
11085         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
11086             // fixme
11087             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
11088         }
11089         
11090         
11091     },
11092     // private
11093     onTypeAhead : function(){
11094         if(this.store.getCount() > 0){
11095             var r = this.store.getAt(0);
11096             var newValue = r.data[this.displayField];
11097             var len = newValue.length;
11098             var selStart = this.getRawValue().length;
11099             
11100             if(selStart != len){
11101                 this.setRawValue(newValue);
11102                 this.selectText(selStart, newValue.length);
11103             }
11104         }
11105     },
11106
11107     // private
11108     onSelect : function(record, index){
11109         
11110         if(this.fireEvent('beforeselect', this, record, index) !== false){
11111         
11112             this.setFromData(index > -1 ? record.data : false);
11113             
11114             this.collapse();
11115             this.fireEvent('select', this, record, index);
11116         }
11117     },
11118
11119     /**
11120      * Returns the currently selected field value or empty string if no value is set.
11121      * @return {String} value The selected value
11122      */
11123     getValue : function(){
11124         
11125         if(this.multiple){
11126             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
11127         }
11128         
11129         if(this.valueField){
11130             return typeof this.value != 'undefined' ? this.value : '';
11131         }else{
11132             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
11133         }
11134     },
11135
11136     /**
11137      * Clears any text/value currently set in the field
11138      */
11139     clearValue : function(){
11140         if(this.hiddenField){
11141             this.hiddenField.dom.value = '';
11142         }
11143         this.value = '';
11144         this.setRawValue('');
11145         this.lastSelectionText = '';
11146         
11147     },
11148
11149     /**
11150      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
11151      * will be displayed in the field.  If the value does not match the data value of an existing item,
11152      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
11153      * Otherwise the field will be blank (although the value will still be set).
11154      * @param {String} value The value to match
11155      */
11156     setValue : function(v){
11157         if(this.multiple){
11158             this.syncValue();
11159             return;
11160         }
11161         
11162         var text = v;
11163         if(this.valueField){
11164             var r = this.findRecord(this.valueField, v);
11165             if(r){
11166                 text = r.data[this.displayField];
11167             }else if(this.valueNotFoundText !== undefined){
11168                 text = this.valueNotFoundText;
11169             }
11170         }
11171         this.lastSelectionText = text;
11172         if(this.hiddenField){
11173             this.hiddenField.dom.value = v;
11174         }
11175         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
11176         this.value = v;
11177     },
11178     /**
11179      * @property {Object} the last set data for the element
11180      */
11181     
11182     lastData : false,
11183     /**
11184      * Sets the value of the field based on a object which is related to the record format for the store.
11185      * @param {Object} value the value to set as. or false on reset?
11186      */
11187     setFromData : function(o){
11188         
11189         if(this.multiple){
11190             if(typeof o.display_name !== 'string'){
11191                 for(var i=0;i<o.display_name.length;i++){
11192                     this.addItem({'id':o.id[i],'display_name':o.display_name[i]});
11193                 }
11194                 return;
11195             }
11196             this.addItem(o);
11197             return;
11198         }
11199             
11200         var dv = ''; // display value
11201         var vv = ''; // value value..
11202         this.lastData = o;
11203         if (this.displayField) {
11204             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11205         } else {
11206             // this is an error condition!!!
11207             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
11208         }
11209         
11210         if(this.valueField){
11211             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
11212         }
11213         
11214         if(this.hiddenField){
11215             this.hiddenField.dom.value = vv;
11216             
11217             this.lastSelectionText = dv;
11218             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11219             this.value = vv;
11220             return;
11221         }
11222         // no hidden field.. - we store the value in 'value', but still display
11223         // display field!!!!
11224         this.lastSelectionText = dv;
11225         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11226         this.value = vv;
11227         
11228         
11229     },
11230     // private
11231     reset : function(){
11232         // overridden so that last data is reset..
11233         this.setValue(this.originalValue);
11234         this.clearInvalid();
11235         this.lastData = false;
11236         if (this.view) {
11237             this.view.clearSelections();
11238         }
11239     },
11240     // private
11241     findRecord : function(prop, value){
11242         var record;
11243         if(this.store.getCount() > 0){
11244             this.store.each(function(r){
11245                 if(r.data[prop] == value){
11246                     record = r;
11247                     return false;
11248                 }
11249                 return true;
11250             });
11251         }
11252         return record;
11253     },
11254     
11255     getName: function()
11256     {
11257         // returns hidden if it's set..
11258         if (!this.rendered) {return ''};
11259         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
11260         
11261     },
11262     // private
11263     onViewMove : function(e, t){
11264         this.inKeyMode = false;
11265     },
11266
11267     // private
11268     onViewOver : function(e, t){
11269         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
11270             return;
11271         }
11272         var item = this.view.findItemFromChild(t);
11273         
11274         if(item){
11275             var index = this.view.indexOf(item);
11276             this.select(index, false);
11277         }
11278     },
11279
11280     // private
11281     onViewClick : function(view, doFocus, el, e)
11282     {
11283         var index = this.view.getSelectedIndexes()[0];
11284         
11285         var r = this.store.getAt(index);
11286         
11287         if(this.tickable){
11288             
11289             if(e.getTarget().nodeName.toLowerCase() != 'input'){
11290                 return;
11291             }
11292             
11293             var rm = false;
11294             var _this = this;
11295             
11296             Roo.each(this.tickItems, function(v,k){
11297                 
11298                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
11299                     _this.tickItems.splice(k, 1);
11300                     rm = true;
11301                     return;
11302                 }
11303             })
11304             
11305             if(rm){
11306                 return;
11307             }
11308             
11309             this.tickItems.push(r.data);
11310             return;
11311         }
11312         
11313         if(r){
11314             this.onSelect(r, index);
11315         }
11316         if(doFocus !== false && !this.blockFocus){
11317             this.inputEl().focus();
11318         }
11319     },
11320
11321     // private
11322     restrictHeight : function(){
11323         //this.innerList.dom.style.height = '';
11324         //var inner = this.innerList.dom;
11325         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
11326         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
11327         //this.list.beginUpdate();
11328         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
11329         this.list.alignTo(this.inputEl(), this.listAlign);
11330         this.list.alignTo(this.inputEl(), this.listAlign);
11331         //this.list.endUpdate();
11332     },
11333
11334     // private
11335     onEmptyResults : function(){
11336         this.collapse();
11337     },
11338
11339     /**
11340      * Returns true if the dropdown list is expanded, else false.
11341      */
11342     isExpanded : function(){
11343         return this.list.isVisible();
11344     },
11345
11346     /**
11347      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
11348      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11349      * @param {String} value The data value of the item to select
11350      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11351      * selected item if it is not currently in view (defaults to true)
11352      * @return {Boolean} True if the value matched an item in the list, else false
11353      */
11354     selectByValue : function(v, scrollIntoView){
11355         if(v !== undefined && v !== null){
11356             var r = this.findRecord(this.valueField || this.displayField, v);
11357             if(r){
11358                 this.select(this.store.indexOf(r), scrollIntoView);
11359                 return true;
11360             }
11361         }
11362         return false;
11363     },
11364
11365     /**
11366      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
11367      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11368      * @param {Number} index The zero-based index of the list item to select
11369      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11370      * selected item if it is not currently in view (defaults to true)
11371      */
11372     select : function(index, scrollIntoView){
11373         this.selectedIndex = index;
11374         this.view.select(index);
11375         if(scrollIntoView !== false){
11376             var el = this.view.getNode(index);
11377             if(el && !this.multiple && !this.tickable){
11378                 this.list.scrollChildIntoView(el, false);
11379             }
11380         }
11381     },
11382
11383     // private
11384     selectNext : function(){
11385         var ct = this.store.getCount();
11386         if(ct > 0){
11387             if(this.selectedIndex == -1){
11388                 this.select(0);
11389             }else if(this.selectedIndex < ct-1){
11390                 this.select(this.selectedIndex+1);
11391             }
11392         }
11393     },
11394
11395     // private
11396     selectPrev : function(){
11397         var ct = this.store.getCount();
11398         if(ct > 0){
11399             if(this.selectedIndex == -1){
11400                 this.select(0);
11401             }else if(this.selectedIndex != 0){
11402                 this.select(this.selectedIndex-1);
11403             }
11404         }
11405     },
11406
11407     // private
11408     onKeyUp : function(e){
11409         if(this.editable !== false && !e.isSpecialKey()){
11410             this.lastKey = e.getKey();
11411             this.dqTask.delay(this.queryDelay);
11412         }
11413     },
11414
11415     // private
11416     validateBlur : function(){
11417         return !this.list || !this.list.isVisible();   
11418     },
11419
11420     // private
11421     initQuery : function(){
11422         this.doQuery(this.getRawValue());
11423     },
11424
11425     // private
11426     doForce : function(){
11427         if(this.inputEl().dom.value.length > 0){
11428             this.inputEl().dom.value =
11429                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
11430              
11431         }
11432     },
11433
11434     /**
11435      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
11436      * query allowing the query action to be canceled if needed.
11437      * @param {String} query The SQL query to execute
11438      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
11439      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
11440      * saved in the current store (defaults to false)
11441      */
11442     doQuery : function(q, forceAll){
11443         
11444         if(q === undefined || q === null){
11445             q = '';
11446         }
11447         var qe = {
11448             query: q,
11449             forceAll: forceAll,
11450             combo: this,
11451             cancel:false
11452         };
11453         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
11454             return false;
11455         }
11456         q = qe.query;
11457         
11458         forceAll = qe.forceAll;
11459         if(forceAll === true || (q.length >= this.minChars)){
11460             
11461             this.hasQuery = true;
11462             
11463             if(this.lastQuery != q || this.alwaysQuery){
11464                 this.lastQuery = q;
11465                 if(this.mode == 'local'){
11466                     this.selectedIndex = -1;
11467                     if(forceAll){
11468                         this.store.clearFilter();
11469                     }else{
11470                         this.store.filter(this.displayField, q);
11471                     }
11472                     this.onLoad();
11473                 }else{
11474                     this.store.baseParams[this.queryParam] = q;
11475                     
11476                     var options = {params : this.getParams(q)};
11477                     
11478                     if(this.loadNext){
11479                         options.add = true;
11480                         options.params.start = this.page * this.pageSize;
11481                     }
11482                     
11483                     this.store.load(options);
11484                     /*
11485                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
11486                      *  we should expand the list on onLoad
11487                      *  so command out it
11488                      */
11489 //                    this.expand();
11490                 }
11491             }else{
11492                 this.selectedIndex = -1;
11493                 this.onLoad();   
11494             }
11495         }
11496         
11497         this.loadNext = false;
11498     },
11499
11500     // private
11501     getParams : function(q){
11502         var p = {};
11503         //p[this.queryParam] = q;
11504         
11505         if(this.pageSize){
11506             p.start = 0;
11507             p.limit = this.pageSize;
11508         }
11509         return p;
11510     },
11511
11512     /**
11513      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
11514      */
11515     collapse : function(){
11516         if(!this.isExpanded()){
11517             return;
11518         }
11519         
11520         this.list.hide();
11521         
11522         if(this.tickable){
11523             this.okBtn.hide();
11524             this.cancelBtn.hide();
11525             this.trigger.show();
11526         }
11527         
11528         Roo.get(document).un('mousedown', this.collapseIf, this);
11529         Roo.get(document).un('mousewheel', this.collapseIf, this);
11530         if (!this.editable) {
11531             Roo.get(document).un('keydown', this.listKeyPress, this);
11532         }
11533         this.fireEvent('collapse', this);
11534     },
11535
11536     // private
11537     collapseIf : function(e){
11538         var in_combo  = e.within(this.el);
11539         var in_list =  e.within(this.list);
11540         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
11541         
11542         if (in_combo || in_list || is_list) {
11543             //e.stopPropagation();
11544             return;
11545         }
11546         
11547         if(this.tickable){
11548             this.onTickableFooterButtonClick(e, false, false);
11549         }
11550
11551         this.collapse();
11552         
11553     },
11554
11555     /**
11556      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
11557      */
11558     expand : function(){
11559        
11560         if(this.isExpanded() || !this.hasFocus){
11561             return;
11562         }
11563         
11564         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
11565         this.list.setWidth(lw);
11566         
11567         
11568          Roo.log('expand');
11569         
11570         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
11571         this.list.setWidth(lw);
11572             
11573         this.list.show();
11574         
11575         this.restrictHeight();
11576         
11577         if(this.tickable){
11578             
11579             this.tickItems = Roo.apply([], this.item);
11580             
11581             this.okBtn.show();
11582             this.cancelBtn.show();
11583             this.trigger.hide();
11584             
11585         }
11586         
11587         Roo.get(document).on('mousedown', this.collapseIf, this);
11588         Roo.get(document).on('mousewheel', this.collapseIf, this);
11589         if (!this.editable) {
11590             Roo.get(document).on('keydown', this.listKeyPress, this);
11591         }
11592         
11593         this.fireEvent('expand', this);
11594     },
11595
11596     // private
11597     // Implements the default empty TriggerField.onTriggerClick function
11598     onTriggerClick : function(e)
11599     {
11600         Roo.log('trigger click');
11601         
11602         if(this.disabled || !this.triggerList){
11603             return;
11604         }
11605         
11606         this.page = 0;
11607         this.loadNext = false;
11608         
11609         if(this.isExpanded()){
11610             this.collapse();
11611             if (!this.blockFocus) {
11612                 this.inputEl().focus();
11613             }
11614             
11615         }else {
11616             this.hasFocus = true;
11617             if(this.triggerAction == 'all') {
11618                 this.doQuery(this.allQuery, true);
11619             } else {
11620                 this.doQuery(this.getRawValue());
11621             }
11622             if (!this.blockFocus) {
11623                 this.inputEl().focus();
11624             }
11625         }
11626     },
11627     
11628     onTickableTriggerClick : function(e)
11629     {
11630         if(this.disabled){
11631             return;
11632         }
11633         
11634         this.page = 0;
11635         this.loadNext = false;
11636         this.hasFocus = true;
11637         
11638         if(this.triggerAction == 'all') {
11639             this.doQuery(this.allQuery, true);
11640         } else {
11641             this.doQuery(this.getRawValue());
11642         }
11643     },
11644     
11645     onSearchFieldClick : function(e)
11646     {
11647         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
11648             return;
11649         }
11650         
11651         this.page = 0;
11652         this.loadNext = false;
11653         this.hasFocus = true;
11654         
11655         if(this.triggerAction == 'all') {
11656             this.doQuery(this.allQuery, true);
11657         } else {
11658             this.doQuery(this.getRawValue());
11659         }
11660     },
11661     
11662     listKeyPress : function(e)
11663     {
11664         //Roo.log('listkeypress');
11665         // scroll to first matching element based on key pres..
11666         if (e.isSpecialKey()) {
11667             return false;
11668         }
11669         var k = String.fromCharCode(e.getKey()).toUpperCase();
11670         //Roo.log(k);
11671         var match  = false;
11672         var csel = this.view.getSelectedNodes();
11673         var cselitem = false;
11674         if (csel.length) {
11675             var ix = this.view.indexOf(csel[0]);
11676             cselitem  = this.store.getAt(ix);
11677             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
11678                 cselitem = false;
11679             }
11680             
11681         }
11682         
11683         this.store.each(function(v) { 
11684             if (cselitem) {
11685                 // start at existing selection.
11686                 if (cselitem.id == v.id) {
11687                     cselitem = false;
11688                 }
11689                 return true;
11690             }
11691                 
11692             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
11693                 match = this.store.indexOf(v);
11694                 return false;
11695             }
11696             return true;
11697         }, this);
11698         
11699         if (match === false) {
11700             return true; // no more action?
11701         }
11702         // scroll to?
11703         this.view.select(match);
11704         var sn = Roo.get(this.view.getSelectedNodes()[0])
11705         //sn.scrollIntoView(sn.dom.parentNode, false);
11706     },
11707     
11708     onViewScroll : function(e, t){
11709         
11710         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){
11711             return;
11712         }
11713         
11714         this.hasQuery = true;
11715         
11716         this.loading = this.list.select('.loading', true).first();
11717         
11718         if(this.loading === null){
11719             this.list.createChild({
11720                 tag: 'div',
11721                 cls: 'loading select2-more-results select2-active',
11722                 html: 'Loading more results...'
11723             })
11724             
11725             this.loading = this.list.select('.loading', true).first();
11726             
11727             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
11728             
11729             this.loading.hide();
11730         }
11731         
11732         this.loading.show();
11733         
11734         var _combo = this;
11735         
11736         this.page++;
11737         this.loadNext = true;
11738         
11739         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
11740         
11741         return;
11742     },
11743     
11744     addItem : function(o)
11745     {   
11746         var dv = ''; // display value
11747         
11748         if (this.displayField) {
11749             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11750         } else {
11751             // this is an error condition!!!
11752             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
11753         }
11754         
11755         if(!dv.length){
11756             return;
11757         }
11758         
11759         var choice = this.choices.createChild({
11760             tag: 'li',
11761             cls: 'select2-search-choice',
11762             cn: [
11763                 {
11764                     tag: 'div',
11765                     html: dv
11766                 },
11767                 {
11768                     tag: 'a',
11769                     href: '#',
11770                     cls: 'select2-search-choice-close',
11771                     tabindex: '-1'
11772                 }
11773             ]
11774             
11775         }, this.searchField);
11776         
11777         var close = choice.select('a.select2-search-choice-close', true).first()
11778         
11779         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
11780         
11781         this.item.push(o);
11782         
11783         this.lastData = o;
11784         
11785         this.syncValue();
11786         
11787         this.inputEl().dom.value = '';
11788         
11789     },
11790     
11791     onRemoveItem : function(e, _self, o)
11792     {
11793         e.preventDefault();
11794         var index = this.item.indexOf(o.data) * 1;
11795         
11796         if( index < 0){
11797             Roo.log('not this item?!');
11798             return;
11799         }
11800         
11801         this.item.splice(index, 1);
11802         o.item.remove();
11803         
11804         this.syncValue();
11805         
11806         this.fireEvent('remove', this, e);
11807         
11808     },
11809     
11810     syncValue : function()
11811     {
11812         if(!this.item.length){
11813             this.clearValue();
11814             return;
11815         }
11816             
11817         var value = [];
11818         var _this = this;
11819         Roo.each(this.item, function(i){
11820             if(_this.valueField){
11821                 value.push(i[_this.valueField]);
11822                 return;
11823             }
11824
11825             value.push(i);
11826         });
11827
11828         this.value = value.join(',');
11829
11830         if(this.hiddenField){
11831             this.hiddenField.dom.value = this.value;
11832         }
11833     },
11834     
11835     clearItem : function()
11836     {
11837         if(!this.multiple){
11838             return;
11839         }
11840         
11841         this.item = [];
11842         
11843         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
11844            c.remove();
11845         });
11846         
11847         this.syncValue();
11848     },
11849     
11850     inputEl: function ()
11851     {
11852         if(this.tickable){
11853             return this.searchField;
11854         }
11855         return this.el.select('input.form-control',true).first();
11856     },
11857     
11858     
11859     onTickableFooterButtonClick : function(e, btn, el)
11860     {
11861         e.preventDefault();
11862         
11863         if(btn && btn.name == 'cancel'){
11864             this.tickItems = Roo.apply([], this.item);
11865             this.collapse();
11866             return;
11867         }
11868         
11869         this.clearItem();
11870         
11871         var _this = this;
11872         
11873         Roo.each(this.tickItems, function(o){
11874             _this.addItem(o);
11875         });
11876         
11877         this.collapse();
11878         
11879     }
11880     
11881     
11882
11883     /** 
11884     * @cfg {Boolean} grow 
11885     * @hide 
11886     */
11887     /** 
11888     * @cfg {Number} growMin 
11889     * @hide 
11890     */
11891     /** 
11892     * @cfg {Number} growMax 
11893     * @hide 
11894     */
11895     /**
11896      * @hide
11897      * @method autoSize
11898      */
11899 });
11900 /*
11901  * Based on:
11902  * Ext JS Library 1.1.1
11903  * Copyright(c) 2006-2007, Ext JS, LLC.
11904  *
11905  * Originally Released Under LGPL - original licence link has changed is not relivant.
11906  *
11907  * Fork - LGPL
11908  * <script type="text/javascript">
11909  */
11910
11911 /**
11912  * @class Roo.View
11913  * @extends Roo.util.Observable
11914  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
11915  * This class also supports single and multi selection modes. <br>
11916  * Create a data model bound view:
11917  <pre><code>
11918  var store = new Roo.data.Store(...);
11919
11920  var view = new Roo.View({
11921     el : "my-element",
11922     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
11923  
11924     singleSelect: true,
11925     selectedClass: "ydataview-selected",
11926     store: store
11927  });
11928
11929  // listen for node click?
11930  view.on("click", function(vw, index, node, e){
11931  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
11932  });
11933
11934  // load XML data
11935  dataModel.load("foobar.xml");
11936  </code></pre>
11937  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
11938  * <br><br>
11939  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
11940  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
11941  * 
11942  * Note: old style constructor is still suported (container, template, config)
11943  * 
11944  * @constructor
11945  * Create a new View
11946  * @param {Object} config The config object
11947  * 
11948  */
11949 Roo.View = function(config, depreciated_tpl, depreciated_config){
11950     
11951     this.parent = false;
11952     
11953     if (typeof(depreciated_tpl) == 'undefined') {
11954         // new way.. - universal constructor.
11955         Roo.apply(this, config);
11956         this.el  = Roo.get(this.el);
11957     } else {
11958         // old format..
11959         this.el  = Roo.get(config);
11960         this.tpl = depreciated_tpl;
11961         Roo.apply(this, depreciated_config);
11962     }
11963     this.wrapEl  = this.el.wrap().wrap();
11964     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
11965     
11966     
11967     if(typeof(this.tpl) == "string"){
11968         this.tpl = new Roo.Template(this.tpl);
11969     } else {
11970         // support xtype ctors..
11971         this.tpl = new Roo.factory(this.tpl, Roo);
11972     }
11973     
11974     
11975     this.tpl.compile();
11976     
11977     /** @private */
11978     this.addEvents({
11979         /**
11980          * @event beforeclick
11981          * Fires before a click is processed. Returns false to cancel the default action.
11982          * @param {Roo.View} this
11983          * @param {Number} index The index of the target node
11984          * @param {HTMLElement} node The target node
11985          * @param {Roo.EventObject} e The raw event object
11986          */
11987             "beforeclick" : true,
11988         /**
11989          * @event click
11990          * Fires when a template node is clicked.
11991          * @param {Roo.View} this
11992          * @param {Number} index The index of the target node
11993          * @param {HTMLElement} node The target node
11994          * @param {Roo.EventObject} e The raw event object
11995          */
11996             "click" : true,
11997         /**
11998          * @event dblclick
11999          * Fires when a template node is double clicked.
12000          * @param {Roo.View} this
12001          * @param {Number} index The index of the target node
12002          * @param {HTMLElement} node The target node
12003          * @param {Roo.EventObject} e The raw event object
12004          */
12005             "dblclick" : true,
12006         /**
12007          * @event contextmenu
12008          * Fires when a template node is right clicked.
12009          * @param {Roo.View} this
12010          * @param {Number} index The index of the target node
12011          * @param {HTMLElement} node The target node
12012          * @param {Roo.EventObject} e The raw event object
12013          */
12014             "contextmenu" : true,
12015         /**
12016          * @event selectionchange
12017          * Fires when the selected nodes change.
12018          * @param {Roo.View} this
12019          * @param {Array} selections Array of the selected nodes
12020          */
12021             "selectionchange" : true,
12022     
12023         /**
12024          * @event beforeselect
12025          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
12026          * @param {Roo.View} this
12027          * @param {HTMLElement} node The node to be selected
12028          * @param {Array} selections Array of currently selected nodes
12029          */
12030             "beforeselect" : true,
12031         /**
12032          * @event preparedata
12033          * Fires on every row to render, to allow you to change the data.
12034          * @param {Roo.View} this
12035          * @param {Object} data to be rendered (change this)
12036          */
12037           "preparedata" : true
12038           
12039           
12040         });
12041
12042
12043
12044     this.el.on({
12045         "click": this.onClick,
12046         "dblclick": this.onDblClick,
12047         "contextmenu": this.onContextMenu,
12048         scope:this
12049     });
12050
12051     this.selections = [];
12052     this.nodes = [];
12053     this.cmp = new Roo.CompositeElementLite([]);
12054     if(this.store){
12055         this.store = Roo.factory(this.store, Roo.data);
12056         this.setStore(this.store, true);
12057     }
12058     
12059     if ( this.footer && this.footer.xtype) {
12060            
12061          var fctr = this.wrapEl.appendChild(document.createElement("div"));
12062         
12063         this.footer.dataSource = this.store
12064         this.footer.container = fctr;
12065         this.footer = Roo.factory(this.footer, Roo);
12066         fctr.insertFirst(this.el);
12067         
12068         // this is a bit insane - as the paging toolbar seems to detach the el..
12069 //        dom.parentNode.parentNode.parentNode
12070          // they get detached?
12071     }
12072     
12073     
12074     Roo.View.superclass.constructor.call(this);
12075     
12076     
12077 };
12078
12079 Roo.extend(Roo.View, Roo.util.Observable, {
12080     
12081      /**
12082      * @cfg {Roo.data.Store} store Data store to load data from.
12083      */
12084     store : false,
12085     
12086     /**
12087      * @cfg {String|Roo.Element} el The container element.
12088      */
12089     el : '',
12090     
12091     /**
12092      * @cfg {String|Roo.Template} tpl The template used by this View 
12093      */
12094     tpl : false,
12095     /**
12096      * @cfg {String} dataName the named area of the template to use as the data area
12097      *                          Works with domtemplates roo-name="name"
12098      */
12099     dataName: false,
12100     /**
12101      * @cfg {String} selectedClass The css class to add to selected nodes
12102      */
12103     selectedClass : "x-view-selected",
12104      /**
12105      * @cfg {String} emptyText The empty text to show when nothing is loaded.
12106      */
12107     emptyText : "",
12108     
12109     /**
12110      * @cfg {String} text to display on mask (default Loading)
12111      */
12112     mask : false,
12113     /**
12114      * @cfg {Boolean} multiSelect Allow multiple selection
12115      */
12116     multiSelect : false,
12117     /**
12118      * @cfg {Boolean} singleSelect Allow single selection
12119      */
12120     singleSelect:  false,
12121     
12122     /**
12123      * @cfg {Boolean} toggleSelect - selecting 
12124      */
12125     toggleSelect : false,
12126     
12127     /**
12128      * @cfg {Boolean} tickable - selecting 
12129      */
12130     tickable : false,
12131     
12132     /**
12133      * Returns the element this view is bound to.
12134      * @return {Roo.Element}
12135      */
12136     getEl : function(){
12137         return this.wrapEl;
12138     },
12139     
12140     
12141
12142     /**
12143      * Refreshes the view. - called by datachanged on the store. - do not call directly.
12144      */
12145     refresh : function(){
12146         Roo.log('refresh');
12147         var t = this.tpl;
12148         
12149         // if we are using something like 'domtemplate', then
12150         // the what gets used is:
12151         // t.applySubtemplate(NAME, data, wrapping data..)
12152         // the outer template then get' applied with
12153         //     the store 'extra data'
12154         // and the body get's added to the
12155         //      roo-name="data" node?
12156         //      <span class='roo-tpl-{name}'></span> ?????
12157         
12158         
12159         
12160         this.clearSelections();
12161         this.el.update("");
12162         var html = [];
12163         var records = this.store.getRange();
12164         if(records.length < 1) {
12165             
12166             // is this valid??  = should it render a template??
12167             
12168             this.el.update(this.emptyText);
12169             return;
12170         }
12171         var el = this.el;
12172         if (this.dataName) {
12173             this.el.update(t.apply(this.store.meta)); //????
12174             el = this.el.child('.roo-tpl-' + this.dataName);
12175         }
12176         
12177         for(var i = 0, len = records.length; i < len; i++){
12178             var data = this.prepareData(records[i].data, i, records[i]);
12179             this.fireEvent("preparedata", this, data, i, records[i]);
12180             
12181             var d = Roo.apply({}, data);
12182             
12183             if(this.tickable){
12184                 Roo.apply(d, {'roo-id' : Roo.id()});
12185                 
12186                 var _this = this;
12187             
12188                 Roo.each(this.parent.item, function(item){
12189                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
12190                         return;
12191                     }
12192                     Roo.apply(d, {'roo-data-checked' : 'checked'});
12193                 });
12194             }
12195             
12196             html[html.length] = Roo.util.Format.trim(
12197                 this.dataName ?
12198                     t.applySubtemplate(this.dataName, d, this.store.meta) :
12199                     t.apply(d)
12200             );
12201         }
12202         
12203         
12204         
12205         el.update(html.join(""));
12206         this.nodes = el.dom.childNodes;
12207         this.updateIndexes(0);
12208     },
12209     
12210
12211     /**
12212      * Function to override to reformat the data that is sent to
12213      * the template for each node.
12214      * DEPRICATED - use the preparedata event handler.
12215      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
12216      * a JSON object for an UpdateManager bound view).
12217      */
12218     prepareData : function(data, index, record)
12219     {
12220         this.fireEvent("preparedata", this, data, index, record);
12221         return data;
12222     },
12223
12224     onUpdate : function(ds, record){
12225          Roo.log('on update');   
12226         this.clearSelections();
12227         var index = this.store.indexOf(record);
12228         var n = this.nodes[index];
12229         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
12230         n.parentNode.removeChild(n);
12231         this.updateIndexes(index, index);
12232     },
12233
12234     
12235     
12236 // --------- FIXME     
12237     onAdd : function(ds, records, index)
12238     {
12239         Roo.log(['on Add', ds, records, index] );        
12240         this.clearSelections();
12241         if(this.nodes.length == 0){
12242             this.refresh();
12243             return;
12244         }
12245         var n = this.nodes[index];
12246         for(var i = 0, len = records.length; i < len; i++){
12247             var d = this.prepareData(records[i].data, i, records[i]);
12248             if(n){
12249                 this.tpl.insertBefore(n, d);
12250             }else{
12251                 
12252                 this.tpl.append(this.el, d);
12253             }
12254         }
12255         this.updateIndexes(index);
12256     },
12257
12258     onRemove : function(ds, record, index){
12259         Roo.log('onRemove');
12260         this.clearSelections();
12261         var el = this.dataName  ?
12262             this.el.child('.roo-tpl-' + this.dataName) :
12263             this.el; 
12264         
12265         el.dom.removeChild(this.nodes[index]);
12266         this.updateIndexes(index);
12267     },
12268
12269     /**
12270      * Refresh an individual node.
12271      * @param {Number} index
12272      */
12273     refreshNode : function(index){
12274         this.onUpdate(this.store, this.store.getAt(index));
12275     },
12276
12277     updateIndexes : function(startIndex, endIndex){
12278         var ns = this.nodes;
12279         startIndex = startIndex || 0;
12280         endIndex = endIndex || ns.length - 1;
12281         for(var i = startIndex; i <= endIndex; i++){
12282             ns[i].nodeIndex = i;
12283         }
12284     },
12285
12286     /**
12287      * Changes the data store this view uses and refresh the view.
12288      * @param {Store} store
12289      */
12290     setStore : function(store, initial){
12291         if(!initial && this.store){
12292             this.store.un("datachanged", this.refresh);
12293             this.store.un("add", this.onAdd);
12294             this.store.un("remove", this.onRemove);
12295             this.store.un("update", this.onUpdate);
12296             this.store.un("clear", this.refresh);
12297             this.store.un("beforeload", this.onBeforeLoad);
12298             this.store.un("load", this.onLoad);
12299             this.store.un("loadexception", this.onLoad);
12300         }
12301         if(store){
12302           
12303             store.on("datachanged", this.refresh, this);
12304             store.on("add", this.onAdd, this);
12305             store.on("remove", this.onRemove, this);
12306             store.on("update", this.onUpdate, this);
12307             store.on("clear", this.refresh, this);
12308             store.on("beforeload", this.onBeforeLoad, this);
12309             store.on("load", this.onLoad, this);
12310             store.on("loadexception", this.onLoad, this);
12311         }
12312         
12313         if(store){
12314             this.refresh();
12315         }
12316     },
12317     /**
12318      * onbeforeLoad - masks the loading area.
12319      *
12320      */
12321     onBeforeLoad : function(store,opts)
12322     {
12323          Roo.log('onBeforeLoad');   
12324         if (!opts.add) {
12325             this.el.update("");
12326         }
12327         this.el.mask(this.mask ? this.mask : "Loading" ); 
12328     },
12329     onLoad : function ()
12330     {
12331         this.el.unmask();
12332     },
12333     
12334
12335     /**
12336      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
12337      * @param {HTMLElement} node
12338      * @return {HTMLElement} The template node
12339      */
12340     findItemFromChild : function(node){
12341         var el = this.dataName  ?
12342             this.el.child('.roo-tpl-' + this.dataName,true) :
12343             this.el.dom; 
12344         
12345         if(!node || node.parentNode == el){
12346                     return node;
12347             }
12348             var p = node.parentNode;
12349             while(p && p != el){
12350             if(p.parentNode == el){
12351                 return p;
12352             }
12353             p = p.parentNode;
12354         }
12355             return null;
12356     },
12357
12358     /** @ignore */
12359     onClick : function(e){
12360         var item = this.findItemFromChild(e.getTarget());
12361         if(item){
12362             var index = this.indexOf(item);
12363             if(this.onItemClick(item, index, e) !== false){
12364                 this.fireEvent("click", this, index, item, e);
12365             }
12366         }else{
12367             this.clearSelections();
12368         }
12369     },
12370
12371     /** @ignore */
12372     onContextMenu : function(e){
12373         var item = this.findItemFromChild(e.getTarget());
12374         if(item){
12375             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
12376         }
12377     },
12378
12379     /** @ignore */
12380     onDblClick : function(e){
12381         var item = this.findItemFromChild(e.getTarget());
12382         if(item){
12383             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
12384         }
12385     },
12386
12387     onItemClick : function(item, index, e)
12388     {
12389         if(this.fireEvent("beforeclick", this, index, item, e) === false){
12390             return false;
12391         }
12392         if (this.toggleSelect) {
12393             var m = this.isSelected(item) ? 'unselect' : 'select';
12394             Roo.log(m);
12395             var _t = this;
12396             _t[m](item, true, false);
12397             return true;
12398         }
12399         if(this.multiSelect || this.singleSelect){
12400             if(this.multiSelect && e.shiftKey && this.lastSelection){
12401                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
12402             }else{
12403                 this.select(item, this.multiSelect && e.ctrlKey);
12404                 this.lastSelection = item;
12405             }
12406             
12407             if(!this.tickable){
12408                 e.preventDefault();
12409             }
12410             
12411         }
12412         return true;
12413     },
12414
12415     /**
12416      * Get the number of selected nodes.
12417      * @return {Number}
12418      */
12419     getSelectionCount : function(){
12420         return this.selections.length;
12421     },
12422
12423     /**
12424      * Get the currently selected nodes.
12425      * @return {Array} An array of HTMLElements
12426      */
12427     getSelectedNodes : function(){
12428         return this.selections;
12429     },
12430
12431     /**
12432      * Get the indexes of the selected nodes.
12433      * @return {Array}
12434      */
12435     getSelectedIndexes : function(){
12436         var indexes = [], s = this.selections;
12437         for(var i = 0, len = s.length; i < len; i++){
12438             indexes.push(s[i].nodeIndex);
12439         }
12440         return indexes;
12441     },
12442
12443     /**
12444      * Clear all selections
12445      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
12446      */
12447     clearSelections : function(suppressEvent){
12448         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
12449             this.cmp.elements = this.selections;
12450             this.cmp.removeClass(this.selectedClass);
12451             this.selections = [];
12452             if(!suppressEvent){
12453                 this.fireEvent("selectionchange", this, this.selections);
12454             }
12455         }
12456     },
12457
12458     /**
12459      * Returns true if the passed node is selected
12460      * @param {HTMLElement/Number} node The node or node index
12461      * @return {Boolean}
12462      */
12463     isSelected : function(node){
12464         var s = this.selections;
12465         if(s.length < 1){
12466             return false;
12467         }
12468         node = this.getNode(node);
12469         return s.indexOf(node) !== -1;
12470     },
12471
12472     /**
12473      * Selects nodes.
12474      * @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
12475      * @param {Boolean} keepExisting (optional) true to keep existing selections
12476      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12477      */
12478     select : function(nodeInfo, keepExisting, suppressEvent){
12479         if(nodeInfo instanceof Array){
12480             if(!keepExisting){
12481                 this.clearSelections(true);
12482             }
12483             for(var i = 0, len = nodeInfo.length; i < len; i++){
12484                 this.select(nodeInfo[i], true, true);
12485             }
12486             return;
12487         } 
12488         var node = this.getNode(nodeInfo);
12489         if(!node || this.isSelected(node)){
12490             return; // already selected.
12491         }
12492         if(!keepExisting){
12493             this.clearSelections(true);
12494         }
12495         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
12496             Roo.fly(node).addClass(this.selectedClass);
12497             this.selections.push(node);
12498             if(!suppressEvent){
12499                 this.fireEvent("selectionchange", this, this.selections);
12500             }
12501         }
12502         
12503         
12504     },
12505       /**
12506      * Unselects nodes.
12507      * @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
12508      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
12509      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12510      */
12511     unselect : function(nodeInfo, keepExisting, suppressEvent)
12512     {
12513         if(nodeInfo instanceof Array){
12514             Roo.each(this.selections, function(s) {
12515                 this.unselect(s, nodeInfo);
12516             }, this);
12517             return;
12518         }
12519         var node = this.getNode(nodeInfo);
12520         if(!node || !this.isSelected(node)){
12521             Roo.log("not selected");
12522             return; // not selected.
12523         }
12524         // fireevent???
12525         var ns = [];
12526         Roo.each(this.selections, function(s) {
12527             if (s == node ) {
12528                 Roo.fly(node).removeClass(this.selectedClass);
12529
12530                 return;
12531             }
12532             ns.push(s);
12533         },this);
12534         
12535         this.selections= ns;
12536         this.fireEvent("selectionchange", this, this.selections);
12537     },
12538
12539     /**
12540      * Gets a template node.
12541      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12542      * @return {HTMLElement} The node or null if it wasn't found
12543      */
12544     getNode : function(nodeInfo){
12545         if(typeof nodeInfo == "string"){
12546             return document.getElementById(nodeInfo);
12547         }else if(typeof nodeInfo == "number"){
12548             return this.nodes[nodeInfo];
12549         }
12550         return nodeInfo;
12551     },
12552
12553     /**
12554      * Gets a range template nodes.
12555      * @param {Number} startIndex
12556      * @param {Number} endIndex
12557      * @return {Array} An array of nodes
12558      */
12559     getNodes : function(start, end){
12560         var ns = this.nodes;
12561         start = start || 0;
12562         end = typeof end == "undefined" ? ns.length - 1 : end;
12563         var nodes = [];
12564         if(start <= end){
12565             for(var i = start; i <= end; i++){
12566                 nodes.push(ns[i]);
12567             }
12568         } else{
12569             for(var i = start; i >= end; i--){
12570                 nodes.push(ns[i]);
12571             }
12572         }
12573         return nodes;
12574     },
12575
12576     /**
12577      * Finds the index of the passed node
12578      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12579      * @return {Number} The index of the node or -1
12580      */
12581     indexOf : function(node){
12582         node = this.getNode(node);
12583         if(typeof node.nodeIndex == "number"){
12584             return node.nodeIndex;
12585         }
12586         var ns = this.nodes;
12587         for(var i = 0, len = ns.length; i < len; i++){
12588             if(ns[i] == node){
12589                 return i;
12590             }
12591         }
12592         return -1;
12593     }
12594 });
12595 /*
12596  * - LGPL
12597  *
12598  * based on jquery fullcalendar
12599  * 
12600  */
12601
12602 Roo.bootstrap = Roo.bootstrap || {};
12603 /**
12604  * @class Roo.bootstrap.Calendar
12605  * @extends Roo.bootstrap.Component
12606  * Bootstrap Calendar class
12607  * @cfg {Boolean} loadMask (true|false) default false
12608  * @cfg {Object} header generate the user specific header of the calendar, default false
12609
12610  * @constructor
12611  * Create a new Container
12612  * @param {Object} config The config object
12613  */
12614
12615
12616
12617 Roo.bootstrap.Calendar = function(config){
12618     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
12619      this.addEvents({
12620         /**
12621              * @event select
12622              * Fires when a date is selected
12623              * @param {DatePicker} this
12624              * @param {Date} date The selected date
12625              */
12626         'select': true,
12627         /**
12628              * @event monthchange
12629              * Fires when the displayed month changes 
12630              * @param {DatePicker} this
12631              * @param {Date} date The selected month
12632              */
12633         'monthchange': true,
12634         /**
12635              * @event evententer
12636              * Fires when mouse over an event
12637              * @param {Calendar} this
12638              * @param {event} Event
12639              */
12640         'evententer': true,
12641         /**
12642              * @event eventleave
12643              * Fires when the mouse leaves an
12644              * @param {Calendar} this
12645              * @param {event}
12646              */
12647         'eventleave': true,
12648         /**
12649              * @event eventclick
12650              * Fires when the mouse click an
12651              * @param {Calendar} this
12652              * @param {event}
12653              */
12654         'eventclick': true
12655         
12656     });
12657
12658 };
12659
12660 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
12661     
12662      /**
12663      * @cfg {Number} startDay
12664      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
12665      */
12666     startDay : 0,
12667     
12668     loadMask : false,
12669     
12670     header : false,
12671       
12672     getAutoCreate : function(){
12673         
12674         
12675         var fc_button = function(name, corner, style, content ) {
12676             return Roo.apply({},{
12677                 tag : 'span',
12678                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
12679                          (corner.length ?
12680                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
12681                             ''
12682                         ),
12683                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
12684                 unselectable: 'on'
12685             });
12686         };
12687         
12688         var header = {};
12689         
12690         if(!this.header){
12691             header = {
12692                 tag : 'table',
12693                 cls : 'fc-header',
12694                 style : 'width:100%',
12695                 cn : [
12696                     {
12697                         tag: 'tr',
12698                         cn : [
12699                             {
12700                                 tag : 'td',
12701                                 cls : 'fc-header-left',
12702                                 cn : [
12703                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
12704                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
12705                                     { tag: 'span', cls: 'fc-header-space' },
12706                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
12707
12708
12709                                 ]
12710                             },
12711
12712                             {
12713                                 tag : 'td',
12714                                 cls : 'fc-header-center',
12715                                 cn : [
12716                                     {
12717                                         tag: 'span',
12718                                         cls: 'fc-header-title',
12719                                         cn : {
12720                                             tag: 'H2',
12721                                             html : 'month / year'
12722                                         }
12723                                     }
12724
12725                                 ]
12726                             },
12727                             {
12728                                 tag : 'td',
12729                                 cls : 'fc-header-right',
12730                                 cn : [
12731                               /*      fc_button('month', 'left', '', 'month' ),
12732                                     fc_button('week', '', '', 'week' ),
12733                                     fc_button('day', 'right', '', 'day' )
12734                                 */    
12735
12736                                 ]
12737                             }
12738
12739                         ]
12740                     }
12741                 ]
12742             };
12743         }
12744         
12745         header = this.header;
12746         
12747        
12748         var cal_heads = function() {
12749             var ret = [];
12750             // fixme - handle this.
12751             
12752             for (var i =0; i < Date.dayNames.length; i++) {
12753                 var d = Date.dayNames[i];
12754                 ret.push({
12755                     tag: 'th',
12756                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
12757                     html : d.substring(0,3)
12758                 });
12759                 
12760             }
12761             ret[0].cls += ' fc-first';
12762             ret[6].cls += ' fc-last';
12763             return ret;
12764         };
12765         var cal_cell = function(n) {
12766             return  {
12767                 tag: 'td',
12768                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
12769                 cn : [
12770                     {
12771                         cn : [
12772                             {
12773                                 cls: 'fc-day-number',
12774                                 html: 'D'
12775                             },
12776                             {
12777                                 cls: 'fc-day-content',
12778                              
12779                                 cn : [
12780                                      {
12781                                         style: 'position: relative;' // height: 17px;
12782                                     }
12783                                 ]
12784                             }
12785                             
12786                             
12787                         ]
12788                     }
12789                 ]
12790                 
12791             }
12792         };
12793         var cal_rows = function() {
12794             
12795             var ret = []
12796             for (var r = 0; r < 6; r++) {
12797                 var row= {
12798                     tag : 'tr',
12799                     cls : 'fc-week',
12800                     cn : []
12801                 };
12802                 
12803                 for (var i =0; i < Date.dayNames.length; i++) {
12804                     var d = Date.dayNames[i];
12805                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
12806
12807                 }
12808                 row.cn[0].cls+=' fc-first';
12809                 row.cn[0].cn[0].style = 'min-height:90px';
12810                 row.cn[6].cls+=' fc-last';
12811                 ret.push(row);
12812                 
12813             }
12814             ret[0].cls += ' fc-first';
12815             ret[4].cls += ' fc-prev-last';
12816             ret[5].cls += ' fc-last';
12817             return ret;
12818             
12819         };
12820         
12821         var cal_table = {
12822             tag: 'table',
12823             cls: 'fc-border-separate',
12824             style : 'width:100%',
12825             cellspacing  : 0,
12826             cn : [
12827                 { 
12828                     tag: 'thead',
12829                     cn : [
12830                         { 
12831                             tag: 'tr',
12832                             cls : 'fc-first fc-last',
12833                             cn : cal_heads()
12834                         }
12835                     ]
12836                 },
12837                 { 
12838                     tag: 'tbody',
12839                     cn : cal_rows()
12840                 }
12841                   
12842             ]
12843         };
12844          
12845          var cfg = {
12846             cls : 'fc fc-ltr',
12847             cn : [
12848                 header,
12849                 {
12850                     cls : 'fc-content',
12851                     style : "position: relative;",
12852                     cn : [
12853                         {
12854                             cls : 'fc-view fc-view-month fc-grid',
12855                             style : 'position: relative',
12856                             unselectable : 'on',
12857                             cn : [
12858                                 {
12859                                     cls : 'fc-event-container',
12860                                     style : 'position:absolute;z-index:8;top:0;left:0;'
12861                                 },
12862                                 cal_table
12863                             ]
12864                         }
12865                     ]
12866     
12867                 }
12868            ] 
12869             
12870         };
12871         
12872          
12873         
12874         return cfg;
12875     },
12876     
12877     
12878     initEvents : function()
12879     {
12880         if(!this.store){
12881             throw "can not find store for calendar";
12882         }
12883         
12884         var mark = {
12885             tag: "div",
12886             cls:"x-dlg-mask",
12887             style: "text-align:center",
12888             cn: [
12889                 {
12890                     tag: "div",
12891                     style: "background-color:white;width:50%;margin:250 auto",
12892                     cn: [
12893                         {
12894                             tag: "img",
12895                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
12896                         },
12897                         {
12898                             tag: "span",
12899                             html: "Loading"
12900                         }
12901                         
12902                     ]
12903                 }
12904             ]
12905         }
12906         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
12907         
12908         var size = this.el.select('.fc-content', true).first().getSize();
12909         this.maskEl.setSize(size.width, size.height);
12910         this.maskEl.enableDisplayMode("block");
12911         if(!this.loadMask){
12912             this.maskEl.hide();
12913         }
12914         
12915         this.store = Roo.factory(this.store, Roo.data);
12916         this.store.on('load', this.onLoad, this);
12917         this.store.on('beforeload', this.onBeforeLoad, this);
12918         
12919         this.resize();
12920         
12921         this.cells = this.el.select('.fc-day',true);
12922         //Roo.log(this.cells);
12923         this.textNodes = this.el.query('.fc-day-number');
12924         this.cells.addClassOnOver('fc-state-hover');
12925         
12926         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
12927         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
12928         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
12929         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
12930         
12931         this.on('monthchange', this.onMonthChange, this);
12932         
12933         this.update(new Date().clearTime());
12934     },
12935     
12936     resize : function() {
12937         var sz  = this.el.getSize();
12938         
12939         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
12940         this.el.select('.fc-day-content div',true).setHeight(34);
12941     },
12942     
12943     
12944     // private
12945     showPrevMonth : function(e){
12946         this.update(this.activeDate.add("mo", -1));
12947     },
12948     showToday : function(e){
12949         this.update(new Date().clearTime());
12950     },
12951     // private
12952     showNextMonth : function(e){
12953         this.update(this.activeDate.add("mo", 1));
12954     },
12955
12956     // private
12957     showPrevYear : function(){
12958         this.update(this.activeDate.add("y", -1));
12959     },
12960
12961     // private
12962     showNextYear : function(){
12963         this.update(this.activeDate.add("y", 1));
12964     },
12965
12966     
12967    // private
12968     update : function(date)
12969     {
12970         var vd = this.activeDate;
12971         this.activeDate = date;
12972 //        if(vd && this.el){
12973 //            var t = date.getTime();
12974 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
12975 //                Roo.log('using add remove');
12976 //                
12977 //                this.fireEvent('monthchange', this, date);
12978 //                
12979 //                this.cells.removeClass("fc-state-highlight");
12980 //                this.cells.each(function(c){
12981 //                   if(c.dateValue == t){
12982 //                       c.addClass("fc-state-highlight");
12983 //                       setTimeout(function(){
12984 //                            try{c.dom.firstChild.focus();}catch(e){}
12985 //                       }, 50);
12986 //                       return false;
12987 //                   }
12988 //                   return true;
12989 //                });
12990 //                return;
12991 //            }
12992 //        }
12993         
12994         var days = date.getDaysInMonth();
12995         
12996         var firstOfMonth = date.getFirstDateOfMonth();
12997         var startingPos = firstOfMonth.getDay()-this.startDay;
12998         
12999         if(startingPos < this.startDay){
13000             startingPos += 7;
13001         }
13002         
13003         var pm = date.add(Date.MONTH, -1);
13004         var prevStart = pm.getDaysInMonth()-startingPos;
13005 //        
13006         this.cells = this.el.select('.fc-day',true);
13007         this.textNodes = this.el.query('.fc-day-number');
13008         this.cells.addClassOnOver('fc-state-hover');
13009         
13010         var cells = this.cells.elements;
13011         var textEls = this.textNodes;
13012         
13013         Roo.each(cells, function(cell){
13014             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
13015         });
13016         
13017         days += startingPos;
13018
13019         // convert everything to numbers so it's fast
13020         var day = 86400000;
13021         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
13022         //Roo.log(d);
13023         //Roo.log(pm);
13024         //Roo.log(prevStart);
13025         
13026         var today = new Date().clearTime().getTime();
13027         var sel = date.clearTime().getTime();
13028         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
13029         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
13030         var ddMatch = this.disabledDatesRE;
13031         var ddText = this.disabledDatesText;
13032         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
13033         var ddaysText = this.disabledDaysText;
13034         var format = this.format;
13035         
13036         var setCellClass = function(cal, cell){
13037             cell.row = 0;
13038             cell.events = [];
13039             cell.more = [];
13040             //Roo.log('set Cell Class');
13041             cell.title = "";
13042             var t = d.getTime();
13043             
13044             //Roo.log(d);
13045             
13046             cell.dateValue = t;
13047             if(t == today){
13048                 cell.className += " fc-today";
13049                 cell.className += " fc-state-highlight";
13050                 cell.title = cal.todayText;
13051             }
13052             if(t == sel){
13053                 // disable highlight in other month..
13054                 //cell.className += " fc-state-highlight";
13055                 
13056             }
13057             // disabling
13058             if(t < min) {
13059                 cell.className = " fc-state-disabled";
13060                 cell.title = cal.minText;
13061                 return;
13062             }
13063             if(t > max) {
13064                 cell.className = " fc-state-disabled";
13065                 cell.title = cal.maxText;
13066                 return;
13067             }
13068             if(ddays){
13069                 if(ddays.indexOf(d.getDay()) != -1){
13070                     cell.title = ddaysText;
13071                     cell.className = " fc-state-disabled";
13072                 }
13073             }
13074             if(ddMatch && format){
13075                 var fvalue = d.dateFormat(format);
13076                 if(ddMatch.test(fvalue)){
13077                     cell.title = ddText.replace("%0", fvalue);
13078                     cell.className = " fc-state-disabled";
13079                 }
13080             }
13081             
13082             if (!cell.initialClassName) {
13083                 cell.initialClassName = cell.dom.className;
13084             }
13085             
13086             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
13087         };
13088
13089         var i = 0;
13090         
13091         for(; i < startingPos; i++) {
13092             textEls[i].innerHTML = (++prevStart);
13093             d.setDate(d.getDate()+1);
13094             
13095             cells[i].className = "fc-past fc-other-month";
13096             setCellClass(this, cells[i]);
13097         }
13098         
13099         var intDay = 0;
13100         
13101         for(; i < days; i++){
13102             intDay = i - startingPos + 1;
13103             textEls[i].innerHTML = (intDay);
13104             d.setDate(d.getDate()+1);
13105             
13106             cells[i].className = ''; // "x-date-active";
13107             setCellClass(this, cells[i]);
13108         }
13109         var extraDays = 0;
13110         
13111         for(; i < 42; i++) {
13112             textEls[i].innerHTML = (++extraDays);
13113             d.setDate(d.getDate()+1);
13114             
13115             cells[i].className = "fc-future fc-other-month";
13116             setCellClass(this, cells[i]);
13117         }
13118         
13119         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
13120         
13121         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
13122         
13123         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
13124         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
13125         
13126         if(totalRows != 6){
13127             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
13128             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
13129         }
13130         
13131         this.fireEvent('monthchange', this, date);
13132         
13133         
13134         /*
13135         if(!this.internalRender){
13136             var main = this.el.dom.firstChild;
13137             var w = main.offsetWidth;
13138             this.el.setWidth(w + this.el.getBorderWidth("lr"));
13139             Roo.fly(main).setWidth(w);
13140             this.internalRender = true;
13141             // opera does not respect the auto grow header center column
13142             // then, after it gets a width opera refuses to recalculate
13143             // without a second pass
13144             if(Roo.isOpera && !this.secondPass){
13145                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
13146                 this.secondPass = true;
13147                 this.update.defer(10, this, [date]);
13148             }
13149         }
13150         */
13151         
13152     },
13153     
13154     findCell : function(dt) {
13155         dt = dt.clearTime().getTime();
13156         var ret = false;
13157         this.cells.each(function(c){
13158             //Roo.log("check " +c.dateValue + '?=' + dt);
13159             if(c.dateValue == dt){
13160                 ret = c;
13161                 return false;
13162             }
13163             return true;
13164         });
13165         
13166         return ret;
13167     },
13168     
13169     findCells : function(ev) {
13170         var s = ev.start.clone().clearTime().getTime();
13171        // Roo.log(s);
13172         var e= ev.end.clone().clearTime().getTime();
13173        // Roo.log(e);
13174         var ret = [];
13175         this.cells.each(function(c){
13176              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
13177             
13178             if(c.dateValue > e){
13179                 return ;
13180             }
13181             if(c.dateValue < s){
13182                 return ;
13183             }
13184             ret.push(c);
13185         });
13186         
13187         return ret;    
13188     },
13189     
13190 //    findBestRow: function(cells)
13191 //    {
13192 //        var ret = 0;
13193 //        
13194 //        for (var i =0 ; i < cells.length;i++) {
13195 //            ret  = Math.max(cells[i].rows || 0,ret);
13196 //        }
13197 //        return ret;
13198 //        
13199 //    },
13200     
13201     
13202     addItem : function(ev)
13203     {
13204         // look for vertical location slot in
13205         var cells = this.findCells(ev);
13206         
13207 //        ev.row = this.findBestRow(cells);
13208         
13209         // work out the location.
13210         
13211         var crow = false;
13212         var rows = [];
13213         for(var i =0; i < cells.length; i++) {
13214             
13215             cells[i].row = cells[0].row;
13216             
13217             if(i == 0){
13218                 cells[i].row = cells[i].row + 1;
13219             }
13220             
13221             if (!crow) {
13222                 crow = {
13223                     start : cells[i],
13224                     end :  cells[i]
13225                 };
13226                 continue;
13227             }
13228             if (crow.start.getY() == cells[i].getY()) {
13229                 // on same row.
13230                 crow.end = cells[i];
13231                 continue;
13232             }
13233             // different row.
13234             rows.push(crow);
13235             crow = {
13236                 start: cells[i],
13237                 end : cells[i]
13238             };
13239             
13240         }
13241         
13242         rows.push(crow);
13243         ev.els = [];
13244         ev.rows = rows;
13245         ev.cells = cells;
13246         
13247         cells[0].events.push(ev);
13248         
13249         this.calevents.push(ev);
13250     },
13251     
13252     clearEvents: function() {
13253         
13254         if(!this.calevents){
13255             return;
13256         }
13257         
13258         Roo.each(this.cells.elements, function(c){
13259             c.row = 0;
13260             c.events = [];
13261             c.more = [];
13262         });
13263         
13264         Roo.each(this.calevents, function(e) {
13265             Roo.each(e.els, function(el) {
13266                 el.un('mouseenter' ,this.onEventEnter, this);
13267                 el.un('mouseleave' ,this.onEventLeave, this);
13268                 el.remove();
13269             },this);
13270         },this);
13271         
13272         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
13273             e.remove();
13274         });
13275         
13276     },
13277     
13278     renderEvents: function()
13279     {   
13280         var _this = this;
13281         
13282         this.cells.each(function(c) {
13283             
13284             if(c.row < 5){
13285                 return;
13286             }
13287             
13288             var ev = c.events;
13289             
13290             var r = 4;
13291             if(c.row != c.events.length){
13292                 r = 4 - (4 - (c.row - c.events.length));
13293             }
13294             
13295             c.events = ev.slice(0, r);
13296             c.more = ev.slice(r);
13297             
13298             if(c.more.length && c.more.length == 1){
13299                 c.events.push(c.more.pop());
13300             }
13301             
13302             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
13303             
13304         });
13305             
13306         this.cells.each(function(c) {
13307             
13308             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
13309             
13310             
13311             for (var e = 0; e < c.events.length; e++){
13312                 var ev = c.events[e];
13313                 var rows = ev.rows;
13314                 
13315                 for(var i = 0; i < rows.length; i++) {
13316                 
13317                     // how many rows should it span..
13318
13319                     var  cfg = {
13320                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
13321                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
13322
13323                         unselectable : "on",
13324                         cn : [
13325                             {
13326                                 cls: 'fc-event-inner',
13327                                 cn : [
13328     //                                {
13329     //                                  tag:'span',
13330     //                                  cls: 'fc-event-time',
13331     //                                  html : cells.length > 1 ? '' : ev.time
13332     //                                },
13333                                     {
13334                                       tag:'span',
13335                                       cls: 'fc-event-title',
13336                                       html : String.format('{0}', ev.title)
13337                                     }
13338
13339
13340                                 ]
13341                             },
13342                             {
13343                                 cls: 'ui-resizable-handle ui-resizable-e',
13344                                 html : '&nbsp;&nbsp;&nbsp'
13345                             }
13346
13347                         ]
13348                     };
13349
13350                     if (i == 0) {
13351                         cfg.cls += ' fc-event-start';
13352                     }
13353                     if ((i+1) == rows.length) {
13354                         cfg.cls += ' fc-event-end';
13355                     }
13356
13357                     var ctr = _this.el.select('.fc-event-container',true).first();
13358                     var cg = ctr.createChild(cfg);
13359
13360                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
13361                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
13362
13363                     var r = (c.more.length) ? 1 : 0;
13364                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
13365                     cg.setWidth(ebox.right - sbox.x -2);
13366
13367                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
13368                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
13369                     cg.on('click', _this.onEventClick, _this, ev);
13370
13371                     ev.els.push(cg);
13372                     
13373                 }
13374                 
13375             }
13376             
13377             
13378             if(c.more.length){
13379                 var  cfg = {
13380                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
13381                     style : 'position: absolute',
13382                     unselectable : "on",
13383                     cn : [
13384                         {
13385                             cls: 'fc-event-inner',
13386                             cn : [
13387                                 {
13388                                   tag:'span',
13389                                   cls: 'fc-event-title',
13390                                   html : 'More'
13391                                 }
13392
13393
13394                             ]
13395                         },
13396                         {
13397                             cls: 'ui-resizable-handle ui-resizable-e',
13398                             html : '&nbsp;&nbsp;&nbsp'
13399                         }
13400
13401                     ]
13402                 };
13403
13404                 var ctr = _this.el.select('.fc-event-container',true).first();
13405                 var cg = ctr.createChild(cfg);
13406
13407                 var sbox = c.select('.fc-day-content',true).first().getBox();
13408                 var ebox = c.select('.fc-day-content',true).first().getBox();
13409                 //Roo.log(cg);
13410                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
13411                 cg.setWidth(ebox.right - sbox.x -2);
13412
13413                 cg.on('click', _this.onMoreEventClick, _this, c.more);
13414                 
13415             }
13416             
13417         });
13418         
13419         
13420         
13421     },
13422     
13423     onEventEnter: function (e, el,event,d) {
13424         this.fireEvent('evententer', this, el, event);
13425     },
13426     
13427     onEventLeave: function (e, el,event,d) {
13428         this.fireEvent('eventleave', this, el, event);
13429     },
13430     
13431     onEventClick: function (e, el,event,d) {
13432         this.fireEvent('eventclick', this, el, event);
13433     },
13434     
13435     onMonthChange: function () {
13436         this.store.load();
13437     },
13438     
13439     onMoreEventClick: function(e, el, more)
13440     {
13441         var _this = this;
13442         
13443         this.calpopover.placement = 'right';
13444         this.calpopover.setTitle('More');
13445         
13446         this.calpopover.setContent('');
13447         
13448         var ctr = this.calpopover.el.select('.popover-content', true).first();
13449         
13450         Roo.each(more, function(m){
13451             var cfg = {
13452                 cls : 'fc-event-hori fc-event-draggable',
13453                 html : m.title
13454             }
13455             var cg = ctr.createChild(cfg);
13456             
13457             cg.on('click', _this.onEventClick, _this, m);
13458         });
13459         
13460         this.calpopover.show(el);
13461         
13462         
13463     },
13464     
13465     onLoad: function () 
13466     {   
13467         this.calevents = [];
13468         var cal = this;
13469         
13470         if(this.store.getCount() > 0){
13471             this.store.data.each(function(d){
13472                cal.addItem({
13473                     id : d.data.id,
13474                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
13475                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
13476                     time : d.data.start_time,
13477                     title : d.data.title,
13478                     description : d.data.description,
13479                     venue : d.data.venue
13480                 });
13481             });
13482         }
13483         
13484         this.renderEvents();
13485         
13486         if(this.calevents.length && this.loadMask){
13487             this.maskEl.hide();
13488         }
13489     },
13490     
13491     onBeforeLoad: function()
13492     {
13493         this.clearEvents();
13494         if(this.loadMask){
13495             this.maskEl.show();
13496         }
13497     }
13498 });
13499
13500  
13501  /*
13502  * - LGPL
13503  *
13504  * element
13505  * 
13506  */
13507
13508 /**
13509  * @class Roo.bootstrap.Popover
13510  * @extends Roo.bootstrap.Component
13511  * Bootstrap Popover class
13512  * @cfg {String} html contents of the popover   (or false to use children..)
13513  * @cfg {String} title of popover (or false to hide)
13514  * @cfg {String} placement how it is placed
13515  * @cfg {String} trigger click || hover (or false to trigger manually)
13516  * @cfg {String} over what (parent or false to trigger manually.)
13517  * 
13518  * @constructor
13519  * Create a new Popover
13520  * @param {Object} config The config object
13521  */
13522
13523 Roo.bootstrap.Popover = function(config){
13524     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
13525 };
13526
13527 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
13528     
13529     title: 'Fill in a title',
13530     html: false,
13531     
13532     placement : 'right',
13533     trigger : 'hover', // hover
13534     
13535     over: 'parent',
13536     
13537     can_build_overlaid : false,
13538     
13539     getChildContainer : function()
13540     {
13541         return this.el.select('.popover-content',true).first();
13542     },
13543     
13544     getAutoCreate : function(){
13545          Roo.log('make popover?');
13546         var cfg = {
13547            cls : 'popover roo-dynamic',
13548            style: 'display:block',
13549            cn : [
13550                 {
13551                     cls : 'arrow'
13552                 },
13553                 {
13554                     cls : 'popover-inner',
13555                     cn : [
13556                         {
13557                             tag: 'h3',
13558                             cls: 'popover-title',
13559                             html : this.title
13560                         },
13561                         {
13562                             cls : 'popover-content',
13563                             html : this.html
13564                         }
13565                     ]
13566                     
13567                 }
13568            ]
13569         };
13570         
13571         return cfg;
13572     },
13573     setTitle: function(str)
13574     {
13575         this.el.select('.popover-title',true).first().dom.innerHTML = str;
13576     },
13577     setContent: function(str)
13578     {
13579         this.el.select('.popover-content',true).first().dom.innerHTML = str;
13580     },
13581     // as it get's added to the bottom of the page.
13582     onRender : function(ct, position)
13583     {
13584         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
13585         if(!this.el){
13586             var cfg = Roo.apply({},  this.getAutoCreate());
13587             cfg.id = Roo.id();
13588             
13589             if (this.cls) {
13590                 cfg.cls += ' ' + this.cls;
13591             }
13592             if (this.style) {
13593                 cfg.style = this.style;
13594             }
13595             Roo.log("adding to ")
13596             this.el = Roo.get(document.body).createChild(cfg, position);
13597             Roo.log(this.el);
13598         }
13599         this.initEvents();
13600     },
13601     
13602     initEvents : function()
13603     {
13604         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
13605         this.el.enableDisplayMode('block');
13606         this.el.hide();
13607         if (this.over === false) {
13608             return; 
13609         }
13610         if (this.triggers === false) {
13611             return;
13612         }
13613         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13614         var triggers = this.trigger ? this.trigger.split(' ') : [];
13615         Roo.each(triggers, function(trigger) {
13616         
13617             if (trigger == 'click') {
13618                 on_el.on('click', this.toggle, this);
13619             } else if (trigger != 'manual') {
13620                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
13621                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
13622       
13623                 on_el.on(eventIn  ,this.enter, this);
13624                 on_el.on(eventOut, this.leave, this);
13625             }
13626         }, this);
13627         
13628     },
13629     
13630     
13631     // private
13632     timeout : null,
13633     hoverState : null,
13634     
13635     toggle : function () {
13636         this.hoverState == 'in' ? this.leave() : this.enter();
13637     },
13638     
13639     enter : function () {
13640        
13641     
13642         clearTimeout(this.timeout);
13643     
13644         this.hoverState = 'in'
13645     
13646         if (!this.delay || !this.delay.show) {
13647             this.show();
13648             return 
13649         }
13650         var _t = this;
13651         this.timeout = setTimeout(function () {
13652             if (_t.hoverState == 'in') {
13653                 _t.show();
13654             }
13655         }, this.delay.show)
13656     },
13657     leave : function() {
13658         clearTimeout(this.timeout);
13659     
13660         this.hoverState = 'out'
13661     
13662         if (!this.delay || !this.delay.hide) {
13663             this.hide();
13664             return 
13665         }
13666         var _t = this;
13667         this.timeout = setTimeout(function () {
13668             if (_t.hoverState == 'out') {
13669                 _t.hide();
13670             }
13671         }, this.delay.hide)
13672     },
13673     
13674     show : function (on_el)
13675     {
13676         if (!on_el) {
13677             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13678         }
13679         // set content.
13680         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
13681         if (this.html !== false) {
13682             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
13683         }
13684         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
13685         if (!this.title.length) {
13686             this.el.select('.popover-title',true).hide();
13687         }
13688         
13689         var placement = typeof this.placement == 'function' ?
13690             this.placement.call(this, this.el, on_el) :
13691             this.placement;
13692             
13693         var autoToken = /\s?auto?\s?/i;
13694         var autoPlace = autoToken.test(placement);
13695         if (autoPlace) {
13696             placement = placement.replace(autoToken, '') || 'top';
13697         }
13698         
13699         //this.el.detach()
13700         //this.el.setXY([0,0]);
13701         this.el.show();
13702         this.el.dom.style.display='block';
13703         this.el.addClass(placement);
13704         
13705         //this.el.appendTo(on_el);
13706         
13707         var p = this.getPosition();
13708         var box = this.el.getBox();
13709         
13710         if (autoPlace) {
13711             // fixme..
13712         }
13713         var align = Roo.bootstrap.Popover.alignment[placement]
13714         this.el.alignTo(on_el, align[0],align[1]);
13715         //var arrow = this.el.select('.arrow',true).first();
13716         //arrow.set(align[2], 
13717         
13718         this.el.addClass('in');
13719         this.hoverState = null;
13720         
13721         if (this.el.hasClass('fade')) {
13722             // fade it?
13723         }
13724         
13725     },
13726     hide : function()
13727     {
13728         this.el.setXY([0,0]);
13729         this.el.removeClass('in');
13730         this.el.hide();
13731         
13732     }
13733     
13734 });
13735
13736 Roo.bootstrap.Popover.alignment = {
13737     'left' : ['r-l', [-10,0], 'right'],
13738     'right' : ['l-r', [10,0], 'left'],
13739     'bottom' : ['t-b', [0,10], 'top'],
13740     'top' : [ 'b-t', [0,-10], 'bottom']
13741 };
13742
13743  /*
13744  * - LGPL
13745  *
13746  * Progress
13747  * 
13748  */
13749
13750 /**
13751  * @class Roo.bootstrap.Progress
13752  * @extends Roo.bootstrap.Component
13753  * Bootstrap Progress class
13754  * @cfg {Boolean} striped striped of the progress bar
13755  * @cfg {Boolean} active animated of the progress bar
13756  * 
13757  * 
13758  * @constructor
13759  * Create a new Progress
13760  * @param {Object} config The config object
13761  */
13762
13763 Roo.bootstrap.Progress = function(config){
13764     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
13765 };
13766
13767 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
13768     
13769     striped : false,
13770     active: false,
13771     
13772     getAutoCreate : function(){
13773         var cfg = {
13774             tag: 'div',
13775             cls: 'progress'
13776         };
13777         
13778         
13779         if(this.striped){
13780             cfg.cls += ' progress-striped';
13781         }
13782       
13783         if(this.active){
13784             cfg.cls += ' active';
13785         }
13786         
13787         
13788         return cfg;
13789     }
13790    
13791 });
13792
13793  
13794
13795  /*
13796  * - LGPL
13797  *
13798  * ProgressBar
13799  * 
13800  */
13801
13802 /**
13803  * @class Roo.bootstrap.ProgressBar
13804  * @extends Roo.bootstrap.Component
13805  * Bootstrap ProgressBar class
13806  * @cfg {Number} aria_valuenow aria-value now
13807  * @cfg {Number} aria_valuemin aria-value min
13808  * @cfg {Number} aria_valuemax aria-value max
13809  * @cfg {String} label label for the progress bar
13810  * @cfg {String} panel (success | info | warning | danger )
13811  * @cfg {String} role role of the progress bar
13812  * @cfg {String} sr_only text
13813  * 
13814  * 
13815  * @constructor
13816  * Create a new ProgressBar
13817  * @param {Object} config The config object
13818  */
13819
13820 Roo.bootstrap.ProgressBar = function(config){
13821     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
13822 };
13823
13824 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
13825     
13826     aria_valuenow : 0,
13827     aria_valuemin : 0,
13828     aria_valuemax : 100,
13829     label : false,
13830     panel : false,
13831     role : false,
13832     sr_only: false,
13833     
13834     getAutoCreate : function()
13835     {
13836         
13837         var cfg = {
13838             tag: 'div',
13839             cls: 'progress-bar',
13840             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
13841         };
13842         
13843         if(this.sr_only){
13844             cfg.cn = {
13845                 tag: 'span',
13846                 cls: 'sr-only',
13847                 html: this.sr_only
13848             }
13849         }
13850         
13851         if(this.role){
13852             cfg.role = this.role;
13853         }
13854         
13855         if(this.aria_valuenow){
13856             cfg['aria-valuenow'] = this.aria_valuenow;
13857         }
13858         
13859         if(this.aria_valuemin){
13860             cfg['aria-valuemin'] = this.aria_valuemin;
13861         }
13862         
13863         if(this.aria_valuemax){
13864             cfg['aria-valuemax'] = this.aria_valuemax;
13865         }
13866         
13867         if(this.label && !this.sr_only){
13868             cfg.html = this.label;
13869         }
13870         
13871         if(this.panel){
13872             cfg.cls += ' progress-bar-' + this.panel;
13873         }
13874         
13875         return cfg;
13876     },
13877     
13878     update : function(aria_valuenow)
13879     {
13880         this.aria_valuenow = aria_valuenow;
13881         
13882         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
13883     }
13884    
13885 });
13886
13887  
13888
13889  /*
13890  * - LGPL
13891  *
13892  * column
13893  * 
13894  */
13895
13896 /**
13897  * @class Roo.bootstrap.TabGroup
13898  * @extends Roo.bootstrap.Column
13899  * Bootstrap Column class
13900  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
13901  * @cfg {Boolean} carousel true to make the group behave like a carousel
13902  * 
13903  * @constructor
13904  * Create a new TabGroup
13905  * @param {Object} config The config object
13906  */
13907
13908 Roo.bootstrap.TabGroup = function(config){
13909     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
13910     if (!this.navId) {
13911         this.navId = Roo.id();
13912     }
13913     this.tabs = [];
13914     Roo.bootstrap.TabGroup.register(this);
13915     
13916 };
13917
13918 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
13919     
13920     carousel : false,
13921     transition : false,
13922      
13923     getAutoCreate : function()
13924     {
13925         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
13926         
13927         cfg.cls += ' tab-content';
13928         
13929         if (this.carousel) {
13930             cfg.cls += ' carousel slide';
13931             cfg.cn = [{
13932                cls : 'carousel-inner'
13933             }]
13934         }
13935         
13936         
13937         return cfg;
13938     },
13939     getChildContainer : function()
13940     {
13941         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
13942     },
13943     
13944     /**
13945     * register a Navigation item
13946     * @param {Roo.bootstrap.NavItem} the navitem to add
13947     */
13948     register : function(item)
13949     {
13950         this.tabs.push( item);
13951         item.navId = this.navId; // not really needed..
13952     
13953     },
13954     
13955     getActivePanel : function()
13956     {
13957         var r = false;
13958         Roo.each(this.tabs, function(t) {
13959             if (t.active) {
13960                 r = t;
13961                 return false;
13962             }
13963             return null;
13964         });
13965         return r;
13966         
13967     },
13968     getPanelByName : function(n)
13969     {
13970         var r = false;
13971         Roo.each(this.tabs, function(t) {
13972             if (t.tabId == n) {
13973                 r = t;
13974                 return false;
13975             }
13976             return null;
13977         });
13978         return r;
13979     },
13980     indexOfPanel : function(p)
13981     {
13982         var r = false;
13983         Roo.each(this.tabs, function(t,i) {
13984             if (t.tabId == p.tabId) {
13985                 r = i;
13986                 return false;
13987             }
13988             return null;
13989         });
13990         return r;
13991     },
13992     /**
13993      * show a specific panel
13994      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
13995      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
13996      */
13997     showPanel : function (pan)
13998     {
13999         
14000         if (typeof(pan) == 'number') {
14001             pan = this.tabs[pan];
14002         }
14003         if (typeof(pan) == 'string') {
14004             pan = this.getPanelByName(pan);
14005         }
14006         if (pan.tabId == this.getActivePanel().tabId) {
14007             return true;
14008         }
14009         var cur = this.getActivePanel();
14010         
14011         if (false === cur.fireEvent('beforedeactivate')) {
14012             return false;
14013         }
14014         
14015         if (this.carousel) {
14016             this.transition = true;
14017             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
14018             var lr = dir == 'next' ? 'left' : 'right';
14019             pan.el.addClass(dir); // or prev
14020             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
14021             cur.el.addClass(lr); // or right
14022             pan.el.addClass(lr);
14023             
14024             var _this = this;
14025             cur.el.on('transitionend', function() {
14026                 Roo.log("trans end?");
14027                 
14028                 pan.el.removeClass([lr,dir]);
14029                 pan.setActive(true);
14030                 
14031                 cur.el.removeClass([lr]);
14032                 cur.setActive(false);
14033                 
14034                 _this.transition = false;
14035                 
14036             }, this, { single:  true } );
14037             return true;
14038         }
14039         
14040         cur.setActive(false);
14041         pan.setActive(true);
14042         return true;
14043         
14044     },
14045     showPanelNext : function()
14046     {
14047         var i = this.indexOfPanel(this.getActivePanel());
14048         if (i > this.tabs.length) {
14049             return;
14050         }
14051         this.showPanel(this.tabs[i+1]);
14052     },
14053     showPanelPrev : function()
14054     {
14055         var i = this.indexOfPanel(this.getActivePanel());
14056         if (i  < 1) {
14057             return;
14058         }
14059         this.showPanel(this.tabs[i-1]);
14060     }
14061     
14062     
14063   
14064 });
14065
14066  
14067
14068  
14069  
14070 Roo.apply(Roo.bootstrap.TabGroup, {
14071     
14072     groups: {},
14073      /**
14074     * register a Navigation Group
14075     * @param {Roo.bootstrap.NavGroup} the navgroup to add
14076     */
14077     register : function(navgrp)
14078     {
14079         this.groups[navgrp.navId] = navgrp;
14080         
14081     },
14082     /**
14083     * fetch a Navigation Group based on the navigation ID
14084     * if one does not exist , it will get created.
14085     * @param {string} the navgroup to add
14086     * @returns {Roo.bootstrap.NavGroup} the navgroup 
14087     */
14088     get: function(navId) {
14089         if (typeof(this.groups[navId]) == 'undefined') {
14090             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
14091         }
14092         return this.groups[navId] ;
14093     }
14094     
14095     
14096     
14097 });
14098
14099  /*
14100  * - LGPL
14101  *
14102  * TabPanel
14103  * 
14104  */
14105
14106 /**
14107  * @class Roo.bootstrap.TabPanel
14108  * @extends Roo.bootstrap.Component
14109  * Bootstrap TabPanel class
14110  * @cfg {Boolean} active panel active
14111  * @cfg {String} html panel content
14112  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
14113  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
14114  * 
14115  * 
14116  * @constructor
14117  * Create a new TabPanel
14118  * @param {Object} config The config object
14119  */
14120
14121 Roo.bootstrap.TabPanel = function(config){
14122     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
14123     this.addEvents({
14124         /**
14125              * @event changed
14126              * Fires when the active status changes
14127              * @param {Roo.bootstrap.TabPanel} this
14128              * @param {Boolean} state the new state
14129             
14130          */
14131         'changed': true,
14132         /**
14133              * @event beforedeactivate
14134              * Fires before a tab is de-activated - can be used to do validation on a form.
14135              * @param {Roo.bootstrap.TabPanel} this
14136              * @return {Boolean} false if there is an error
14137             
14138          */
14139         'beforedeactivate': true
14140      });
14141     
14142     this.tabId = this.tabId || Roo.id();
14143   
14144 };
14145
14146 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
14147     
14148     active: false,
14149     html: false,
14150     tabId: false,
14151     navId : false,
14152     
14153     getAutoCreate : function(){
14154         var cfg = {
14155             tag: 'div',
14156             // item is needed for carousel - not sure if it has any effect otherwise
14157             cls: 'tab-pane item',
14158             html: this.html || ''
14159         };
14160         
14161         if(this.active){
14162             cfg.cls += ' active';
14163         }
14164         
14165         if(this.tabId){
14166             cfg.tabId = this.tabId;
14167         }
14168         
14169         
14170         return cfg;
14171     },
14172     
14173     initEvents:  function()
14174     {
14175         Roo.log('-------- init events on tab panel ---------');
14176         
14177         var p = this.parent();
14178         this.navId = this.navId || p.navId;
14179         
14180         if (typeof(this.navId) != 'undefined') {
14181             // not really needed.. but just in case.. parent should be a NavGroup.
14182             var tg = Roo.bootstrap.TabGroup.get(this.navId);
14183             Roo.log(['register', tg, this]);
14184             tg.register(this);
14185         }
14186     },
14187     
14188     
14189     onRender : function(ct, position)
14190     {
14191        // Roo.log("Call onRender: " + this.xtype);
14192         
14193         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
14194         
14195         
14196         
14197         
14198         
14199     },
14200     
14201     setActive: function(state)
14202     {
14203         Roo.log("panel - set active " + this.tabId + "=" + state);
14204         
14205         this.active = state;
14206         if (!state) {
14207             this.el.removeClass('active');
14208             
14209         } else  if (!this.el.hasClass('active')) {
14210             this.el.addClass('active');
14211         }
14212         this.fireEvent('changed', this, state);
14213     }
14214     
14215     
14216 });
14217  
14218
14219  
14220
14221  /*
14222  * - LGPL
14223  *
14224  * DateField
14225  * 
14226  */
14227
14228 /**
14229  * @class Roo.bootstrap.DateField
14230  * @extends Roo.bootstrap.Input
14231  * Bootstrap DateField class
14232  * @cfg {Number} weekStart default 0
14233  * @cfg {Number} weekStart default 0
14234  * @cfg {Number} viewMode default empty, (months|years)
14235  * @cfg {Number} minViewMode default empty, (months|years)
14236  * @cfg {Number} startDate default -Infinity
14237  * @cfg {Number} endDate default Infinity
14238  * @cfg {Boolean} todayHighlight default false
14239  * @cfg {Boolean} todayBtn default false
14240  * @cfg {Boolean} calendarWeeks default false
14241  * @cfg {Object} daysOfWeekDisabled default empty
14242  * 
14243  * @cfg {Boolean} keyboardNavigation default true
14244  * @cfg {String} language default en
14245  * 
14246  * @constructor
14247  * Create a new DateField
14248  * @param {Object} config The config object
14249  */
14250
14251 Roo.bootstrap.DateField = function(config){
14252     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
14253      this.addEvents({
14254             /**
14255              * @event show
14256              * Fires when this field show.
14257              * @param {Roo.bootstrap.DateField} this
14258              * @param {Mixed} date The date value
14259              */
14260             show : true,
14261             /**
14262              * @event show
14263              * Fires when this field hide.
14264              * @param {Roo.bootstrap.DateField} this
14265              * @param {Mixed} date The date value
14266              */
14267             hide : true,
14268             /**
14269              * @event select
14270              * Fires when select a date.
14271              * @param {Roo.bootstrap.DateField} this
14272              * @param {Mixed} date The date value
14273              */
14274             select : true
14275         });
14276 };
14277
14278 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
14279     
14280     /**
14281      * @cfg {String} format
14282      * The default date format string which can be overriden for localization support.  The format must be
14283      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
14284      */
14285     format : "m/d/y",
14286     /**
14287      * @cfg {String} altFormats
14288      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
14289      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
14290      */
14291     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
14292     
14293     weekStart : 0,
14294     
14295     viewMode : '',
14296     
14297     minViewMode : '',
14298     
14299     todayHighlight : false,
14300     
14301     todayBtn: false,
14302     
14303     language: 'en',
14304     
14305     keyboardNavigation: true,
14306     
14307     calendarWeeks: false,
14308     
14309     startDate: -Infinity,
14310     
14311     endDate: Infinity,
14312     
14313     daysOfWeekDisabled: [],
14314     
14315     _events: [],
14316     
14317     UTCDate: function()
14318     {
14319         return new Date(Date.UTC.apply(Date, arguments));
14320     },
14321     
14322     UTCToday: function()
14323     {
14324         var today = new Date();
14325         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
14326     },
14327     
14328     getDate: function() {
14329             var d = this.getUTCDate();
14330             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
14331     },
14332     
14333     getUTCDate: function() {
14334             return this.date;
14335     },
14336     
14337     setDate: function(d) {
14338             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
14339     },
14340     
14341     setUTCDate: function(d) {
14342             this.date = d;
14343             this.setValue(this.formatDate(this.date));
14344     },
14345         
14346     onRender: function(ct, position)
14347     {
14348         
14349         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
14350         
14351         this.language = this.language || 'en';
14352         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
14353         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
14354         
14355         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
14356         this.format = this.format || 'm/d/y';
14357         this.isInline = false;
14358         this.isInput = true;
14359         this.component = this.el.select('.add-on', true).first() || false;
14360         this.component = (this.component && this.component.length === 0) ? false : this.component;
14361         this.hasInput = this.component && this.inputEL().length;
14362         
14363         if (typeof(this.minViewMode === 'string')) {
14364             switch (this.minViewMode) {
14365                 case 'months':
14366                     this.minViewMode = 1;
14367                     break;
14368                 case 'years':
14369                     this.minViewMode = 2;
14370                     break;
14371                 default:
14372                     this.minViewMode = 0;
14373                     break;
14374             }
14375         }
14376         
14377         if (typeof(this.viewMode === 'string')) {
14378             switch (this.viewMode) {
14379                 case 'months':
14380                     this.viewMode = 1;
14381                     break;
14382                 case 'years':
14383                     this.viewMode = 2;
14384                     break;
14385                 default:
14386                     this.viewMode = 0;
14387                     break;
14388             }
14389         }
14390                 
14391         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
14392         
14393 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
14394         
14395         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14396         
14397         this.picker().on('mousedown', this.onMousedown, this);
14398         this.picker().on('click', this.onClick, this);
14399         
14400         this.picker().addClass('datepicker-dropdown');
14401         
14402         this.startViewMode = this.viewMode;
14403         
14404         
14405         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
14406             if(!this.calendarWeeks){
14407                 v.remove();
14408                 return;
14409             };
14410             
14411             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
14412             v.attr('colspan', function(i, val){
14413                 return parseInt(val) + 1;
14414             });
14415         })
14416                         
14417         
14418         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
14419         
14420         this.setStartDate(this.startDate);
14421         this.setEndDate(this.endDate);
14422         
14423         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
14424         
14425         this.fillDow();
14426         this.fillMonths();
14427         this.update();
14428         this.showMode();
14429         
14430         if(this.isInline) {
14431             this.show();
14432         }
14433     },
14434     
14435     picker : function()
14436     {
14437         return this.pickerEl;
14438 //        return this.el.select('.datepicker', true).first();
14439     },
14440     
14441     fillDow: function()
14442     {
14443         var dowCnt = this.weekStart;
14444         
14445         var dow = {
14446             tag: 'tr',
14447             cn: [
14448                 
14449             ]
14450         };
14451         
14452         if(this.calendarWeeks){
14453             dow.cn.push({
14454                 tag: 'th',
14455                 cls: 'cw',
14456                 html: '&nbsp;'
14457             })
14458         }
14459         
14460         while (dowCnt < this.weekStart + 7) {
14461             dow.cn.push({
14462                 tag: 'th',
14463                 cls: 'dow',
14464                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
14465             });
14466         }
14467         
14468         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
14469     },
14470     
14471     fillMonths: function()
14472     {    
14473         var i = 0
14474         var months = this.picker().select('>.datepicker-months td', true).first();
14475         
14476         months.dom.innerHTML = '';
14477         
14478         while (i < 12) {
14479             var month = {
14480                 tag: 'span',
14481                 cls: 'month',
14482                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
14483             }
14484             
14485             months.createChild(month);
14486         }
14487         
14488     },
14489     
14490     update: function()
14491     {
14492         
14493         this.date = (typeof(this.date) === 'undefined' || !this.date.length) ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
14494         
14495         if (this.date < this.startDate) {
14496             this.viewDate = new Date(this.startDate);
14497         } else if (this.date > this.endDate) {
14498             this.viewDate = new Date(this.endDate);
14499         } else {
14500             this.viewDate = new Date(this.date);
14501         }
14502         
14503         this.fill();
14504     },
14505     
14506     fill: function() 
14507     {
14508         var d = new Date(this.viewDate),
14509                 year = d.getUTCFullYear(),
14510                 month = d.getUTCMonth(),
14511                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
14512                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
14513                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
14514                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
14515                 currentDate = this.date && this.date.valueOf(),
14516                 today = this.UTCToday();
14517         
14518         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
14519         
14520 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
14521         
14522 //        this.picker.select('>tfoot th.today').
14523 //                                              .text(dates[this.language].today)
14524 //                                              .toggle(this.todayBtn !== false);
14525     
14526         this.updateNavArrows();
14527         this.fillMonths();
14528                                                 
14529         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
14530         
14531         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
14532          
14533         prevMonth.setUTCDate(day);
14534         
14535         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
14536         
14537         var nextMonth = new Date(prevMonth);
14538         
14539         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
14540         
14541         nextMonth = nextMonth.valueOf();
14542         
14543         var fillMonths = false;
14544         
14545         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
14546         
14547         while(prevMonth.valueOf() < nextMonth) {
14548             var clsName = '';
14549             
14550             if (prevMonth.getUTCDay() === this.weekStart) {
14551                 if(fillMonths){
14552                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
14553                 }
14554                     
14555                 fillMonths = {
14556                     tag: 'tr',
14557                     cn: []
14558                 };
14559                 
14560                 if(this.calendarWeeks){
14561                     // ISO 8601: First week contains first thursday.
14562                     // ISO also states week starts on Monday, but we can be more abstract here.
14563                     var
14564                     // Start of current week: based on weekstart/current date
14565                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
14566                     // Thursday of this week
14567                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
14568                     // First Thursday of year, year from thursday
14569                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
14570                     // Calendar week: ms between thursdays, div ms per day, div 7 days
14571                     calWeek =  (th - yth) / 864e5 / 7 + 1;
14572                     
14573                     fillMonths.cn.push({
14574                         tag: 'td',
14575                         cls: 'cw',
14576                         html: calWeek
14577                     });
14578                 }
14579             }
14580             
14581             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
14582                 clsName += ' old';
14583             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
14584                 clsName += ' new';
14585             }
14586             if (this.todayHighlight &&
14587                 prevMonth.getUTCFullYear() == today.getFullYear() &&
14588                 prevMonth.getUTCMonth() == today.getMonth() &&
14589                 prevMonth.getUTCDate() == today.getDate()) {
14590                 clsName += ' today';
14591             }
14592             
14593             if (currentDate && prevMonth.valueOf() === currentDate) {
14594                 clsName += ' active';
14595             }
14596             
14597             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
14598                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
14599                     clsName += ' disabled';
14600             }
14601             
14602             fillMonths.cn.push({
14603                 tag: 'td',
14604                 cls: 'day ' + clsName,
14605                 html: prevMonth.getDate()
14606             })
14607             
14608             prevMonth.setDate(prevMonth.getDate()+1);
14609         }
14610           
14611         var currentYear = this.date && this.date.getUTCFullYear();
14612         var currentMonth = this.date && this.date.getUTCMonth();
14613         
14614         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
14615         
14616         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
14617             v.removeClass('active');
14618             
14619             if(currentYear === year && k === currentMonth){
14620                 v.addClass('active');
14621             }
14622             
14623             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
14624                 v.addClass('disabled');
14625             }
14626             
14627         });
14628         
14629         
14630         year = parseInt(year/10, 10) * 10;
14631         
14632         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
14633         
14634         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
14635         
14636         year -= 1;
14637         for (var i = -1; i < 11; i++) {
14638             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
14639                 tag: 'span',
14640                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
14641                 html: year
14642             })
14643             
14644             year += 1;
14645         }
14646     },
14647     
14648     showMode: function(dir) 
14649     {
14650         if (dir) {
14651             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
14652         }
14653         Roo.each(this.picker().select('>div',true).elements, function(v){
14654             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14655             v.hide();
14656         });
14657         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
14658     },
14659     
14660     place: function()
14661     {
14662         if(this.isInline) return;
14663         
14664         this.picker().removeClass(['bottom', 'top']);
14665         
14666         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
14667             /*
14668              * place to the top of element!
14669              *
14670              */
14671             
14672             this.picker().addClass('top');
14673             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
14674             
14675             return;
14676         }
14677         
14678         this.picker().addClass('bottom');
14679         
14680         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
14681     },
14682     
14683     parseDate : function(value)
14684     {
14685         if(!value || value instanceof Date){
14686             return value;
14687         }
14688         var v = Date.parseDate(value, this.format);
14689         if (!v && this.useIso) {
14690             v = Date.parseDate(value, 'Y-m-d');
14691         }
14692         if(!v && this.altFormats){
14693             if(!this.altFormatsArray){
14694                 this.altFormatsArray = this.altFormats.split("|");
14695             }
14696             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
14697                 v = Date.parseDate(value, this.altFormatsArray[i]);
14698             }
14699         }
14700         return v;
14701     },
14702     
14703     formatDate : function(date, fmt)
14704     {
14705         return (!date || !(date instanceof Date)) ?
14706         date : date.dateFormat(fmt || this.format);
14707     },
14708     
14709     onFocus : function()
14710     {
14711         Roo.bootstrap.DateField.superclass.onFocus.call(this);
14712         this.show();
14713     },
14714     
14715     onBlur : function()
14716     {
14717         Roo.bootstrap.DateField.superclass.onBlur.call(this);
14718         
14719         var d = this.inputEl().getValue();
14720         
14721         this.setValue(d);
14722                 
14723         this.hide();
14724     },
14725     
14726     show : function()
14727     {
14728         this.picker().show();
14729         this.update();
14730         this.place();
14731         
14732         this.fireEvent('show', this, this.date);
14733     },
14734     
14735     hide : function()
14736     {
14737         if(this.isInline) return;
14738         this.picker().hide();
14739         this.viewMode = this.startViewMode;
14740         this.showMode();
14741         
14742         this.fireEvent('hide', this, this.date);
14743         
14744     },
14745     
14746     onMousedown: function(e)
14747     {
14748         e.stopPropagation();
14749         e.preventDefault();
14750     },
14751     
14752     keyup: function(e)
14753     {
14754         Roo.bootstrap.DateField.superclass.keyup.call(this);
14755         this.update();
14756     },
14757
14758     setValue: function(v)
14759     {
14760         var d = new Date(v);
14761         
14762         if(isNaN(d.getTime())){
14763             this.date = '';
14764             return;
14765         }
14766         
14767         v = this.formatDate(d);
14768         
14769         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
14770         
14771         this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
14772
14773         this.update();
14774
14775         this.fireEvent('select', this, this.date);
14776         
14777     },
14778     
14779     getValue: function()
14780     {
14781         return this.formatDate(this.date);
14782     },
14783     
14784     fireKey: function(e)
14785     {
14786         if (!this.picker().isVisible()){
14787             if (e.keyCode == 27) // allow escape to hide and re-show picker
14788                 this.show();
14789             return;
14790         }
14791         
14792         var dateChanged = false,
14793         dir, day, month,
14794         newDate, newViewDate;
14795         
14796         switch(e.keyCode){
14797             case 27: // escape
14798                 this.hide();
14799                 e.preventDefault();
14800                 break;
14801             case 37: // left
14802             case 39: // right
14803                 if (!this.keyboardNavigation) break;
14804                 dir = e.keyCode == 37 ? -1 : 1;
14805                 
14806                 if (e.ctrlKey){
14807                     newDate = this.moveYear(this.date, dir);
14808                     newViewDate = this.moveYear(this.viewDate, dir);
14809                 } else if (e.shiftKey){
14810                     newDate = this.moveMonth(this.date, dir);
14811                     newViewDate = this.moveMonth(this.viewDate, dir);
14812                 } else {
14813                     newDate = new Date(this.date);
14814                     newDate.setUTCDate(this.date.getUTCDate() + dir);
14815                     newViewDate = new Date(this.viewDate);
14816                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
14817                 }
14818                 if (this.dateWithinRange(newDate)){
14819                     this.date = newDate;
14820                     this.viewDate = newViewDate;
14821                     this.setValue(this.formatDate(this.date));
14822 //                    this.update();
14823                     e.preventDefault();
14824                     dateChanged = true;
14825                 }
14826                 break;
14827             case 38: // up
14828             case 40: // down
14829                 if (!this.keyboardNavigation) break;
14830                 dir = e.keyCode == 38 ? -1 : 1;
14831                 if (e.ctrlKey){
14832                     newDate = this.moveYear(this.date, dir);
14833                     newViewDate = this.moveYear(this.viewDate, dir);
14834                 } else if (e.shiftKey){
14835                     newDate = this.moveMonth(this.date, dir);
14836                     newViewDate = this.moveMonth(this.viewDate, dir);
14837                 } else {
14838                     newDate = new Date(this.date);
14839                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
14840                     newViewDate = new Date(this.viewDate);
14841                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
14842                 }
14843                 if (this.dateWithinRange(newDate)){
14844                     this.date = newDate;
14845                     this.viewDate = newViewDate;
14846                     this.setValue(this.formatDate(this.date));
14847 //                    this.update();
14848                     e.preventDefault();
14849                     dateChanged = true;
14850                 }
14851                 break;
14852             case 13: // enter
14853                 this.setValue(this.formatDate(this.date));
14854                 this.hide();
14855                 e.preventDefault();
14856                 break;
14857             case 9: // tab
14858                 this.setValue(this.formatDate(this.date));
14859                 this.hide();
14860                 break;
14861             case 16: // shift
14862             case 17: // ctrl
14863             case 18: // alt
14864                 break;
14865             default :
14866                 this.hide();
14867                 
14868         }
14869     },
14870     
14871     
14872     onClick: function(e) 
14873     {
14874         e.stopPropagation();
14875         e.preventDefault();
14876         
14877         var target = e.getTarget();
14878         
14879         if(target.nodeName.toLowerCase() === 'i'){
14880             target = Roo.get(target).dom.parentNode;
14881         }
14882         
14883         var nodeName = target.nodeName;
14884         var className = target.className;
14885         var html = target.innerHTML;
14886         
14887         switch(nodeName.toLowerCase()) {
14888             case 'th':
14889                 switch(className) {
14890                     case 'switch':
14891                         this.showMode(1);
14892                         break;
14893                     case 'prev':
14894                     case 'next':
14895                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
14896                         switch(this.viewMode){
14897                                 case 0:
14898                                         this.viewDate = this.moveMonth(this.viewDate, dir);
14899                                         break;
14900                                 case 1:
14901                                 case 2:
14902                                         this.viewDate = this.moveYear(this.viewDate, dir);
14903                                         break;
14904                         }
14905                         this.fill();
14906                         break;
14907                     case 'today':
14908                         var date = new Date();
14909                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
14910 //                        this.fill()
14911                         this.setValue(this.formatDate(this.date));
14912                         
14913                         this.hide();
14914                         break;
14915                 }
14916                 break;
14917             case 'span':
14918                 if (className.indexOf('disabled') === -1) {
14919                     this.viewDate.setUTCDate(1);
14920                     if (className.indexOf('month') !== -1) {
14921                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
14922                     } else {
14923                         var year = parseInt(html, 10) || 0;
14924                         this.viewDate.setUTCFullYear(year);
14925                         
14926                     }
14927                     this.showMode(-1);
14928                     this.fill();
14929                 }
14930                 break;
14931                 
14932             case 'td':
14933                 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
14934                     var day = parseInt(html, 10) || 1;
14935                     var year = this.viewDate.getUTCFullYear(),
14936                         month = this.viewDate.getUTCMonth();
14937
14938                     if (className.indexOf('old') !== -1) {
14939                         if(month === 0 ){
14940                             month = 11;
14941                             year -= 1;
14942                         }else{
14943                             month -= 1;
14944                         }
14945                     } else if (className.indexOf('new') !== -1) {
14946                         if (month == 11) {
14947                             month = 0;
14948                             year += 1;
14949                         } else {
14950                             month += 1;
14951                         }
14952                     }
14953                     this.date = this.UTCDate(year, month, day,0,0,0,0);
14954                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
14955 //                    this.fill();
14956                     this.setValue(this.formatDate(this.date));
14957                     this.hide();
14958                 }
14959                 break;
14960         }
14961     },
14962     
14963     setStartDate: function(startDate)
14964     {
14965         this.startDate = startDate || -Infinity;
14966         if (this.startDate !== -Infinity) {
14967             this.startDate = this.parseDate(this.startDate);
14968         }
14969         this.update();
14970         this.updateNavArrows();
14971     },
14972
14973     setEndDate: function(endDate)
14974     {
14975         this.endDate = endDate || Infinity;
14976         if (this.endDate !== Infinity) {
14977             this.endDate = this.parseDate(this.endDate);
14978         }
14979         this.update();
14980         this.updateNavArrows();
14981     },
14982     
14983     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
14984     {
14985         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
14986         if (typeof(this.daysOfWeekDisabled) !== 'object') {
14987             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
14988         }
14989         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
14990             return parseInt(d, 10);
14991         });
14992         this.update();
14993         this.updateNavArrows();
14994     },
14995     
14996     updateNavArrows: function() 
14997     {
14998         var d = new Date(this.viewDate),
14999         year = d.getUTCFullYear(),
15000         month = d.getUTCMonth();
15001         
15002         Roo.each(this.picker().select('.prev', true).elements, function(v){
15003             v.show();
15004             switch (this.viewMode) {
15005                 case 0:
15006
15007                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
15008                         v.hide();
15009                     }
15010                     break;
15011                 case 1:
15012                 case 2:
15013                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
15014                         v.hide();
15015                     }
15016                     break;
15017             }
15018         });
15019         
15020         Roo.each(this.picker().select('.next', true).elements, function(v){
15021             v.show();
15022             switch (this.viewMode) {
15023                 case 0:
15024
15025                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
15026                         v.hide();
15027                     }
15028                     break;
15029                 case 1:
15030                 case 2:
15031                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
15032                         v.hide();
15033                     }
15034                     break;
15035             }
15036         })
15037     },
15038     
15039     moveMonth: function(date, dir)
15040     {
15041         if (!dir) return date;
15042         var new_date = new Date(date.valueOf()),
15043         day = new_date.getUTCDate(),
15044         month = new_date.getUTCMonth(),
15045         mag = Math.abs(dir),
15046         new_month, test;
15047         dir = dir > 0 ? 1 : -1;
15048         if (mag == 1){
15049             test = dir == -1
15050             // If going back one month, make sure month is not current month
15051             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
15052             ? function(){
15053                 return new_date.getUTCMonth() == month;
15054             }
15055             // If going forward one month, make sure month is as expected
15056             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
15057             : function(){
15058                 return new_date.getUTCMonth() != new_month;
15059             };
15060             new_month = month + dir;
15061             new_date.setUTCMonth(new_month);
15062             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
15063             if (new_month < 0 || new_month > 11)
15064                 new_month = (new_month + 12) % 12;
15065         } else {
15066             // For magnitudes >1, move one month at a time...
15067             for (var i=0; i<mag; i++)
15068                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
15069                 new_date = this.moveMonth(new_date, dir);
15070             // ...then reset the day, keeping it in the new month
15071             new_month = new_date.getUTCMonth();
15072             new_date.setUTCDate(day);
15073             test = function(){
15074                 return new_month != new_date.getUTCMonth();
15075             };
15076         }
15077         // Common date-resetting loop -- if date is beyond end of month, make it
15078         // end of month
15079         while (test()){
15080             new_date.setUTCDate(--day);
15081             new_date.setUTCMonth(new_month);
15082         }
15083         return new_date;
15084     },
15085
15086     moveYear: function(date, dir)
15087     {
15088         return this.moveMonth(date, dir*12);
15089     },
15090
15091     dateWithinRange: function(date)
15092     {
15093         return date >= this.startDate && date <= this.endDate;
15094     },
15095
15096     
15097     remove: function() 
15098     {
15099         this.picker().remove();
15100     }
15101    
15102 });
15103
15104 Roo.apply(Roo.bootstrap.DateField,  {
15105     
15106     head : {
15107         tag: 'thead',
15108         cn: [
15109         {
15110             tag: 'tr',
15111             cn: [
15112             {
15113                 tag: 'th',
15114                 cls: 'prev',
15115                 html: '<i class="fa fa-arrow-left"/>'
15116             },
15117             {
15118                 tag: 'th',
15119                 cls: 'switch',
15120                 colspan: '5'
15121             },
15122             {
15123                 tag: 'th',
15124                 cls: 'next',
15125                 html: '<i class="fa fa-arrow-right"/>'
15126             }
15127
15128             ]
15129         }
15130         ]
15131     },
15132     
15133     content : {
15134         tag: 'tbody',
15135         cn: [
15136         {
15137             tag: 'tr',
15138             cn: [
15139             {
15140                 tag: 'td',
15141                 colspan: '7'
15142             }
15143             ]
15144         }
15145         ]
15146     },
15147     
15148     footer : {
15149         tag: 'tfoot',
15150         cn: [
15151         {
15152             tag: 'tr',
15153             cn: [
15154             {
15155                 tag: 'th',
15156                 colspan: '7',
15157                 cls: 'today'
15158             }
15159                     
15160             ]
15161         }
15162         ]
15163     },
15164     
15165     dates:{
15166         en: {
15167             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
15168             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
15169             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
15170             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
15171             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
15172             today: "Today"
15173         }
15174     },
15175     
15176     modes: [
15177     {
15178         clsName: 'days',
15179         navFnc: 'Month',
15180         navStep: 1
15181     },
15182     {
15183         clsName: 'months',
15184         navFnc: 'FullYear',
15185         navStep: 1
15186     },
15187     {
15188         clsName: 'years',
15189         navFnc: 'FullYear',
15190         navStep: 10
15191     }]
15192 });
15193
15194 Roo.apply(Roo.bootstrap.DateField,  {
15195   
15196     template : {
15197         tag: 'div',
15198         cls: 'datepicker dropdown-menu',
15199         cn: [
15200         {
15201             tag: 'div',
15202             cls: 'datepicker-days',
15203             cn: [
15204             {
15205                 tag: 'table',
15206                 cls: 'table-condensed',
15207                 cn:[
15208                 Roo.bootstrap.DateField.head,
15209                 {
15210                     tag: 'tbody'
15211                 },
15212                 Roo.bootstrap.DateField.footer
15213                 ]
15214             }
15215             ]
15216         },
15217         {
15218             tag: 'div',
15219             cls: 'datepicker-months',
15220             cn: [
15221             {
15222                 tag: 'table',
15223                 cls: 'table-condensed',
15224                 cn:[
15225                 Roo.bootstrap.DateField.head,
15226                 Roo.bootstrap.DateField.content,
15227                 Roo.bootstrap.DateField.footer
15228                 ]
15229             }
15230             ]
15231         },
15232         {
15233             tag: 'div',
15234             cls: 'datepicker-years',
15235             cn: [
15236             {
15237                 tag: 'table',
15238                 cls: 'table-condensed',
15239                 cn:[
15240                 Roo.bootstrap.DateField.head,
15241                 Roo.bootstrap.DateField.content,
15242                 Roo.bootstrap.DateField.footer
15243                 ]
15244             }
15245             ]
15246         }
15247         ]
15248     }
15249 });
15250
15251  
15252
15253  /*
15254  * - LGPL
15255  *
15256  * TimeField
15257  * 
15258  */
15259
15260 /**
15261  * @class Roo.bootstrap.TimeField
15262  * @extends Roo.bootstrap.Input
15263  * Bootstrap DateField class
15264  * 
15265  * 
15266  * @constructor
15267  * Create a new TimeField
15268  * @param {Object} config The config object
15269  */
15270
15271 Roo.bootstrap.TimeField = function(config){
15272     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
15273     this.addEvents({
15274             /**
15275              * @event show
15276              * Fires when this field show.
15277              * @param {Roo.bootstrap.DateField} this
15278              * @param {Mixed} date The date value
15279              */
15280             show : true,
15281             /**
15282              * @event show
15283              * Fires when this field hide.
15284              * @param {Roo.bootstrap.DateField} this
15285              * @param {Mixed} date The date value
15286              */
15287             hide : true,
15288             /**
15289              * @event select
15290              * Fires when select a date.
15291              * @param {Roo.bootstrap.DateField} this
15292              * @param {Mixed} date The date value
15293              */
15294             select : true
15295         });
15296 };
15297
15298 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
15299     
15300     /**
15301      * @cfg {String} format
15302      * The default time format string which can be overriden for localization support.  The format must be
15303      * valid according to {@link Date#parseDate} (defaults to 'H:i').
15304      */
15305     format : "H:i",
15306        
15307     onRender: function(ct, position)
15308     {
15309         
15310         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
15311                 
15312         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
15313         
15314         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15315         
15316         this.pop = this.picker().select('>.datepicker-time',true).first();
15317         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block' 
15318         
15319         this.picker().on('mousedown', this.onMousedown, this);
15320         this.picker().on('click', this.onClick, this);
15321         
15322         this.picker().addClass('datepicker-dropdown');
15323     
15324         this.fillTime();
15325         this.update();
15326             
15327         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
15328         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
15329         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
15330         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
15331         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
15332         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
15333
15334     },
15335     
15336     fireKey: function(e){
15337         if (!this.picker().isVisible()){
15338             if (e.keyCode == 27) // allow escape to hide and re-show picker
15339                 this.show();
15340             return;
15341         }
15342
15343         e.preventDefault();
15344         
15345         switch(e.keyCode){
15346             case 27: // escape
15347                 this.hide();
15348                 break;
15349             case 37: // left
15350             case 39: // right
15351                 this.onTogglePeriod();
15352                 break;
15353             case 38: // up
15354                 this.onIncrementMinutes();
15355                 break;
15356             case 40: // down
15357                 this.onDecrementMinutes();
15358                 break;
15359             case 13: // enter
15360             case 9: // tab
15361                 this.setTime();
15362                 break;
15363         }
15364     },
15365     
15366     onClick: function(e) {
15367         e.stopPropagation();
15368         e.preventDefault();
15369     },
15370     
15371     picker : function()
15372     {
15373         return this.el.select('.datepicker', true).first();
15374     },
15375     
15376     fillTime: function()
15377     {    
15378         var time = this.pop.select('tbody', true).first();
15379         
15380         time.dom.innerHTML = '';
15381         
15382         time.createChild({
15383             tag: 'tr',
15384             cn: [
15385                 {
15386                     tag: 'td',
15387                     cn: [
15388                         {
15389                             tag: 'a',
15390                             href: '#',
15391                             cls: 'btn',
15392                             cn: [
15393                                 {
15394                                     tag: 'span',
15395                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
15396                                 }
15397                             ]
15398                         } 
15399                     ]
15400                 },
15401                 {
15402                     tag: 'td',
15403                     cls: 'separator'
15404                 },
15405                 {
15406                     tag: 'td',
15407                     cn: [
15408                         {
15409                             tag: 'a',
15410                             href: '#',
15411                             cls: 'btn',
15412                             cn: [
15413                                 {
15414                                     tag: 'span',
15415                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
15416                                 }
15417                             ]
15418                         }
15419                     ]
15420                 },
15421                 {
15422                     tag: 'td',
15423                     cls: 'separator'
15424                 }
15425             ]
15426         });
15427         
15428         time.createChild({
15429             tag: 'tr',
15430             cn: [
15431                 {
15432                     tag: 'td',
15433                     cn: [
15434                         {
15435                             tag: 'span',
15436                             cls: 'timepicker-hour',
15437                             html: '00'
15438                         }  
15439                     ]
15440                 },
15441                 {
15442                     tag: 'td',
15443                     cls: 'separator',
15444                     html: ':'
15445                 },
15446                 {
15447                     tag: 'td',
15448                     cn: [
15449                         {
15450                             tag: 'span',
15451                             cls: 'timepicker-minute',
15452                             html: '00'
15453                         }  
15454                     ]
15455                 },
15456                 {
15457                     tag: 'td',
15458                     cls: 'separator'
15459                 },
15460                 {
15461                     tag: 'td',
15462                     cn: [
15463                         {
15464                             tag: 'button',
15465                             type: 'button',
15466                             cls: 'btn btn-primary period',
15467                             html: 'AM'
15468                             
15469                         }
15470                     ]
15471                 }
15472             ]
15473         });
15474         
15475         time.createChild({
15476             tag: 'tr',
15477             cn: [
15478                 {
15479                     tag: 'td',
15480                     cn: [
15481                         {
15482                             tag: 'a',
15483                             href: '#',
15484                             cls: 'btn',
15485                             cn: [
15486                                 {
15487                                     tag: 'span',
15488                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
15489                                 }
15490                             ]
15491                         }
15492                     ]
15493                 },
15494                 {
15495                     tag: 'td',
15496                     cls: 'separator'
15497                 },
15498                 {
15499                     tag: 'td',
15500                     cn: [
15501                         {
15502                             tag: 'a',
15503                             href: '#',
15504                             cls: 'btn',
15505                             cn: [
15506                                 {
15507                                     tag: 'span',
15508                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
15509                                 }
15510                             ]
15511                         }
15512                     ]
15513                 },
15514                 {
15515                     tag: 'td',
15516                     cls: 'separator'
15517                 }
15518             ]
15519         });
15520         
15521     },
15522     
15523     update: function()
15524     {
15525         
15526         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
15527         
15528         this.fill();
15529     },
15530     
15531     fill: function() 
15532     {
15533         var hours = this.time.getHours();
15534         var minutes = this.time.getMinutes();
15535         var period = 'AM';
15536         
15537         if(hours > 11){
15538             period = 'PM';
15539         }
15540         
15541         if(hours == 0){
15542             hours = 12;
15543         }
15544         
15545         
15546         if(hours > 12){
15547             hours = hours - 12;
15548         }
15549         
15550         if(hours < 10){
15551             hours = '0' + hours;
15552         }
15553         
15554         if(minutes < 10){
15555             minutes = '0' + minutes;
15556         }
15557         
15558         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
15559         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
15560         this.pop.select('button', true).first().dom.innerHTML = period;
15561         
15562     },
15563     
15564     place: function()
15565     {   
15566         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
15567         
15568         var cls = ['bottom'];
15569         
15570         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
15571             cls.pop();
15572             cls.push('top');
15573         }
15574         
15575         cls.push('right');
15576         
15577         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
15578             cls.pop();
15579             cls.push('left');
15580         }
15581         
15582         this.picker().addClass(cls.join('-'));
15583         
15584         var _this = this;
15585         
15586         Roo.each(cls, function(c){
15587             if(c == 'bottom'){
15588                 _this.picker().setTop(_this.inputEl().getHeight());
15589                 return;
15590             }
15591             if(c == 'top'){
15592                 _this.picker().setTop(0 - _this.picker().getHeight());
15593                 return;
15594             }
15595             
15596             if(c == 'left'){
15597                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
15598                 return;
15599             }
15600             if(c == 'right'){
15601                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
15602                 return;
15603             }
15604         });
15605         
15606     },
15607   
15608     onFocus : function()
15609     {
15610         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
15611         this.show();
15612     },
15613     
15614     onBlur : function()
15615     {
15616         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
15617         this.hide();
15618     },
15619     
15620     show : function()
15621     {
15622         this.picker().show();
15623         this.pop.show();
15624         this.update();
15625         this.place();
15626         
15627         this.fireEvent('show', this, this.date);
15628     },
15629     
15630     hide : function()
15631     {
15632         this.picker().hide();
15633         this.pop.hide();
15634         
15635         this.fireEvent('hide', this, this.date);
15636     },
15637     
15638     setTime : function()
15639     {
15640         this.hide();
15641         this.setValue(this.time.format(this.format));
15642         
15643         this.fireEvent('select', this, this.date);
15644         
15645         
15646     },
15647     
15648     onMousedown: function(e){
15649         e.stopPropagation();
15650         e.preventDefault();
15651     },
15652     
15653     onIncrementHours: function()
15654     {
15655         Roo.log('onIncrementHours');
15656         this.time = this.time.add(Date.HOUR, 1);
15657         this.update();
15658         
15659     },
15660     
15661     onDecrementHours: function()
15662     {
15663         Roo.log('onDecrementHours');
15664         this.time = this.time.add(Date.HOUR, -1);
15665         this.update();
15666     },
15667     
15668     onIncrementMinutes: function()
15669     {
15670         Roo.log('onIncrementMinutes');
15671         this.time = this.time.add(Date.MINUTE, 1);
15672         this.update();
15673     },
15674     
15675     onDecrementMinutes: function()
15676     {
15677         Roo.log('onDecrementMinutes');
15678         this.time = this.time.add(Date.MINUTE, -1);
15679         this.update();
15680     },
15681     
15682     onTogglePeriod: function()
15683     {
15684         Roo.log('onTogglePeriod');
15685         this.time = this.time.add(Date.HOUR, 12);
15686         this.update();
15687     }
15688     
15689    
15690 });
15691
15692 Roo.apply(Roo.bootstrap.TimeField,  {
15693     
15694     content : {
15695         tag: 'tbody',
15696         cn: [
15697             {
15698                 tag: 'tr',
15699                 cn: [
15700                 {
15701                     tag: 'td',
15702                     colspan: '7'
15703                 }
15704                 ]
15705             }
15706         ]
15707     },
15708     
15709     footer : {
15710         tag: 'tfoot',
15711         cn: [
15712             {
15713                 tag: 'tr',
15714                 cn: [
15715                 {
15716                     tag: 'th',
15717                     colspan: '7',
15718                     cls: '',
15719                     cn: [
15720                         {
15721                             tag: 'button',
15722                             cls: 'btn btn-info ok',
15723                             html: 'OK'
15724                         }
15725                     ]
15726                 }
15727
15728                 ]
15729             }
15730         ]
15731     }
15732 });
15733
15734 Roo.apply(Roo.bootstrap.TimeField,  {
15735   
15736     template : {
15737         tag: 'div',
15738         cls: 'datepicker dropdown-menu',
15739         cn: [
15740             {
15741                 tag: 'div',
15742                 cls: 'datepicker-time',
15743                 cn: [
15744                 {
15745                     tag: 'table',
15746                     cls: 'table-condensed',
15747                     cn:[
15748                     Roo.bootstrap.TimeField.content,
15749                     Roo.bootstrap.TimeField.footer
15750                     ]
15751                 }
15752                 ]
15753             }
15754         ]
15755     }
15756 });
15757
15758  
15759
15760  /*
15761  * - LGPL
15762  *
15763  * CheckBox
15764  * 
15765  */
15766
15767 /**
15768  * @class Roo.bootstrap.CheckBox
15769  * @extends Roo.bootstrap.Input
15770  * Bootstrap CheckBox class
15771  * 
15772  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
15773  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
15774  * @cfg {String} boxLabel The text that appears beside the checkbox
15775  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
15776  * @cfg {Boolean} checked initnal the element
15777  * 
15778  * 
15779  * @constructor
15780  * Create a new CheckBox
15781  * @param {Object} config The config object
15782  */
15783
15784 Roo.bootstrap.CheckBox = function(config){
15785     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
15786    
15787         this.addEvents({
15788             /**
15789             * @event check
15790             * Fires when the element is checked or unchecked.
15791             * @param {Roo.bootstrap.CheckBox} this This input
15792             * @param {Boolean} checked The new checked value
15793             */
15794            check : true
15795         });
15796 };
15797
15798 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
15799     
15800     inputType: 'checkbox',
15801     inputValue: 1,
15802     valueOff: 0,
15803     boxLabel: false,
15804     checked: false,
15805     weight : false,
15806     
15807     getAutoCreate : function()
15808     {
15809         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
15810         
15811         var id = Roo.id();
15812         
15813         var cfg = {};
15814         
15815         cfg.cls = 'form-group checkbox' //input-group
15816         
15817         
15818         
15819         
15820         var input =  {
15821             tag: 'input',
15822             id : id,
15823             type : this.inputType,
15824             value : (!this.checked) ? this.valueOff : this.inputValue,
15825             cls : 'roo-checkbox', //'form-box',
15826             placeholder : this.placeholder || ''
15827             
15828         };
15829         
15830         if (this.weight) { // Validity check?
15831             cfg.cls += " checkbox-" + this.weight;
15832         }
15833         
15834         if (this.disabled) {
15835             input.disabled=true;
15836         }
15837         
15838         if(this.checked){
15839             input.checked = this.checked;
15840         }
15841         
15842         if (this.name) {
15843             input.name = this.name;
15844         }
15845         
15846         if (this.size) {
15847             input.cls += ' input-' + this.size;
15848         }
15849         
15850         var settings=this;
15851         ['xs','sm','md','lg'].map(function(size){
15852             if (settings[size]) {
15853                 cfg.cls += ' col-' + size + '-' + settings[size];
15854             }
15855         });
15856         
15857        
15858         
15859         var inputblock = input;
15860         
15861         
15862         
15863         
15864         if (this.before || this.after) {
15865             
15866             inputblock = {
15867                 cls : 'input-group',
15868                 cn :  [] 
15869             };
15870             if (this.before) {
15871                 inputblock.cn.push({
15872                     tag :'span',
15873                     cls : 'input-group-addon',
15874                     html : this.before
15875                 });
15876             }
15877             inputblock.cn.push(input);
15878             if (this.after) {
15879                 inputblock.cn.push({
15880                     tag :'span',
15881                     cls : 'input-group-addon',
15882                     html : this.after
15883                 });
15884             }
15885             
15886         };
15887         
15888         if (align ==='left' && this.fieldLabel.length) {
15889                 Roo.log("left and has label");
15890                 cfg.cn = [
15891                     
15892                     {
15893                         tag: 'label',
15894                         'for' :  id,
15895                         cls : 'control-label col-md-' + this.labelWidth,
15896                         html : this.fieldLabel
15897                         
15898                     },
15899                     {
15900                         cls : "col-md-" + (12 - this.labelWidth), 
15901                         cn: [
15902                             inputblock
15903                         ]
15904                     }
15905                     
15906                 ];
15907         } else if ( this.fieldLabel.length) {
15908                 Roo.log(" label");
15909                 cfg.cn = [
15910                    
15911                     {
15912                         tag: this.boxLabel ? 'span' : 'label',
15913                         'for': id,
15914                         cls: 'control-label box-input-label',
15915                         //cls : 'input-group-addon',
15916                         html : this.fieldLabel
15917                         
15918                     },
15919                     
15920                     inputblock
15921                     
15922                 ];
15923
15924         } else {
15925             
15926                 Roo.log(" no label && no align");
15927                 cfg.cn = [  inputblock ] ;
15928                 
15929                 
15930         };
15931          if(this.boxLabel){
15932             cfg.cn.push( {
15933                 tag: 'label',
15934                 'for': id,
15935                 cls: 'box-label',
15936                 html: this.boxLabel
15937                 
15938             });
15939         }
15940         
15941         
15942        
15943         return cfg;
15944         
15945     },
15946     
15947     /**
15948      * return the real input element.
15949      */
15950     inputEl: function ()
15951     {
15952         return this.el.select('input.roo-checkbox',true).first();
15953     },
15954     
15955     label: function()
15956     {
15957         return this.el.select('label.control-label',true).first();
15958     },
15959     
15960     initEvents : function()
15961     {
15962 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
15963         
15964         this.inputEl().on('click', this.onClick,  this);
15965         
15966     },
15967     
15968     onClick : function()
15969     {   
15970         this.setChecked(!this.checked);
15971     },
15972     
15973     setChecked : function(state,suppressEvent)
15974     {
15975         this.checked = state;
15976         
15977         this.inputEl().dom.checked = state;
15978         
15979         if(suppressEvent !== true){
15980             this.fireEvent('check', this, state);
15981         }
15982         
15983         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
15984         
15985     },
15986     
15987     setValue : function(v,suppressEvent)
15988     {
15989         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
15990     }
15991     
15992 });
15993
15994  
15995 /*
15996  * - LGPL
15997  *
15998  * Radio
15999  * 
16000  */
16001
16002 /**
16003  * @class Roo.bootstrap.Radio
16004  * @extends Roo.bootstrap.CheckBox
16005  * Bootstrap Radio class
16006
16007  * @constructor
16008  * Create a new Radio
16009  * @param {Object} config The config object
16010  */
16011
16012 Roo.bootstrap.Radio = function(config){
16013     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
16014    
16015 };
16016
16017 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
16018     
16019     inputType: 'radio',
16020     inputValue: '',
16021     valueOff: '',
16022     
16023     getAutoCreate : function()
16024     {
16025         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
16026         
16027         var id = Roo.id();
16028         
16029         var cfg = {};
16030         
16031         cfg.cls = 'form-group radio' //input-group
16032         
16033         var input =  {
16034             tag: 'input',
16035             id : id,
16036             type : this.inputType,
16037             value : (!this.checked) ? this.valueOff : this.inputValue,
16038             cls : 'roo-radio',
16039             placeholder : this.placeholder || ''
16040             
16041         };
16042           if (this.weight) { // Validity check?
16043             cfg.cls += " radio-" + this.weight;
16044         }
16045         if (this.disabled) {
16046             input.disabled=true;
16047         }
16048         
16049         if(this.checked){
16050             input.checked = this.checked;
16051         }
16052         
16053         if (this.name) {
16054             input.name = this.name;
16055         }
16056         
16057         if (this.size) {
16058             input.cls += ' input-' + this.size;
16059         }
16060         
16061         var settings=this;
16062         ['xs','sm','md','lg'].map(function(size){
16063             if (settings[size]) {
16064                 cfg.cls += ' col-' + size + '-' + settings[size];
16065             }
16066         });
16067         
16068         var inputblock = input;
16069         
16070         if (this.before || this.after) {
16071             
16072             inputblock = {
16073                 cls : 'input-group',
16074                 cn :  [] 
16075             };
16076             if (this.before) {
16077                 inputblock.cn.push({
16078                     tag :'span',
16079                     cls : 'input-group-addon',
16080                     html : this.before
16081                 });
16082             }
16083             inputblock.cn.push(input);
16084             if (this.after) {
16085                 inputblock.cn.push({
16086                     tag :'span',
16087                     cls : 'input-group-addon',
16088                     html : this.after
16089                 });
16090             }
16091             
16092         };
16093         
16094         if (align ==='left' && this.fieldLabel.length) {
16095                 Roo.log("left and has label");
16096                 cfg.cn = [
16097                     
16098                     {
16099                         tag: 'label',
16100                         'for' :  id,
16101                         cls : 'control-label col-md-' + this.labelWidth,
16102                         html : this.fieldLabel
16103                         
16104                     },
16105                     {
16106                         cls : "col-md-" + (12 - this.labelWidth), 
16107                         cn: [
16108                             inputblock
16109                         ]
16110                     }
16111                     
16112                 ];
16113         } else if ( this.fieldLabel.length) {
16114                 Roo.log(" label");
16115                  cfg.cn = [
16116                    
16117                     {
16118                         tag: 'label',
16119                         'for': id,
16120                         cls: 'control-label box-input-label',
16121                         //cls : 'input-group-addon',
16122                         html : this.fieldLabel
16123                         
16124                     },
16125                     
16126                     inputblock
16127                     
16128                 ];
16129
16130         } else {
16131             
16132                    Roo.log(" no label && no align");
16133                 cfg.cn = [
16134                     
16135                         inputblock
16136                     
16137                 ];
16138                 
16139                 
16140         };
16141         
16142         if(this.boxLabel){
16143             cfg.cn.push({
16144                 tag: 'label',
16145                 'for': id,
16146                 cls: 'box-label',
16147                 html: this.boxLabel
16148             })
16149         }
16150         
16151         return cfg;
16152         
16153     },
16154     inputEl: function ()
16155     {
16156         return this.el.select('input.roo-radio',true).first();
16157     },
16158     onClick : function()
16159     {   
16160         this.setChecked(true);
16161     },
16162     
16163     setChecked : function(state,suppressEvent)
16164     {
16165         if(state){
16166             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
16167                 v.dom.checked = false;
16168             });
16169         }
16170         
16171         this.checked = state;
16172         this.inputEl().dom.checked = state;
16173         
16174         if(suppressEvent !== true){
16175             this.fireEvent('check', this, state);
16176         }
16177         
16178         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
16179         
16180     },
16181     
16182     getGroupValue : function()
16183     {
16184         var value = ''
16185         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
16186             if(v.dom.checked == true){
16187                 value = v.dom.value;
16188             }
16189         });
16190         
16191         return value;
16192     },
16193     
16194     /**
16195      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
16196      * @return {Mixed} value The field value
16197      */
16198     getValue : function(){
16199         return this.getGroupValue();
16200     }
16201     
16202 });
16203
16204  
16205 //<script type="text/javascript">
16206
16207 /*
16208  * Based  Ext JS Library 1.1.1
16209  * Copyright(c) 2006-2007, Ext JS, LLC.
16210  * LGPL
16211  *
16212  */
16213  
16214 /**
16215  * @class Roo.HtmlEditorCore
16216  * @extends Roo.Component
16217  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
16218  *
16219  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
16220  */
16221
16222 Roo.HtmlEditorCore = function(config){
16223     
16224     
16225     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
16226     this.addEvents({
16227         /**
16228          * @event initialize
16229          * Fires when the editor is fully initialized (including the iframe)
16230          * @param {Roo.HtmlEditorCore} this
16231          */
16232         initialize: true,
16233         /**
16234          * @event activate
16235          * Fires when the editor is first receives the focus. Any insertion must wait
16236          * until after this event.
16237          * @param {Roo.HtmlEditorCore} this
16238          */
16239         activate: true,
16240          /**
16241          * @event beforesync
16242          * Fires before the textarea is updated with content from the editor iframe. Return false
16243          * to cancel the sync.
16244          * @param {Roo.HtmlEditorCore} this
16245          * @param {String} html
16246          */
16247         beforesync: true,
16248          /**
16249          * @event beforepush
16250          * Fires before the iframe editor is updated with content from the textarea. Return false
16251          * to cancel the push.
16252          * @param {Roo.HtmlEditorCore} this
16253          * @param {String} html
16254          */
16255         beforepush: true,
16256          /**
16257          * @event sync
16258          * Fires when the textarea is updated with content from the editor iframe.
16259          * @param {Roo.HtmlEditorCore} this
16260          * @param {String} html
16261          */
16262         sync: true,
16263          /**
16264          * @event push
16265          * Fires when the iframe editor is updated with content from the textarea.
16266          * @param {Roo.HtmlEditorCore} this
16267          * @param {String} html
16268          */
16269         push: true,
16270         
16271         /**
16272          * @event editorevent
16273          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
16274          * @param {Roo.HtmlEditorCore} this
16275          */
16276         editorevent: true
16277     });
16278      
16279 };
16280
16281
16282 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
16283
16284
16285      /**
16286      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
16287      */
16288     
16289     owner : false,
16290     
16291      /**
16292      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
16293      *                        Roo.resizable.
16294      */
16295     resizable : false,
16296      /**
16297      * @cfg {Number} height (in pixels)
16298      */   
16299     height: 300,
16300    /**
16301      * @cfg {Number} width (in pixels)
16302      */   
16303     width: 500,
16304     
16305     /**
16306      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
16307      * 
16308      */
16309     stylesheets: false,
16310     
16311     // id of frame..
16312     frameId: false,
16313     
16314     // private properties
16315     validationEvent : false,
16316     deferHeight: true,
16317     initialized : false,
16318     activated : false,
16319     sourceEditMode : false,
16320     onFocus : Roo.emptyFn,
16321     iframePad:3,
16322     hideMode:'offsets',
16323     
16324     clearUp: true,
16325     
16326      
16327     
16328
16329     /**
16330      * Protected method that will not generally be called directly. It
16331      * is called when the editor initializes the iframe with HTML contents. Override this method if you
16332      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
16333      */
16334     getDocMarkup : function(){
16335         // body styles..
16336         var st = '';
16337         Roo.log(this.stylesheets);
16338         
16339         // inherit styels from page...?? 
16340         if (this.stylesheets === false) {
16341             
16342             Roo.get(document.head).select('style').each(function(node) {
16343                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16344             });
16345             
16346             Roo.get(document.head).select('link').each(function(node) { 
16347                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16348             });
16349             
16350         } else if (!this.stylesheets.length) {
16351                 // simple..
16352                 st = '<style type="text/css">' +
16353                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16354                    '</style>';
16355         } else {
16356             Roo.each(this.stylesheets, function(s) {
16357                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
16358             });
16359             
16360         }
16361         
16362         st +=  '<style type="text/css">' +
16363             'IMG { cursor: pointer } ' +
16364         '</style>';
16365
16366         
16367         return '<html><head>' + st  +
16368             //<style type="text/css">' +
16369             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16370             //'</style>' +
16371             ' </head><body class="roo-htmleditor-body"></body></html>';
16372     },
16373
16374     // private
16375     onRender : function(ct, position)
16376     {
16377         var _t = this;
16378         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
16379         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
16380         
16381         
16382         this.el.dom.style.border = '0 none';
16383         this.el.dom.setAttribute('tabIndex', -1);
16384         this.el.addClass('x-hidden hide');
16385         
16386         
16387         
16388         if(Roo.isIE){ // fix IE 1px bogus margin
16389             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
16390         }
16391        
16392         
16393         this.frameId = Roo.id();
16394         
16395          
16396         
16397         var iframe = this.owner.wrap.createChild({
16398             tag: 'iframe',
16399             cls: 'form-control', // bootstrap..
16400             id: this.frameId,
16401             name: this.frameId,
16402             frameBorder : 'no',
16403             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
16404         }, this.el
16405         );
16406         
16407         
16408         this.iframe = iframe.dom;
16409
16410          this.assignDocWin();
16411         
16412         this.doc.designMode = 'on';
16413        
16414         this.doc.open();
16415         this.doc.write(this.getDocMarkup());
16416         this.doc.close();
16417
16418         
16419         var task = { // must defer to wait for browser to be ready
16420             run : function(){
16421                 //console.log("run task?" + this.doc.readyState);
16422                 this.assignDocWin();
16423                 if(this.doc.body || this.doc.readyState == 'complete'){
16424                     try {
16425                         this.doc.designMode="on";
16426                     } catch (e) {
16427                         return;
16428                     }
16429                     Roo.TaskMgr.stop(task);
16430                     this.initEditor.defer(10, this);
16431                 }
16432             },
16433             interval : 10,
16434             duration: 10000,
16435             scope: this
16436         };
16437         Roo.TaskMgr.start(task);
16438
16439         
16440          
16441     },
16442
16443     // private
16444     onResize : function(w, h)
16445     {
16446          Roo.log('resize: ' +w + ',' + h );
16447         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
16448         if(!this.iframe){
16449             return;
16450         }
16451         if(typeof w == 'number'){
16452             
16453             this.iframe.style.width = w + 'px';
16454         }
16455         if(typeof h == 'number'){
16456             
16457             this.iframe.style.height = h + 'px';
16458             if(this.doc){
16459                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
16460             }
16461         }
16462         
16463     },
16464
16465     /**
16466      * Toggles the editor between standard and source edit mode.
16467      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
16468      */
16469     toggleSourceEdit : function(sourceEditMode){
16470         
16471         this.sourceEditMode = sourceEditMode === true;
16472         
16473         if(this.sourceEditMode){
16474  
16475             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
16476             
16477         }else{
16478             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
16479             //this.iframe.className = '';
16480             this.deferFocus();
16481         }
16482         //this.setSize(this.owner.wrap.getSize());
16483         //this.fireEvent('editmodechange', this, this.sourceEditMode);
16484     },
16485
16486     
16487   
16488
16489     /**
16490      * Protected method that will not generally be called directly. If you need/want
16491      * custom HTML cleanup, this is the method you should override.
16492      * @param {String} html The HTML to be cleaned
16493      * return {String} The cleaned HTML
16494      */
16495     cleanHtml : function(html){
16496         html = String(html);
16497         if(html.length > 5){
16498             if(Roo.isSafari){ // strip safari nonsense
16499                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
16500             }
16501         }
16502         if(html == '&nbsp;'){
16503             html = '';
16504         }
16505         return html;
16506     },
16507
16508     /**
16509      * HTML Editor -> Textarea
16510      * Protected method that will not generally be called directly. Syncs the contents
16511      * of the editor iframe with the textarea.
16512      */
16513     syncValue : function(){
16514         if(this.initialized){
16515             var bd = (this.doc.body || this.doc.documentElement);
16516             //this.cleanUpPaste(); -- this is done else where and causes havoc..
16517             var html = bd.innerHTML;
16518             if(Roo.isSafari){
16519                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
16520                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
16521                 if(m && m[1]){
16522                     html = '<div style="'+m[0]+'">' + html + '</div>';
16523                 }
16524             }
16525             html = this.cleanHtml(html);
16526             // fix up the special chars.. normaly like back quotes in word...
16527             // however we do not want to do this with chinese..
16528             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
16529                 var cc = b.charCodeAt();
16530                 if (
16531                     (cc >= 0x4E00 && cc < 0xA000 ) ||
16532                     (cc >= 0x3400 && cc < 0x4E00 ) ||
16533                     (cc >= 0xf900 && cc < 0xfb00 )
16534                 ) {
16535                         return b;
16536                 }
16537                 return "&#"+cc+";" 
16538             });
16539             if(this.owner.fireEvent('beforesync', this, html) !== false){
16540                 this.el.dom.value = html;
16541                 this.owner.fireEvent('sync', this, html);
16542             }
16543         }
16544     },
16545
16546     /**
16547      * Protected method that will not generally be called directly. Pushes the value of the textarea
16548      * into the iframe editor.
16549      */
16550     pushValue : function(){
16551         if(this.initialized){
16552             var v = this.el.dom.value.trim();
16553             
16554 //            if(v.length < 1){
16555 //                v = '&#160;';
16556 //            }
16557             
16558             if(this.owner.fireEvent('beforepush', this, v) !== false){
16559                 var d = (this.doc.body || this.doc.documentElement);
16560                 d.innerHTML = v;
16561                 this.cleanUpPaste();
16562                 this.el.dom.value = d.innerHTML;
16563                 this.owner.fireEvent('push', this, v);
16564             }
16565         }
16566     },
16567
16568     // private
16569     deferFocus : function(){
16570         this.focus.defer(10, this);
16571     },
16572
16573     // doc'ed in Field
16574     focus : function(){
16575         if(this.win && !this.sourceEditMode){
16576             this.win.focus();
16577         }else{
16578             this.el.focus();
16579         }
16580     },
16581     
16582     assignDocWin: function()
16583     {
16584         var iframe = this.iframe;
16585         
16586          if(Roo.isIE){
16587             this.doc = iframe.contentWindow.document;
16588             this.win = iframe.contentWindow;
16589         } else {
16590 //            if (!Roo.get(this.frameId)) {
16591 //                return;
16592 //            }
16593 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
16594 //            this.win = Roo.get(this.frameId).dom.contentWindow;
16595             
16596             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
16597                 return;
16598             }
16599             
16600             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
16601             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
16602         }
16603     },
16604     
16605     // private
16606     initEditor : function(){
16607         //console.log("INIT EDITOR");
16608         this.assignDocWin();
16609         
16610         
16611         
16612         this.doc.designMode="on";
16613         this.doc.open();
16614         this.doc.write(this.getDocMarkup());
16615         this.doc.close();
16616         
16617         var dbody = (this.doc.body || this.doc.documentElement);
16618         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
16619         // this copies styles from the containing element into thsi one..
16620         // not sure why we need all of this..
16621         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
16622         
16623         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
16624         //ss['background-attachment'] = 'fixed'; // w3c
16625         dbody.bgProperties = 'fixed'; // ie
16626         //Roo.DomHelper.applyStyles(dbody, ss);
16627         Roo.EventManager.on(this.doc, {
16628             //'mousedown': this.onEditorEvent,
16629             'mouseup': this.onEditorEvent,
16630             'dblclick': this.onEditorEvent,
16631             'click': this.onEditorEvent,
16632             'keyup': this.onEditorEvent,
16633             buffer:100,
16634             scope: this
16635         });
16636         if(Roo.isGecko){
16637             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
16638         }
16639         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
16640             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
16641         }
16642         this.initialized = true;
16643
16644         this.owner.fireEvent('initialize', this);
16645         this.pushValue();
16646     },
16647
16648     // private
16649     onDestroy : function(){
16650         
16651         
16652         
16653         if(this.rendered){
16654             
16655             //for (var i =0; i < this.toolbars.length;i++) {
16656             //    // fixme - ask toolbars for heights?
16657             //    this.toolbars[i].onDestroy();
16658            // }
16659             
16660             //this.wrap.dom.innerHTML = '';
16661             //this.wrap.remove();
16662         }
16663     },
16664
16665     // private
16666     onFirstFocus : function(){
16667         
16668         this.assignDocWin();
16669         
16670         
16671         this.activated = true;
16672          
16673     
16674         if(Roo.isGecko){ // prevent silly gecko errors
16675             this.win.focus();
16676             var s = this.win.getSelection();
16677             if(!s.focusNode || s.focusNode.nodeType != 3){
16678                 var r = s.getRangeAt(0);
16679                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
16680                 r.collapse(true);
16681                 this.deferFocus();
16682             }
16683             try{
16684                 this.execCmd('useCSS', true);
16685                 this.execCmd('styleWithCSS', false);
16686             }catch(e){}
16687         }
16688         this.owner.fireEvent('activate', this);
16689     },
16690
16691     // private
16692     adjustFont: function(btn){
16693         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
16694         //if(Roo.isSafari){ // safari
16695         //    adjust *= 2;
16696        // }
16697         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
16698         if(Roo.isSafari){ // safari
16699             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
16700             v =  (v < 10) ? 10 : v;
16701             v =  (v > 48) ? 48 : v;
16702             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
16703             
16704         }
16705         
16706         
16707         v = Math.max(1, v+adjust);
16708         
16709         this.execCmd('FontSize', v  );
16710     },
16711
16712     onEditorEvent : function(e){
16713         this.owner.fireEvent('editorevent', this, e);
16714       //  this.updateToolbar();
16715         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
16716     },
16717
16718     insertTag : function(tg)
16719     {
16720         // could be a bit smarter... -> wrap the current selected tRoo..
16721         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
16722             
16723             range = this.createRange(this.getSelection());
16724             var wrappingNode = this.doc.createElement(tg.toLowerCase());
16725             wrappingNode.appendChild(range.extractContents());
16726             range.insertNode(wrappingNode);
16727
16728             return;
16729             
16730             
16731             
16732         }
16733         this.execCmd("formatblock",   tg);
16734         
16735     },
16736     
16737     insertText : function(txt)
16738     {
16739         
16740         
16741         var range = this.createRange();
16742         range.deleteContents();
16743                //alert(Sender.getAttribute('label'));
16744                
16745         range.insertNode(this.doc.createTextNode(txt));
16746     } ,
16747     
16748      
16749
16750     /**
16751      * Executes a Midas editor command on the editor document and performs necessary focus and
16752      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
16753      * @param {String} cmd The Midas command
16754      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16755      */
16756     relayCmd : function(cmd, value){
16757         this.win.focus();
16758         this.execCmd(cmd, value);
16759         this.owner.fireEvent('editorevent', this);
16760         //this.updateToolbar();
16761         this.owner.deferFocus();
16762     },
16763
16764     /**
16765      * Executes a Midas editor command directly on the editor document.
16766      * For visual commands, you should use {@link #relayCmd} instead.
16767      * <b>This should only be called after the editor is initialized.</b>
16768      * @param {String} cmd The Midas command
16769      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16770      */
16771     execCmd : function(cmd, value){
16772         this.doc.execCommand(cmd, false, value === undefined ? null : value);
16773         this.syncValue();
16774     },
16775  
16776  
16777    
16778     /**
16779      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
16780      * to insert tRoo.
16781      * @param {String} text | dom node.. 
16782      */
16783     insertAtCursor : function(text)
16784     {
16785         
16786         
16787         
16788         if(!this.activated){
16789             return;
16790         }
16791         /*
16792         if(Roo.isIE){
16793             this.win.focus();
16794             var r = this.doc.selection.createRange();
16795             if(r){
16796                 r.collapse(true);
16797                 r.pasteHTML(text);
16798                 this.syncValue();
16799                 this.deferFocus();
16800             
16801             }
16802             return;
16803         }
16804         */
16805         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
16806             this.win.focus();
16807             
16808             
16809             // from jquery ui (MIT licenced)
16810             var range, node;
16811             var win = this.win;
16812             
16813             if (win.getSelection && win.getSelection().getRangeAt) {
16814                 range = win.getSelection().getRangeAt(0);
16815                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
16816                 range.insertNode(node);
16817             } else if (win.document.selection && win.document.selection.createRange) {
16818                 // no firefox support
16819                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
16820                 win.document.selection.createRange().pasteHTML(txt);
16821             } else {
16822                 // no firefox support
16823                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
16824                 this.execCmd('InsertHTML', txt);
16825             } 
16826             
16827             this.syncValue();
16828             
16829             this.deferFocus();
16830         }
16831     },
16832  // private
16833     mozKeyPress : function(e){
16834         if(e.ctrlKey){
16835             var c = e.getCharCode(), cmd;
16836           
16837             if(c > 0){
16838                 c = String.fromCharCode(c).toLowerCase();
16839                 switch(c){
16840                     case 'b':
16841                         cmd = 'bold';
16842                         break;
16843                     case 'i':
16844                         cmd = 'italic';
16845                         break;
16846                     
16847                     case 'u':
16848                         cmd = 'underline';
16849                         break;
16850                     
16851                     case 'v':
16852                         this.cleanUpPaste.defer(100, this);
16853                         return;
16854                         
16855                 }
16856                 if(cmd){
16857                     this.win.focus();
16858                     this.execCmd(cmd);
16859                     this.deferFocus();
16860                     e.preventDefault();
16861                 }
16862                 
16863             }
16864         }
16865     },
16866
16867     // private
16868     fixKeys : function(){ // load time branching for fastest keydown performance
16869         if(Roo.isIE){
16870             return function(e){
16871                 var k = e.getKey(), r;
16872                 if(k == e.TAB){
16873                     e.stopEvent();
16874                     r = this.doc.selection.createRange();
16875                     if(r){
16876                         r.collapse(true);
16877                         r.pasteHTML('&#160;&#160;&#160;&#160;');
16878                         this.deferFocus();
16879                     }
16880                     return;
16881                 }
16882                 
16883                 if(k == e.ENTER){
16884                     r = this.doc.selection.createRange();
16885                     if(r){
16886                         var target = r.parentElement();
16887                         if(!target || target.tagName.toLowerCase() != 'li'){
16888                             e.stopEvent();
16889                             r.pasteHTML('<br />');
16890                             r.collapse(false);
16891                             r.select();
16892                         }
16893                     }
16894                 }
16895                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
16896                     this.cleanUpPaste.defer(100, this);
16897                     return;
16898                 }
16899                 
16900                 
16901             };
16902         }else if(Roo.isOpera){
16903             return function(e){
16904                 var k = e.getKey();
16905                 if(k == e.TAB){
16906                     e.stopEvent();
16907                     this.win.focus();
16908                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
16909                     this.deferFocus();
16910                 }
16911                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
16912                     this.cleanUpPaste.defer(100, this);
16913                     return;
16914                 }
16915                 
16916             };
16917         }else if(Roo.isSafari){
16918             return function(e){
16919                 var k = e.getKey();
16920                 
16921                 if(k == e.TAB){
16922                     e.stopEvent();
16923                     this.execCmd('InsertText','\t');
16924                     this.deferFocus();
16925                     return;
16926                 }
16927                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
16928                     this.cleanUpPaste.defer(100, this);
16929                     return;
16930                 }
16931                 
16932              };
16933         }
16934     }(),
16935     
16936     getAllAncestors: function()
16937     {
16938         var p = this.getSelectedNode();
16939         var a = [];
16940         if (!p) {
16941             a.push(p); // push blank onto stack..
16942             p = this.getParentElement();
16943         }
16944         
16945         
16946         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
16947             a.push(p);
16948             p = p.parentNode;
16949         }
16950         a.push(this.doc.body);
16951         return a;
16952     },
16953     lastSel : false,
16954     lastSelNode : false,
16955     
16956     
16957     getSelection : function() 
16958     {
16959         this.assignDocWin();
16960         return Roo.isIE ? this.doc.selection : this.win.getSelection();
16961     },
16962     
16963     getSelectedNode: function() 
16964     {
16965         // this may only work on Gecko!!!
16966         
16967         // should we cache this!!!!
16968         
16969         
16970         
16971          
16972         var range = this.createRange(this.getSelection()).cloneRange();
16973         
16974         if (Roo.isIE) {
16975             var parent = range.parentElement();
16976             while (true) {
16977                 var testRange = range.duplicate();
16978                 testRange.moveToElementText(parent);
16979                 if (testRange.inRange(range)) {
16980                     break;
16981                 }
16982                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
16983                     break;
16984                 }
16985                 parent = parent.parentElement;
16986             }
16987             return parent;
16988         }
16989         
16990         // is ancestor a text element.
16991         var ac =  range.commonAncestorContainer;
16992         if (ac.nodeType == 3) {
16993             ac = ac.parentNode;
16994         }
16995         
16996         var ar = ac.childNodes;
16997          
16998         var nodes = [];
16999         var other_nodes = [];
17000         var has_other_nodes = false;
17001         for (var i=0;i<ar.length;i++) {
17002             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
17003                 continue;
17004             }
17005             // fullly contained node.
17006             
17007             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
17008                 nodes.push(ar[i]);
17009                 continue;
17010             }
17011             
17012             // probably selected..
17013             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
17014                 other_nodes.push(ar[i]);
17015                 continue;
17016             }
17017             // outer..
17018             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
17019                 continue;
17020             }
17021             
17022             
17023             has_other_nodes = true;
17024         }
17025         if (!nodes.length && other_nodes.length) {
17026             nodes= other_nodes;
17027         }
17028         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
17029             return false;
17030         }
17031         
17032         return nodes[0];
17033     },
17034     createRange: function(sel)
17035     {
17036         // this has strange effects when using with 
17037         // top toolbar - not sure if it's a great idea.
17038         //this.editor.contentWindow.focus();
17039         if (typeof sel != "undefined") {
17040             try {
17041                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
17042             } catch(e) {
17043                 return this.doc.createRange();
17044             }
17045         } else {
17046             return this.doc.createRange();
17047         }
17048     },
17049     getParentElement: function()
17050     {
17051         
17052         this.assignDocWin();
17053         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
17054         
17055         var range = this.createRange(sel);
17056          
17057         try {
17058             var p = range.commonAncestorContainer;
17059             while (p.nodeType == 3) { // text node
17060                 p = p.parentNode;
17061             }
17062             return p;
17063         } catch (e) {
17064             return null;
17065         }
17066     
17067     },
17068     /***
17069      *
17070      * Range intersection.. the hard stuff...
17071      *  '-1' = before
17072      *  '0' = hits..
17073      *  '1' = after.
17074      *         [ -- selected range --- ]
17075      *   [fail]                        [fail]
17076      *
17077      *    basically..
17078      *      if end is before start or  hits it. fail.
17079      *      if start is after end or hits it fail.
17080      *
17081      *   if either hits (but other is outside. - then it's not 
17082      *   
17083      *    
17084      **/
17085     
17086     
17087     // @see http://www.thismuchiknow.co.uk/?p=64.
17088     rangeIntersectsNode : function(range, node)
17089     {
17090         var nodeRange = node.ownerDocument.createRange();
17091         try {
17092             nodeRange.selectNode(node);
17093         } catch (e) {
17094             nodeRange.selectNodeContents(node);
17095         }
17096     
17097         var rangeStartRange = range.cloneRange();
17098         rangeStartRange.collapse(true);
17099     
17100         var rangeEndRange = range.cloneRange();
17101         rangeEndRange.collapse(false);
17102     
17103         var nodeStartRange = nodeRange.cloneRange();
17104         nodeStartRange.collapse(true);
17105     
17106         var nodeEndRange = nodeRange.cloneRange();
17107         nodeEndRange.collapse(false);
17108     
17109         return rangeStartRange.compareBoundaryPoints(
17110                  Range.START_TO_START, nodeEndRange) == -1 &&
17111                rangeEndRange.compareBoundaryPoints(
17112                  Range.START_TO_START, nodeStartRange) == 1;
17113         
17114          
17115     },
17116     rangeCompareNode : function(range, node)
17117     {
17118         var nodeRange = node.ownerDocument.createRange();
17119         try {
17120             nodeRange.selectNode(node);
17121         } catch (e) {
17122             nodeRange.selectNodeContents(node);
17123         }
17124         
17125         
17126         range.collapse(true);
17127     
17128         nodeRange.collapse(true);
17129      
17130         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
17131         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
17132          
17133         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
17134         
17135         var nodeIsBefore   =  ss == 1;
17136         var nodeIsAfter    = ee == -1;
17137         
17138         if (nodeIsBefore && nodeIsAfter)
17139             return 0; // outer
17140         if (!nodeIsBefore && nodeIsAfter)
17141             return 1; //right trailed.
17142         
17143         if (nodeIsBefore && !nodeIsAfter)
17144             return 2;  // left trailed.
17145         // fully contined.
17146         return 3;
17147     },
17148
17149     // private? - in a new class?
17150     cleanUpPaste :  function()
17151     {
17152         // cleans up the whole document..
17153         Roo.log('cleanuppaste');
17154         
17155         this.cleanUpChildren(this.doc.body);
17156         var clean = this.cleanWordChars(this.doc.body.innerHTML);
17157         if (clean != this.doc.body.innerHTML) {
17158             this.doc.body.innerHTML = clean;
17159         }
17160         
17161     },
17162     
17163     cleanWordChars : function(input) {// change the chars to hex code
17164         var he = Roo.HtmlEditorCore;
17165         
17166         var output = input;
17167         Roo.each(he.swapCodes, function(sw) { 
17168             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
17169             
17170             output = output.replace(swapper, sw[1]);
17171         });
17172         
17173         return output;
17174     },
17175     
17176     
17177     cleanUpChildren : function (n)
17178     {
17179         if (!n.childNodes.length) {
17180             return;
17181         }
17182         for (var i = n.childNodes.length-1; i > -1 ; i--) {
17183            this.cleanUpChild(n.childNodes[i]);
17184         }
17185     },
17186     
17187     
17188         
17189     
17190     cleanUpChild : function (node)
17191     {
17192         var ed = this;
17193         //console.log(node);
17194         if (node.nodeName == "#text") {
17195             // clean up silly Windows -- stuff?
17196             return; 
17197         }
17198         if (node.nodeName == "#comment") {
17199             node.parentNode.removeChild(node);
17200             // clean up silly Windows -- stuff?
17201             return; 
17202         }
17203         
17204         if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1 && this.clearUp) {
17205             // remove node.
17206             node.parentNode.removeChild(node);
17207             return;
17208             
17209         }
17210         
17211         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
17212         
17213         // remove <a name=....> as rendering on yahoo mailer is borked with this.
17214         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
17215         
17216         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
17217         //    remove_keep_children = true;
17218         //}
17219         
17220         if (remove_keep_children) {
17221             this.cleanUpChildren(node);
17222             // inserts everything just before this node...
17223             while (node.childNodes.length) {
17224                 var cn = node.childNodes[0];
17225                 node.removeChild(cn);
17226                 node.parentNode.insertBefore(cn, node);
17227             }
17228             node.parentNode.removeChild(node);
17229             return;
17230         }
17231         
17232         if (!node.attributes || !node.attributes.length) {
17233             this.cleanUpChildren(node);
17234             return;
17235         }
17236         
17237         function cleanAttr(n,v)
17238         {
17239             
17240             if (v.match(/^\./) || v.match(/^\//)) {
17241                 return;
17242             }
17243             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
17244                 return;
17245             }
17246             if (v.match(/^#/)) {
17247                 return;
17248             }
17249 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
17250             node.removeAttribute(n);
17251             
17252         }
17253         
17254         function cleanStyle(n,v)
17255         {
17256             if (v.match(/expression/)) { //XSS?? should we even bother..
17257                 node.removeAttribute(n);
17258                 return;
17259             }
17260             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
17261             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
17262             
17263             
17264             var parts = v.split(/;/);
17265             var clean = [];
17266             
17267             Roo.each(parts, function(p) {
17268                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
17269                 if (!p.length) {
17270                     return true;
17271                 }
17272                 var l = p.split(':').shift().replace(/\s+/g,'');
17273                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
17274                 
17275                 if ( cblack.indexOf(l) > -1) {
17276 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17277                     //node.removeAttribute(n);
17278                     return true;
17279                 }
17280                 //Roo.log()
17281                 // only allow 'c whitelisted system attributes'
17282                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
17283 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17284                     //node.removeAttribute(n);
17285                     return true;
17286                 }
17287                 
17288                 
17289                  
17290                 
17291                 clean.push(p);
17292                 return true;
17293             });
17294             if (clean.length) { 
17295                 node.setAttribute(n, clean.join(';'));
17296             } else {
17297                 node.removeAttribute(n);
17298             }
17299             
17300         }
17301         
17302         
17303         for (var i = node.attributes.length-1; i > -1 ; i--) {
17304             var a = node.attributes[i];
17305             //console.log(a);
17306             
17307             if (a.name.toLowerCase().substr(0,2)=='on')  {
17308                 node.removeAttribute(a.name);
17309                 continue;
17310             }
17311             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
17312                 node.removeAttribute(a.name);
17313                 continue;
17314             }
17315             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
17316                 cleanAttr(a.name,a.value); // fixme..
17317                 continue;
17318             }
17319             if (a.name == 'style') {
17320                 cleanStyle(a.name,a.value);
17321                 continue;
17322             }
17323             /// clean up MS crap..
17324             // tecnically this should be a list of valid class'es..
17325             
17326             
17327             if (a.name == 'class') {
17328                 if (a.value.match(/^Mso/)) {
17329                     node.className = '';
17330                 }
17331                 
17332                 if (a.value.match(/body/)) {
17333                     node.className = '';
17334                 }
17335                 continue;
17336             }
17337             
17338             // style cleanup!?
17339             // class cleanup?
17340             
17341         }
17342         
17343         
17344         this.cleanUpChildren(node);
17345         
17346         
17347     },
17348     /**
17349      * Clean up MS wordisms...
17350      */
17351     cleanWord : function(node)
17352     {
17353         var _t = this;
17354         var cleanWordChildren = function()
17355         {
17356             if (!node.childNodes.length) {
17357                 return;
17358             }
17359             for (var i = node.childNodes.length-1; i > -1 ; i--) {
17360                _t.cleanWord(node.childNodes[i]);
17361             }
17362         }
17363         
17364         
17365         if (!node) {
17366             this.cleanWord(this.doc.body);
17367             return;
17368         }
17369         if (node.nodeName == "#text") {
17370             // clean up silly Windows -- stuff?
17371             return; 
17372         }
17373         if (node.nodeName == "#comment") {
17374             node.parentNode.removeChild(node);
17375             // clean up silly Windows -- stuff?
17376             return; 
17377         }
17378         
17379         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
17380             node.parentNode.removeChild(node);
17381             return;
17382         }
17383         
17384         // remove - but keep children..
17385         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
17386             while (node.childNodes.length) {
17387                 var cn = node.childNodes[0];
17388                 node.removeChild(cn);
17389                 node.parentNode.insertBefore(cn, node);
17390             }
17391             node.parentNode.removeChild(node);
17392             cleanWordChildren();
17393             return;
17394         }
17395         // clean styles
17396         if (node.className.length) {
17397             
17398             var cn = node.className.split(/\W+/);
17399             var cna = [];
17400             Roo.each(cn, function(cls) {
17401                 if (cls.match(/Mso[a-zA-Z]+/)) {
17402                     return;
17403                 }
17404                 cna.push(cls);
17405             });
17406             node.className = cna.length ? cna.join(' ') : '';
17407             if (!cna.length) {
17408                 node.removeAttribute("class");
17409             }
17410         }
17411         
17412         if (node.hasAttribute("lang")) {
17413             node.removeAttribute("lang");
17414         }
17415         
17416         if (node.hasAttribute("style")) {
17417             
17418             var styles = node.getAttribute("style").split(";");
17419             var nstyle = [];
17420             Roo.each(styles, function(s) {
17421                 if (!s.match(/:/)) {
17422                     return;
17423                 }
17424                 var kv = s.split(":");
17425                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
17426                     return;
17427                 }
17428                 // what ever is left... we allow.
17429                 nstyle.push(s);
17430             });
17431             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
17432             if (!nstyle.length) {
17433                 node.removeAttribute('style');
17434             }
17435         }
17436         
17437         cleanWordChildren();
17438         
17439         
17440     },
17441     domToHTML : function(currentElement, depth, nopadtext) {
17442         
17443             depth = depth || 0;
17444             nopadtext = nopadtext || false;
17445         
17446             if (!currentElement) {
17447                 return this.domToHTML(this.doc.body);
17448             }
17449             
17450             //Roo.log(currentElement);
17451             var j;
17452             var allText = false;
17453             var nodeName = currentElement.nodeName;
17454             var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
17455             
17456             if  (nodeName == '#text') {
17457                 return currentElement.nodeValue;
17458             }
17459             
17460             
17461             var ret = '';
17462             if (nodeName != 'BODY') {
17463                  
17464                 var i = 0;
17465                 // Prints the node tagName, such as <A>, <IMG>, etc
17466                 if (tagName) {
17467                     var attr = [];
17468                     for(i = 0; i < currentElement.attributes.length;i++) {
17469                         // quoting?
17470                         var aname = currentElement.attributes.item(i).name;
17471                         if (!currentElement.attributes.item(i).value.length) {
17472                             continue;
17473                         }
17474                         attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
17475                     }
17476                     
17477                     ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
17478                 } 
17479                 else {
17480                     
17481                     // eack
17482                 }
17483             } else {
17484                 tagName = false;
17485             }
17486             if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
17487                 return ret;
17488             }
17489             if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
17490                 nopadtext = true;
17491             }
17492             
17493             
17494             // Traverse the tree
17495             i = 0;
17496             var currentElementChild = currentElement.childNodes.item(i);
17497             var allText = true;
17498             var innerHTML  = '';
17499             lastnode = '';
17500             while (currentElementChild) {
17501                 // Formatting code (indent the tree so it looks nice on the screen)
17502                 var nopad = nopadtext;
17503                 if (lastnode == 'SPAN') {
17504                     nopad  = true;
17505                 }
17506                 // text
17507                 if  (currentElementChild.nodeName == '#text') {
17508                     var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
17509                     if (!nopad && toadd.length > 80) {
17510                         innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
17511                     }
17512                     innerHTML  += toadd;
17513                     
17514                     i++;
17515                     currentElementChild = currentElement.childNodes.item(i);
17516                     lastNode = '';
17517                     continue;
17518                 }
17519                 allText = false;
17520                 
17521                 innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
17522                     
17523                 // Recursively traverse the tree structure of the child node
17524                 innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
17525                 lastnode = currentElementChild.nodeName;
17526                 i++;
17527                 currentElementChild=currentElement.childNodes.item(i);
17528             }
17529             
17530             ret += innerHTML;
17531             
17532             if (!allText) {
17533                     // The remaining code is mostly for formatting the tree
17534                 ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
17535             }
17536             
17537             
17538             if (tagName) {
17539                 ret+= "</"+tagName+">";
17540             }
17541             return ret;
17542             
17543         }
17544     
17545     // hide stuff that is not compatible
17546     /**
17547      * @event blur
17548      * @hide
17549      */
17550     /**
17551      * @event change
17552      * @hide
17553      */
17554     /**
17555      * @event focus
17556      * @hide
17557      */
17558     /**
17559      * @event specialkey
17560      * @hide
17561      */
17562     /**
17563      * @cfg {String} fieldClass @hide
17564      */
17565     /**
17566      * @cfg {String} focusClass @hide
17567      */
17568     /**
17569      * @cfg {String} autoCreate @hide
17570      */
17571     /**
17572      * @cfg {String} inputType @hide
17573      */
17574     /**
17575      * @cfg {String} invalidClass @hide
17576      */
17577     /**
17578      * @cfg {String} invalidText @hide
17579      */
17580     /**
17581      * @cfg {String} msgFx @hide
17582      */
17583     /**
17584      * @cfg {String} validateOnBlur @hide
17585      */
17586 });
17587
17588 Roo.HtmlEditorCore.white = [
17589         'area', 'br', 'img', 'input', 'hr', 'wbr',
17590         
17591        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
17592        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
17593        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
17594        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
17595        'table',   'ul',         'xmp', 
17596        
17597        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
17598       'thead',   'tr', 
17599      
17600       'dir', 'menu', 'ol', 'ul', 'dl',
17601        
17602       'embed',  'object'
17603 ];
17604
17605
17606 Roo.HtmlEditorCore.black = [
17607     //    'embed',  'object', // enable - backend responsiblity to clean thiese
17608         'applet', // 
17609         'base',   'basefont', 'bgsound', 'blink',  'body', 
17610         'frame',  'frameset', 'head',    'html',   'ilayer', 
17611         'iframe', 'layer',  'link',     'meta',    'object',   
17612         'script', 'style' ,'title',  'xml' // clean later..
17613 ];
17614 Roo.HtmlEditorCore.clean = [
17615     'script', 'style', 'title', 'xml'
17616 ];
17617 Roo.HtmlEditorCore.remove = [
17618     'font'
17619 ];
17620 // attributes..
17621
17622 Roo.HtmlEditorCore.ablack = [
17623     'on'
17624 ];
17625     
17626 Roo.HtmlEditorCore.aclean = [ 
17627     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
17628 ];
17629
17630 // protocols..
17631 Roo.HtmlEditorCore.pwhite= [
17632         'http',  'https',  'mailto'
17633 ];
17634
17635 // white listed style attributes.
17636 Roo.HtmlEditorCore.cwhite= [
17637       //  'text-align', /// default is to allow most things..
17638       
17639          
17640 //        'font-size'//??
17641 ];
17642
17643 // black listed style attributes.
17644 Roo.HtmlEditorCore.cblack= [
17645       //  'font-size' -- this can be set by the project 
17646 ];
17647
17648
17649 Roo.HtmlEditorCore.swapCodes   =[ 
17650     [    8211, "--" ], 
17651     [    8212, "--" ], 
17652     [    8216,  "'" ],  
17653     [    8217, "'" ],  
17654     [    8220, '"' ],  
17655     [    8221, '"' ],  
17656     [    8226, "*" ],  
17657     [    8230, "..." ]
17658 ]; 
17659
17660     /*
17661  * - LGPL
17662  *
17663  * HtmlEditor
17664  * 
17665  */
17666
17667 /**
17668  * @class Roo.bootstrap.HtmlEditor
17669  * @extends Roo.bootstrap.TextArea
17670  * Bootstrap HtmlEditor class
17671
17672  * @constructor
17673  * Create a new HtmlEditor
17674  * @param {Object} config The config object
17675  */
17676
17677 Roo.bootstrap.HtmlEditor = function(config){
17678     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
17679     if (!this.toolbars) {
17680         this.toolbars = [];
17681     }
17682     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
17683     this.addEvents({
17684             /**
17685              * @event initialize
17686              * Fires when the editor is fully initialized (including the iframe)
17687              * @param {HtmlEditor} this
17688              */
17689             initialize: true,
17690             /**
17691              * @event activate
17692              * Fires when the editor is first receives the focus. Any insertion must wait
17693              * until after this event.
17694              * @param {HtmlEditor} this
17695              */
17696             activate: true,
17697              /**
17698              * @event beforesync
17699              * Fires before the textarea is updated with content from the editor iframe. Return false
17700              * to cancel the sync.
17701              * @param {HtmlEditor} this
17702              * @param {String} html
17703              */
17704             beforesync: true,
17705              /**
17706              * @event beforepush
17707              * Fires before the iframe editor is updated with content from the textarea. Return false
17708              * to cancel the push.
17709              * @param {HtmlEditor} this
17710              * @param {String} html
17711              */
17712             beforepush: true,
17713              /**
17714              * @event sync
17715              * Fires when the textarea is updated with content from the editor iframe.
17716              * @param {HtmlEditor} this
17717              * @param {String} html
17718              */
17719             sync: true,
17720              /**
17721              * @event push
17722              * Fires when the iframe editor is updated with content from the textarea.
17723              * @param {HtmlEditor} this
17724              * @param {String} html
17725              */
17726             push: true,
17727              /**
17728              * @event editmodechange
17729              * Fires when the editor switches edit modes
17730              * @param {HtmlEditor} this
17731              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
17732              */
17733             editmodechange: true,
17734             /**
17735              * @event editorevent
17736              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
17737              * @param {HtmlEditor} this
17738              */
17739             editorevent: true,
17740             /**
17741              * @event firstfocus
17742              * Fires when on first focus - needed by toolbars..
17743              * @param {HtmlEditor} this
17744              */
17745             firstfocus: true,
17746             /**
17747              * @event autosave
17748              * Auto save the htmlEditor value as a file into Events
17749              * @param {HtmlEditor} this
17750              */
17751             autosave: true,
17752             /**
17753              * @event savedpreview
17754              * preview the saved version of htmlEditor
17755              * @param {HtmlEditor} this
17756              */
17757             savedpreview: true
17758         });
17759 };
17760
17761
17762 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
17763     
17764     
17765       /**
17766      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
17767      */
17768     toolbars : false,
17769    
17770      /**
17771      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
17772      *                        Roo.resizable.
17773      */
17774     resizable : false,
17775      /**
17776      * @cfg {Number} height (in pixels)
17777      */   
17778     height: 300,
17779    /**
17780      * @cfg {Number} width (in pixels)
17781      */   
17782     width: false,
17783     
17784     /**
17785      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
17786      * 
17787      */
17788     stylesheets: false,
17789     
17790     // id of frame..
17791     frameId: false,
17792     
17793     // private properties
17794     validationEvent : false,
17795     deferHeight: true,
17796     initialized : false,
17797     activated : false,
17798     
17799     onFocus : Roo.emptyFn,
17800     iframePad:3,
17801     hideMode:'offsets',
17802     
17803     
17804     tbContainer : false,
17805     
17806     toolbarContainer :function() {
17807         return this.wrap.select('.x-html-editor-tb',true).first();
17808     },
17809
17810     /**
17811      * Protected method that will not generally be called directly. It
17812      * is called when the editor creates its toolbar. Override this method if you need to
17813      * add custom toolbar buttons.
17814      * @param {HtmlEditor} editor
17815      */
17816     createToolbar : function(){
17817         
17818         Roo.log("create toolbars");
17819         
17820         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
17821         this.toolbars[0].render(this.toolbarContainer());
17822         
17823         return;
17824         
17825 //        if (!editor.toolbars || !editor.toolbars.length) {
17826 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
17827 //        }
17828 //        
17829 //        for (var i =0 ; i < editor.toolbars.length;i++) {
17830 //            editor.toolbars[i] = Roo.factory(
17831 //                    typeof(editor.toolbars[i]) == 'string' ?
17832 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
17833 //                Roo.bootstrap.HtmlEditor);
17834 //            editor.toolbars[i].init(editor);
17835 //        }
17836     },
17837
17838      
17839     // private
17840     onRender : function(ct, position)
17841     {
17842        // Roo.log("Call onRender: " + this.xtype);
17843         var _t = this;
17844         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
17845       
17846         this.wrap = this.inputEl().wrap({
17847             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
17848         });
17849         
17850         this.editorcore.onRender(ct, position);
17851          
17852         if (this.resizable) {
17853             this.resizeEl = new Roo.Resizable(this.wrap, {
17854                 pinned : true,
17855                 wrap: true,
17856                 dynamic : true,
17857                 minHeight : this.height,
17858                 height: this.height,
17859                 handles : this.resizable,
17860                 width: this.width,
17861                 listeners : {
17862                     resize : function(r, w, h) {
17863                         _t.onResize(w,h); // -something
17864                     }
17865                 }
17866             });
17867             
17868         }
17869         this.createToolbar(this);
17870        
17871         
17872         if(!this.width && this.resizable){
17873             this.setSize(this.wrap.getSize());
17874         }
17875         if (this.resizeEl) {
17876             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
17877             // should trigger onReize..
17878         }
17879         
17880     },
17881
17882     // private
17883     onResize : function(w, h)
17884     {
17885         Roo.log('resize: ' +w + ',' + h );
17886         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
17887         var ew = false;
17888         var eh = false;
17889         
17890         if(this.inputEl() ){
17891             if(typeof w == 'number'){
17892                 var aw = w - this.wrap.getFrameWidth('lr');
17893                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
17894                 ew = aw;
17895             }
17896             if(typeof h == 'number'){
17897                  var tbh = -11;  // fixme it needs to tool bar size!
17898                 for (var i =0; i < this.toolbars.length;i++) {
17899                     // fixme - ask toolbars for heights?
17900                     tbh += this.toolbars[i].el.getHeight();
17901                     //if (this.toolbars[i].footer) {
17902                     //    tbh += this.toolbars[i].footer.el.getHeight();
17903                     //}
17904                 }
17905               
17906                 
17907                 
17908                 
17909                 
17910                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
17911                 ah -= 5; // knock a few pixes off for look..
17912                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
17913                 var eh = ah;
17914             }
17915         }
17916         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
17917         this.editorcore.onResize(ew,eh);
17918         
17919     },
17920
17921     /**
17922      * Toggles the editor between standard and source edit mode.
17923      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
17924      */
17925     toggleSourceEdit : function(sourceEditMode)
17926     {
17927         this.editorcore.toggleSourceEdit(sourceEditMode);
17928         
17929         if(this.editorcore.sourceEditMode){
17930             Roo.log('editor - showing textarea');
17931             
17932 //            Roo.log('in');
17933 //            Roo.log(this.syncValue());
17934             this.syncValue();
17935             this.inputEl().removeClass(['hide', 'x-hidden']);
17936             this.inputEl().dom.removeAttribute('tabIndex');
17937             this.inputEl().focus();
17938         }else{
17939             Roo.log('editor - hiding textarea');
17940 //            Roo.log('out')
17941 //            Roo.log(this.pushValue()); 
17942             this.pushValue();
17943             
17944             this.inputEl().addClass(['hide', 'x-hidden']);
17945             this.inputEl().dom.setAttribute('tabIndex', -1);
17946             //this.deferFocus();
17947         }
17948          
17949         if(this.resizable){
17950             this.setSize(this.wrap.getSize());
17951         }
17952         
17953         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
17954     },
17955  
17956     // private (for BoxComponent)
17957     adjustSize : Roo.BoxComponent.prototype.adjustSize,
17958
17959     // private (for BoxComponent)
17960     getResizeEl : function(){
17961         return this.wrap;
17962     },
17963
17964     // private (for BoxComponent)
17965     getPositionEl : function(){
17966         return this.wrap;
17967     },
17968
17969     // private
17970     initEvents : function(){
17971         this.originalValue = this.getValue();
17972     },
17973
17974 //    /**
17975 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
17976 //     * @method
17977 //     */
17978 //    markInvalid : Roo.emptyFn,
17979 //    /**
17980 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
17981 //     * @method
17982 //     */
17983 //    clearInvalid : Roo.emptyFn,
17984
17985     setValue : function(v){
17986         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
17987         this.editorcore.pushValue();
17988     },
17989
17990      
17991     // private
17992     deferFocus : function(){
17993         this.focus.defer(10, this);
17994     },
17995
17996     // doc'ed in Field
17997     focus : function(){
17998         this.editorcore.focus();
17999         
18000     },
18001       
18002
18003     // private
18004     onDestroy : function(){
18005         
18006         
18007         
18008         if(this.rendered){
18009             
18010             for (var i =0; i < this.toolbars.length;i++) {
18011                 // fixme - ask toolbars for heights?
18012                 this.toolbars[i].onDestroy();
18013             }
18014             
18015             this.wrap.dom.innerHTML = '';
18016             this.wrap.remove();
18017         }
18018     },
18019
18020     // private
18021     onFirstFocus : function(){
18022         //Roo.log("onFirstFocus");
18023         this.editorcore.onFirstFocus();
18024          for (var i =0; i < this.toolbars.length;i++) {
18025             this.toolbars[i].onFirstFocus();
18026         }
18027         
18028     },
18029     
18030     // private
18031     syncValue : function()
18032     {   
18033         this.editorcore.syncValue();
18034     },
18035     
18036     pushValue : function()
18037     {   
18038         this.editorcore.pushValue();
18039     }
18040      
18041     
18042     // hide stuff that is not compatible
18043     /**
18044      * @event blur
18045      * @hide
18046      */
18047     /**
18048      * @event change
18049      * @hide
18050      */
18051     /**
18052      * @event focus
18053      * @hide
18054      */
18055     /**
18056      * @event specialkey
18057      * @hide
18058      */
18059     /**
18060      * @cfg {String} fieldClass @hide
18061      */
18062     /**
18063      * @cfg {String} focusClass @hide
18064      */
18065     /**
18066      * @cfg {String} autoCreate @hide
18067      */
18068     /**
18069      * @cfg {String} inputType @hide
18070      */
18071     /**
18072      * @cfg {String} invalidClass @hide
18073      */
18074     /**
18075      * @cfg {String} invalidText @hide
18076      */
18077     /**
18078      * @cfg {String} msgFx @hide
18079      */
18080     /**
18081      * @cfg {String} validateOnBlur @hide
18082      */
18083 });
18084  
18085     
18086    
18087    
18088    
18089       
18090 Roo.namespace('Roo.bootstrap.htmleditor');
18091 /**
18092  * @class Roo.bootstrap.HtmlEditorToolbar1
18093  * Basic Toolbar
18094  * 
18095  * Usage:
18096  *
18097  new Roo.bootstrap.HtmlEditor({
18098     ....
18099     toolbars : [
18100         new Roo.bootstrap.HtmlEditorToolbar1({
18101             disable : { fonts: 1 , format: 1, ..., ... , ...],
18102             btns : [ .... ]
18103         })
18104     }
18105      
18106  * 
18107  * @cfg {Object} disable List of elements to disable..
18108  * @cfg {Array} btns List of additional buttons.
18109  * 
18110  * 
18111  * NEEDS Extra CSS? 
18112  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
18113  */
18114  
18115 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
18116 {
18117     
18118     Roo.apply(this, config);
18119     
18120     // default disabled, based on 'good practice'..
18121     this.disable = this.disable || {};
18122     Roo.applyIf(this.disable, {
18123         fontSize : true,
18124         colors : true,
18125         specialElements : true
18126     });
18127     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
18128     
18129     this.editor = config.editor;
18130     this.editorcore = config.editor.editorcore;
18131     
18132     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
18133     
18134     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
18135     // dont call parent... till later.
18136 }
18137 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
18138      
18139     bar : true,
18140     
18141     editor : false,
18142     editorcore : false,
18143     
18144     
18145     formats : [
18146         "p" ,  
18147         "h1","h2","h3","h4","h5","h6", 
18148         "pre", "code", 
18149         "abbr", "acronym", "address", "cite", "samp", "var",
18150         'div','span'
18151     ],
18152     
18153     onRender : function(ct, position)
18154     {
18155        // Roo.log("Call onRender: " + this.xtype);
18156         
18157        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
18158        Roo.log(this.el);
18159        this.el.dom.style.marginBottom = '0';
18160        var _this = this;
18161        var editorcore = this.editorcore;
18162        var editor= this.editor;
18163        
18164        var children = [];
18165        var btn = function(id,cmd , toggle, handler){
18166        
18167             var  event = toggle ? 'toggle' : 'click';
18168        
18169             var a = {
18170                 size : 'sm',
18171                 xtype: 'Button',
18172                 xns: Roo.bootstrap,
18173                 glyphicon : id,
18174                 cmd : id || cmd,
18175                 enableToggle:toggle !== false,
18176                 //html : 'submit'
18177                 pressed : toggle ? false : null,
18178                 listeners : {}
18179             }
18180             a.listeners[toggle ? 'toggle' : 'click'] = function() {
18181                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
18182             }
18183             children.push(a);
18184             return a;
18185        }
18186         
18187         var style = {
18188                 xtype: 'Button',
18189                 size : 'sm',
18190                 xns: Roo.bootstrap,
18191                 glyphicon : 'font',
18192                 //html : 'submit'
18193                 menu : {
18194                     xtype: 'Menu',
18195                     xns: Roo.bootstrap,
18196                     items:  []
18197                 }
18198         };
18199         Roo.each(this.formats, function(f) {
18200             style.menu.items.push({
18201                 xtype :'MenuItem',
18202                 xns: Roo.bootstrap,
18203                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
18204                 tagname : f,
18205                 listeners : {
18206                     click : function()
18207                     {
18208                         editorcore.insertTag(this.tagname);
18209                         editor.focus();
18210                     }
18211                 }
18212                 
18213             });
18214         });
18215          children.push(style);   
18216             
18217             
18218         btn('bold',false,true);
18219         btn('italic',false,true);
18220         btn('align-left', 'justifyleft',true);
18221         btn('align-center', 'justifycenter',true);
18222         btn('align-right' , 'justifyright',true);
18223         btn('link', false, false, function(btn) {
18224             //Roo.log("create link?");
18225             var url = prompt(this.createLinkText, this.defaultLinkValue);
18226             if(url && url != 'http:/'+'/'){
18227                 this.editorcore.relayCmd('createlink', url);
18228             }
18229         }),
18230         btn('list','insertunorderedlist',true);
18231         btn('pencil', false,true, function(btn){
18232                 Roo.log(this);
18233                 
18234                 this.toggleSourceEdit(btn.pressed);
18235         });
18236         /*
18237         var cog = {
18238                 xtype: 'Button',
18239                 size : 'sm',
18240                 xns: Roo.bootstrap,
18241                 glyphicon : 'cog',
18242                 //html : 'submit'
18243                 menu : {
18244                     xtype: 'Menu',
18245                     xns: Roo.bootstrap,
18246                     items:  []
18247                 }
18248         };
18249         
18250         cog.menu.items.push({
18251             xtype :'MenuItem',
18252             xns: Roo.bootstrap,
18253             html : Clean styles,
18254             tagname : f,
18255             listeners : {
18256                 click : function()
18257                 {
18258                     editorcore.insertTag(this.tagname);
18259                     editor.focus();
18260                 }
18261             }
18262             
18263         });
18264        */
18265         
18266          
18267        this.xtype = 'NavSimplebar';
18268         
18269         for(var i=0;i< children.length;i++) {
18270             
18271             this.buttons.add(this.addxtypeChild(children[i]));
18272             
18273         }
18274         
18275         editor.on('editorevent', this.updateToolbar, this);
18276     },
18277     onBtnClick : function(id)
18278     {
18279        this.editorcore.relayCmd(id);
18280        this.editorcore.focus();
18281     },
18282     
18283     /**
18284      * Protected method that will not generally be called directly. It triggers
18285      * a toolbar update by reading the markup state of the current selection in the editor.
18286      */
18287     updateToolbar: function(){
18288
18289         if(!this.editorcore.activated){
18290             this.editor.onFirstFocus(); // is this neeed?
18291             return;
18292         }
18293
18294         var btns = this.buttons; 
18295         var doc = this.editorcore.doc;
18296         btns.get('bold').setActive(doc.queryCommandState('bold'));
18297         btns.get('italic').setActive(doc.queryCommandState('italic'));
18298         //btns.get('underline').setActive(doc.queryCommandState('underline'));
18299         
18300         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
18301         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
18302         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
18303         
18304         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
18305         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
18306          /*
18307         
18308         var ans = this.editorcore.getAllAncestors();
18309         if (this.formatCombo) {
18310             
18311             
18312             var store = this.formatCombo.store;
18313             this.formatCombo.setValue("");
18314             for (var i =0; i < ans.length;i++) {
18315                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
18316                     // select it..
18317                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
18318                     break;
18319                 }
18320             }
18321         }
18322         
18323         
18324         
18325         // hides menus... - so this cant be on a menu...
18326         Roo.bootstrap.MenuMgr.hideAll();
18327         */
18328         Roo.bootstrap.MenuMgr.hideAll();
18329         //this.editorsyncValue();
18330     },
18331     onFirstFocus: function() {
18332         this.buttons.each(function(item){
18333            item.enable();
18334         });
18335     },
18336     toggleSourceEdit : function(sourceEditMode){
18337         
18338           
18339         if(sourceEditMode){
18340             Roo.log("disabling buttons");
18341            this.buttons.each( function(item){
18342                 if(item.cmd != 'pencil'){
18343                     item.disable();
18344                 }
18345             });
18346           
18347         }else{
18348             Roo.log("enabling buttons");
18349             if(this.editorcore.initialized){
18350                 this.buttons.each( function(item){
18351                     item.enable();
18352                 });
18353             }
18354             
18355         }
18356         Roo.log("calling toggole on editor");
18357         // tell the editor that it's been pressed..
18358         this.editor.toggleSourceEdit(sourceEditMode);
18359        
18360     }
18361 });
18362
18363
18364
18365
18366
18367 /**
18368  * @class Roo.bootstrap.Table.AbstractSelectionModel
18369  * @extends Roo.util.Observable
18370  * Abstract base class for grid SelectionModels.  It provides the interface that should be
18371  * implemented by descendant classes.  This class should not be directly instantiated.
18372  * @constructor
18373  */
18374 Roo.bootstrap.Table.AbstractSelectionModel = function(){
18375     this.locked = false;
18376     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
18377 };
18378
18379
18380 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
18381     /** @ignore Called by the grid automatically. Do not call directly. */
18382     init : function(grid){
18383         this.grid = grid;
18384         this.initEvents();
18385     },
18386
18387     /**
18388      * Locks the selections.
18389      */
18390     lock : function(){
18391         this.locked = true;
18392     },
18393
18394     /**
18395      * Unlocks the selections.
18396      */
18397     unlock : function(){
18398         this.locked = false;
18399     },
18400
18401     /**
18402      * Returns true if the selections are locked.
18403      * @return {Boolean}
18404      */
18405     isLocked : function(){
18406         return this.locked;
18407     }
18408 });
18409 /**
18410  * @extends Roo.bootstrap.Table.AbstractSelectionModel
18411  * @class Roo.bootstrap.Table.RowSelectionModel
18412  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
18413  * It supports multiple selections and keyboard selection/navigation. 
18414  * @constructor
18415  * @param {Object} config
18416  */
18417
18418 Roo.bootstrap.Table.RowSelectionModel = function(config){
18419     Roo.apply(this, config);
18420     this.selections = new Roo.util.MixedCollection(false, function(o){
18421         return o.id;
18422     });
18423
18424     this.last = false;
18425     this.lastActive = false;
18426
18427     this.addEvents({
18428         /**
18429              * @event selectionchange
18430              * Fires when the selection changes
18431              * @param {SelectionModel} this
18432              */
18433             "selectionchange" : true,
18434         /**
18435              * @event afterselectionchange
18436              * Fires after the selection changes (eg. by key press or clicking)
18437              * @param {SelectionModel} this
18438              */
18439             "afterselectionchange" : true,
18440         /**
18441              * @event beforerowselect
18442              * Fires when a row is selected being selected, return false to cancel.
18443              * @param {SelectionModel} this
18444              * @param {Number} rowIndex The selected index
18445              * @param {Boolean} keepExisting False if other selections will be cleared
18446              */
18447             "beforerowselect" : true,
18448         /**
18449              * @event rowselect
18450              * Fires when a row is selected.
18451              * @param {SelectionModel} this
18452              * @param {Number} rowIndex The selected index
18453              * @param {Roo.data.Record} r The record
18454              */
18455             "rowselect" : true,
18456         /**
18457              * @event rowdeselect
18458              * Fires when a row is deselected.
18459              * @param {SelectionModel} this
18460              * @param {Number} rowIndex The selected index
18461              */
18462         "rowdeselect" : true
18463     });
18464     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
18465     this.locked = false;
18466 };
18467
18468 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
18469     /**
18470      * @cfg {Boolean} singleSelect
18471      * True to allow selection of only one row at a time (defaults to false)
18472      */
18473     singleSelect : false,
18474
18475     // private
18476     initEvents : function(){
18477
18478         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
18479             this.grid.on("mousedown", this.handleMouseDown, this);
18480         }else{ // allow click to work like normal
18481             this.grid.on("rowclick", this.handleDragableRowClick, this);
18482         }
18483
18484         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
18485             "up" : function(e){
18486                 if(!e.shiftKey){
18487                     this.selectPrevious(e.shiftKey);
18488                 }else if(this.last !== false && this.lastActive !== false){
18489                     var last = this.last;
18490                     this.selectRange(this.last,  this.lastActive-1);
18491                     this.grid.getView().focusRow(this.lastActive);
18492                     if(last !== false){
18493                         this.last = last;
18494                     }
18495                 }else{
18496                     this.selectFirstRow();
18497                 }
18498                 this.fireEvent("afterselectionchange", this);
18499             },
18500             "down" : function(e){
18501                 if(!e.shiftKey){
18502                     this.selectNext(e.shiftKey);
18503                 }else if(this.last !== false && this.lastActive !== false){
18504                     var last = this.last;
18505                     this.selectRange(this.last,  this.lastActive+1);
18506                     this.grid.getView().focusRow(this.lastActive);
18507                     if(last !== false){
18508                         this.last = last;
18509                     }
18510                 }else{
18511                     this.selectFirstRow();
18512                 }
18513                 this.fireEvent("afterselectionchange", this);
18514             },
18515             scope: this
18516         });
18517
18518         var view = this.grid.view;
18519         view.on("refresh", this.onRefresh, this);
18520         view.on("rowupdated", this.onRowUpdated, this);
18521         view.on("rowremoved", this.onRemove, this);
18522     },
18523
18524     // private
18525     onRefresh : function(){
18526         var ds = this.grid.dataSource, i, v = this.grid.view;
18527         var s = this.selections;
18528         s.each(function(r){
18529             if((i = ds.indexOfId(r.id)) != -1){
18530                 v.onRowSelect(i);
18531             }else{
18532                 s.remove(r);
18533             }
18534         });
18535     },
18536
18537     // private
18538     onRemove : function(v, index, r){
18539         this.selections.remove(r);
18540     },
18541
18542     // private
18543     onRowUpdated : function(v, index, r){
18544         if(this.isSelected(r)){
18545             v.onRowSelect(index);
18546         }
18547     },
18548
18549     /**
18550      * Select records.
18551      * @param {Array} records The records to select
18552      * @param {Boolean} keepExisting (optional) True to keep existing selections
18553      */
18554     selectRecords : function(records, keepExisting){
18555         if(!keepExisting){
18556             this.clearSelections();
18557         }
18558         var ds = this.grid.dataSource;
18559         for(var i = 0, len = records.length; i < len; i++){
18560             this.selectRow(ds.indexOf(records[i]), true);
18561         }
18562     },
18563
18564     /**
18565      * Gets the number of selected rows.
18566      * @return {Number}
18567      */
18568     getCount : function(){
18569         return this.selections.length;
18570     },
18571
18572     /**
18573      * Selects the first row in the grid.
18574      */
18575     selectFirstRow : function(){
18576         this.selectRow(0);
18577     },
18578
18579     /**
18580      * Select the last row.
18581      * @param {Boolean} keepExisting (optional) True to keep existing selections
18582      */
18583     selectLastRow : function(keepExisting){
18584         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
18585     },
18586
18587     /**
18588      * Selects the row immediately following the last selected row.
18589      * @param {Boolean} keepExisting (optional) True to keep existing selections
18590      */
18591     selectNext : function(keepExisting){
18592         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
18593             this.selectRow(this.last+1, keepExisting);
18594             this.grid.getView().focusRow(this.last);
18595         }
18596     },
18597
18598     /**
18599      * Selects the row that precedes the last selected row.
18600      * @param {Boolean} keepExisting (optional) True to keep existing selections
18601      */
18602     selectPrevious : function(keepExisting){
18603         if(this.last){
18604             this.selectRow(this.last-1, keepExisting);
18605             this.grid.getView().focusRow(this.last);
18606         }
18607     },
18608
18609     /**
18610      * Returns the selected records
18611      * @return {Array} Array of selected records
18612      */
18613     getSelections : function(){
18614         return [].concat(this.selections.items);
18615     },
18616
18617     /**
18618      * Returns the first selected record.
18619      * @return {Record}
18620      */
18621     getSelected : function(){
18622         return this.selections.itemAt(0);
18623     },
18624
18625
18626     /**
18627      * Clears all selections.
18628      */
18629     clearSelections : function(fast){
18630         if(this.locked) return;
18631         if(fast !== true){
18632             var ds = this.grid.dataSource;
18633             var s = this.selections;
18634             s.each(function(r){
18635                 this.deselectRow(ds.indexOfId(r.id));
18636             }, this);
18637             s.clear();
18638         }else{
18639             this.selections.clear();
18640         }
18641         this.last = false;
18642     },
18643
18644
18645     /**
18646      * Selects all rows.
18647      */
18648     selectAll : function(){
18649         if(this.locked) return;
18650         this.selections.clear();
18651         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
18652             this.selectRow(i, true);
18653         }
18654     },
18655
18656     /**
18657      * Returns True if there is a selection.
18658      * @return {Boolean}
18659      */
18660     hasSelection : function(){
18661         return this.selections.length > 0;
18662     },
18663
18664     /**
18665      * Returns True if the specified row is selected.
18666      * @param {Number/Record} record The record or index of the record to check
18667      * @return {Boolean}
18668      */
18669     isSelected : function(index){
18670         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
18671         return (r && this.selections.key(r.id) ? true : false);
18672     },
18673
18674     /**
18675      * Returns True if the specified record id is selected.
18676      * @param {String} id The id of record to check
18677      * @return {Boolean}
18678      */
18679     isIdSelected : function(id){
18680         return (this.selections.key(id) ? true : false);
18681     },
18682
18683     // private
18684     handleMouseDown : function(e, t){
18685         var view = this.grid.getView(), rowIndex;
18686         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
18687             return;
18688         };
18689         if(e.shiftKey && this.last !== false){
18690             var last = this.last;
18691             this.selectRange(last, rowIndex, e.ctrlKey);
18692             this.last = last; // reset the last
18693             view.focusRow(rowIndex);
18694         }else{
18695             var isSelected = this.isSelected(rowIndex);
18696             if(e.button !== 0 && isSelected){
18697                 view.focusRow(rowIndex);
18698             }else if(e.ctrlKey && isSelected){
18699                 this.deselectRow(rowIndex);
18700             }else if(!isSelected){
18701                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
18702                 view.focusRow(rowIndex);
18703             }
18704         }
18705         this.fireEvent("afterselectionchange", this);
18706     },
18707     // private
18708     handleDragableRowClick :  function(grid, rowIndex, e) 
18709     {
18710         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
18711             this.selectRow(rowIndex, false);
18712             grid.view.focusRow(rowIndex);
18713              this.fireEvent("afterselectionchange", this);
18714         }
18715     },
18716     
18717     /**
18718      * Selects multiple rows.
18719      * @param {Array} rows Array of the indexes of the row to select
18720      * @param {Boolean} keepExisting (optional) True to keep existing selections
18721      */
18722     selectRows : function(rows, keepExisting){
18723         if(!keepExisting){
18724             this.clearSelections();
18725         }
18726         for(var i = 0, len = rows.length; i < len; i++){
18727             this.selectRow(rows[i], true);
18728         }
18729     },
18730
18731     /**
18732      * Selects a range of rows. All rows in between startRow and endRow are also selected.
18733      * @param {Number} startRow The index of the first row in the range
18734      * @param {Number} endRow The index of the last row in the range
18735      * @param {Boolean} keepExisting (optional) True to retain existing selections
18736      */
18737     selectRange : function(startRow, endRow, keepExisting){
18738         if(this.locked) return;
18739         if(!keepExisting){
18740             this.clearSelections();
18741         }
18742         if(startRow <= endRow){
18743             for(var i = startRow; i <= endRow; i++){
18744                 this.selectRow(i, true);
18745             }
18746         }else{
18747             for(var i = startRow; i >= endRow; i--){
18748                 this.selectRow(i, true);
18749             }
18750         }
18751     },
18752
18753     /**
18754      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
18755      * @param {Number} startRow The index of the first row in the range
18756      * @param {Number} endRow The index of the last row in the range
18757      */
18758     deselectRange : function(startRow, endRow, preventViewNotify){
18759         if(this.locked) return;
18760         for(var i = startRow; i <= endRow; i++){
18761             this.deselectRow(i, preventViewNotify);
18762         }
18763     },
18764
18765     /**
18766      * Selects a row.
18767      * @param {Number} row The index of the row to select
18768      * @param {Boolean} keepExisting (optional) True to keep existing selections
18769      */
18770     selectRow : function(index, keepExisting, preventViewNotify){
18771         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
18772         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
18773             if(!keepExisting || this.singleSelect){
18774                 this.clearSelections();
18775             }
18776             var r = this.grid.dataSource.getAt(index);
18777             this.selections.add(r);
18778             this.last = this.lastActive = index;
18779             if(!preventViewNotify){
18780                 this.grid.getView().onRowSelect(index);
18781             }
18782             this.fireEvent("rowselect", this, index, r);
18783             this.fireEvent("selectionchange", this);
18784         }
18785     },
18786
18787     /**
18788      * Deselects a row.
18789      * @param {Number} row The index of the row to deselect
18790      */
18791     deselectRow : function(index, preventViewNotify){
18792         if(this.locked) return;
18793         if(this.last == index){
18794             this.last = false;
18795         }
18796         if(this.lastActive == index){
18797             this.lastActive = false;
18798         }
18799         var r = this.grid.dataSource.getAt(index);
18800         this.selections.remove(r);
18801         if(!preventViewNotify){
18802             this.grid.getView().onRowDeselect(index);
18803         }
18804         this.fireEvent("rowdeselect", this, index);
18805         this.fireEvent("selectionchange", this);
18806     },
18807
18808     // private
18809     restoreLast : function(){
18810         if(this._last){
18811             this.last = this._last;
18812         }
18813     },
18814
18815     // private
18816     acceptsNav : function(row, col, cm){
18817         return !cm.isHidden(col) && cm.isCellEditable(col, row);
18818     },
18819
18820     // private
18821     onEditorKey : function(field, e){
18822         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
18823         if(k == e.TAB){
18824             e.stopEvent();
18825             ed.completeEdit();
18826             if(e.shiftKey){
18827                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
18828             }else{
18829                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
18830             }
18831         }else if(k == e.ENTER && !e.ctrlKey){
18832             e.stopEvent();
18833             ed.completeEdit();
18834             if(e.shiftKey){
18835                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
18836             }else{
18837                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
18838             }
18839         }else if(k == e.ESC){
18840             ed.cancelEdit();
18841         }
18842         if(newCell){
18843             g.startEditing(newCell[0], newCell[1]);
18844         }
18845     }
18846 });/*
18847  * Based on:
18848  * Ext JS Library 1.1.1
18849  * Copyright(c) 2006-2007, Ext JS, LLC.
18850  *
18851  * Originally Released Under LGPL - original licence link has changed is not relivant.
18852  *
18853  * Fork - LGPL
18854  * <script type="text/javascript">
18855  */
18856  
18857 /**
18858  * @class Roo.bootstrap.PagingToolbar
18859  * @extends Roo.Row
18860  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
18861  * @constructor
18862  * Create a new PagingToolbar
18863  * @param {Object} config The config object
18864  */
18865 Roo.bootstrap.PagingToolbar = function(config)
18866 {
18867     // old args format still supported... - xtype is prefered..
18868         // created from xtype...
18869     var ds = config.dataSource;
18870     this.toolbarItems = [];
18871     if (config.items) {
18872         this.toolbarItems = config.items;
18873 //        config.items = [];
18874     }
18875     
18876     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
18877     this.ds = ds;
18878     this.cursor = 0;
18879     if (ds) { 
18880         this.bind(ds);
18881     }
18882     
18883     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
18884     
18885 };
18886
18887 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
18888     /**
18889      * @cfg {Roo.data.Store} dataSource
18890      * The underlying data store providing the paged data
18891      */
18892     /**
18893      * @cfg {String/HTMLElement/Element} container
18894      * container The id or element that will contain the toolbar
18895      */
18896     /**
18897      * @cfg {Boolean} displayInfo
18898      * True to display the displayMsg (defaults to false)
18899      */
18900     /**
18901      * @cfg {Number} pageSize
18902      * The number of records to display per page (defaults to 20)
18903      */
18904     pageSize: 20,
18905     /**
18906      * @cfg {String} displayMsg
18907      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
18908      */
18909     displayMsg : 'Displaying {0} - {1} of {2}',
18910     /**
18911      * @cfg {String} emptyMsg
18912      * The message to display when no records are found (defaults to "No data to display")
18913      */
18914     emptyMsg : 'No data to display',
18915     /**
18916      * Customizable piece of the default paging text (defaults to "Page")
18917      * @type String
18918      */
18919     beforePageText : "Page",
18920     /**
18921      * Customizable piece of the default paging text (defaults to "of %0")
18922      * @type String
18923      */
18924     afterPageText : "of {0}",
18925     /**
18926      * Customizable piece of the default paging text (defaults to "First Page")
18927      * @type String
18928      */
18929     firstText : "First Page",
18930     /**
18931      * Customizable piece of the default paging text (defaults to "Previous Page")
18932      * @type String
18933      */
18934     prevText : "Previous Page",
18935     /**
18936      * Customizable piece of the default paging text (defaults to "Next Page")
18937      * @type String
18938      */
18939     nextText : "Next Page",
18940     /**
18941      * Customizable piece of the default paging text (defaults to "Last Page")
18942      * @type String
18943      */
18944     lastText : "Last Page",
18945     /**
18946      * Customizable piece of the default paging text (defaults to "Refresh")
18947      * @type String
18948      */
18949     refreshText : "Refresh",
18950
18951     buttons : false,
18952     // private
18953     onRender : function(ct, position) 
18954     {
18955         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
18956         this.navgroup.parentId = this.id;
18957         this.navgroup.onRender(this.el, null);
18958         // add the buttons to the navgroup
18959         
18960         if(this.displayInfo){
18961             Roo.log(this.el.select('ul.navbar-nav',true).first());
18962             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
18963             this.displayEl = this.el.select('.x-paging-info', true).first();
18964 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
18965 //            this.displayEl = navel.el.select('span',true).first();
18966         }
18967         
18968         var _this = this;
18969         
18970         if(this.buttons){
18971             Roo.each(_this.buttons, function(e){
18972                Roo.factory(e).onRender(_this.el, null);
18973             });
18974         }
18975             
18976         Roo.each(_this.toolbarItems, function(e) {
18977             _this.navgroup.addItem(e);
18978         });
18979         
18980         this.first = this.navgroup.addItem({
18981             tooltip: this.firstText,
18982             cls: "prev",
18983             icon : 'fa fa-backward',
18984             disabled: true,
18985             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
18986         });
18987         
18988         this.prev =  this.navgroup.addItem({
18989             tooltip: this.prevText,
18990             cls: "prev",
18991             icon : 'fa fa-step-backward',
18992             disabled: true,
18993             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
18994         });
18995     //this.addSeparator();
18996         
18997         
18998         var field = this.navgroup.addItem( {
18999             tagtype : 'span',
19000             cls : 'x-paging-position',
19001             
19002             html : this.beforePageText  +
19003                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
19004                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
19005          } ); //?? escaped?
19006         
19007         this.field = field.el.select('input', true).first();
19008         this.field.on("keydown", this.onPagingKeydown, this);
19009         this.field.on("focus", function(){this.dom.select();});
19010     
19011     
19012         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
19013         //this.field.setHeight(18);
19014         //this.addSeparator();
19015         this.next = this.navgroup.addItem({
19016             tooltip: this.nextText,
19017             cls: "next",
19018             html : ' <i class="fa fa-step-forward">',
19019             disabled: true,
19020             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
19021         });
19022         this.last = this.navgroup.addItem({
19023             tooltip: this.lastText,
19024             icon : 'fa fa-forward',
19025             cls: "next",
19026             disabled: true,
19027             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
19028         });
19029     //this.addSeparator();
19030         this.loading = this.navgroup.addItem({
19031             tooltip: this.refreshText,
19032             icon: 'fa fa-refresh',
19033             
19034             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
19035         });
19036
19037     },
19038
19039     // private
19040     updateInfo : function(){
19041         if(this.displayEl){
19042             var count = this.ds.getCount();
19043             var msg = count == 0 ?
19044                 this.emptyMsg :
19045                 String.format(
19046                     this.displayMsg,
19047                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
19048                 );
19049             this.displayEl.update(msg);
19050         }
19051     },
19052
19053     // private
19054     onLoad : function(ds, r, o){
19055        this.cursor = o.params ? o.params.start : 0;
19056        var d = this.getPageData(),
19057             ap = d.activePage,
19058             ps = d.pages;
19059         
19060        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
19061        this.field.dom.value = ap;
19062        this.first.setDisabled(ap == 1);
19063        this.prev.setDisabled(ap == 1);
19064        this.next.setDisabled(ap == ps);
19065        this.last.setDisabled(ap == ps);
19066        this.loading.enable();
19067        this.updateInfo();
19068     },
19069
19070     // private
19071     getPageData : function(){
19072         var total = this.ds.getTotalCount();
19073         return {
19074             total : total,
19075             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
19076             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
19077         };
19078     },
19079
19080     // private
19081     onLoadError : function(){
19082         this.loading.enable();
19083     },
19084
19085     // private
19086     onPagingKeydown : function(e){
19087         var k = e.getKey();
19088         var d = this.getPageData();
19089         if(k == e.RETURN){
19090             var v = this.field.dom.value, pageNum;
19091             if(!v || isNaN(pageNum = parseInt(v, 10))){
19092                 this.field.dom.value = d.activePage;
19093                 return;
19094             }
19095             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
19096             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
19097             e.stopEvent();
19098         }
19099         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))
19100         {
19101           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
19102           this.field.dom.value = pageNum;
19103           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
19104           e.stopEvent();
19105         }
19106         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
19107         {
19108           var v = this.field.dom.value, pageNum; 
19109           var increment = (e.shiftKey) ? 10 : 1;
19110           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
19111             increment *= -1;
19112           if(!v || isNaN(pageNum = parseInt(v, 10))) {
19113             this.field.dom.value = d.activePage;
19114             return;
19115           }
19116           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
19117           {
19118             this.field.dom.value = parseInt(v, 10) + increment;
19119             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
19120             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
19121           }
19122           e.stopEvent();
19123         }
19124     },
19125
19126     // private
19127     beforeLoad : function(){
19128         if(this.loading){
19129             this.loading.disable();
19130         }
19131     },
19132
19133     // private
19134     onClick : function(which){
19135         var ds = this.ds;
19136         if (!ds) {
19137             return;
19138         }
19139         switch(which){
19140             case "first":
19141                 ds.load({params:{start: 0, limit: this.pageSize}});
19142             break;
19143             case "prev":
19144                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
19145             break;
19146             case "next":
19147                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
19148             break;
19149             case "last":
19150                 var total = ds.getTotalCount();
19151                 var extra = total % this.pageSize;
19152                 var lastStart = extra ? (total - extra) : total-this.pageSize;
19153                 ds.load({params:{start: lastStart, limit: this.pageSize}});
19154             break;
19155             case "refresh":
19156                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
19157             break;
19158         }
19159     },
19160
19161     /**
19162      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
19163      * @param {Roo.data.Store} store The data store to unbind
19164      */
19165     unbind : function(ds){
19166         ds.un("beforeload", this.beforeLoad, this);
19167         ds.un("load", this.onLoad, this);
19168         ds.un("loadexception", this.onLoadError, this);
19169         ds.un("remove", this.updateInfo, this);
19170         ds.un("add", this.updateInfo, this);
19171         this.ds = undefined;
19172     },
19173
19174     /**
19175      * Binds the paging toolbar to the specified {@link Roo.data.Store}
19176      * @param {Roo.data.Store} store The data store to bind
19177      */
19178     bind : function(ds){
19179         ds.on("beforeload", this.beforeLoad, this);
19180         ds.on("load", this.onLoad, this);
19181         ds.on("loadexception", this.onLoadError, this);
19182         ds.on("remove", this.updateInfo, this);
19183         ds.on("add", this.updateInfo, this);
19184         this.ds = ds;
19185     }
19186 });/*
19187  * - LGPL
19188  *
19189  * element
19190  * 
19191  */
19192
19193 /**
19194  * @class Roo.bootstrap.MessageBar
19195  * @extends Roo.bootstrap.Component
19196  * Bootstrap MessageBar class
19197  * @cfg {String} html contents of the MessageBar
19198  * @cfg {String} weight (info | success | warning | danger) default info
19199  * @cfg {String} beforeClass insert the bar before the given class
19200  * @cfg {Boolean} closable (true | false) default false
19201  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
19202  * 
19203  * @constructor
19204  * Create a new Element
19205  * @param {Object} config The config object
19206  */
19207
19208 Roo.bootstrap.MessageBar = function(config){
19209     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
19210 };
19211
19212 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
19213     
19214     html: '',
19215     weight: 'info',
19216     closable: false,
19217     fixed: false,
19218     beforeClass: 'bootstrap-sticky-wrap',
19219     
19220     getAutoCreate : function(){
19221         
19222         var cfg = {
19223             tag: 'div',
19224             cls: 'alert alert-dismissable alert-' + this.weight,
19225             cn: [
19226                 {
19227                     tag: 'span',
19228                     cls: 'message',
19229                     html: this.html || ''
19230                 }
19231             ]
19232         }
19233         
19234         if(this.fixed){
19235             cfg.cls += ' alert-messages-fixed';
19236         }
19237         
19238         if(this.closable){
19239             cfg.cn.push({
19240                 tag: 'button',
19241                 cls: 'close',
19242                 html: 'x'
19243             });
19244         }
19245         
19246         return cfg;
19247     },
19248     
19249     onRender : function(ct, position)
19250     {
19251         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19252         
19253         if(!this.el){
19254             var cfg = Roo.apply({},  this.getAutoCreate());
19255             cfg.id = Roo.id();
19256             
19257             if (this.cls) {
19258                 cfg.cls += ' ' + this.cls;
19259             }
19260             if (this.style) {
19261                 cfg.style = this.style;
19262             }
19263             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
19264             
19265             this.el.setVisibilityMode(Roo.Element.DISPLAY);
19266         }
19267         
19268         this.el.select('>button.close').on('click', this.hide, this);
19269         
19270     },
19271     
19272     show : function()
19273     {
19274         if (!this.rendered) {
19275             this.render();
19276         }
19277         
19278         this.el.show();
19279         
19280         this.fireEvent('show', this);
19281         
19282     },
19283     
19284     hide : function()
19285     {
19286         if (!this.rendered) {
19287             this.render();
19288         }
19289         
19290         this.el.hide();
19291         
19292         this.fireEvent('hide', this);
19293     },
19294     
19295     update : function()
19296     {
19297 //        var e = this.el.dom.firstChild;
19298 //        
19299 //        if(this.closable){
19300 //            e = e.nextSibling;
19301 //        }
19302 //        
19303 //        e.data = this.html || '';
19304
19305         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
19306     }
19307    
19308 });
19309
19310  
19311
19312      /*
19313  * - LGPL
19314  *
19315  * Graph
19316  * 
19317  */
19318
19319
19320 /**
19321  * @class Roo.bootstrap.Graph
19322  * @extends Roo.bootstrap.Component
19323  * Bootstrap Graph class
19324 > Prameters
19325  -sm {number} sm 4
19326  -md {number} md 5
19327  @cfg {String} graphtype  bar | vbar | pie
19328  @cfg {number} g_x coodinator | centre x (pie)
19329  @cfg {number} g_y coodinator | centre y (pie)
19330  @cfg {number} g_r radius (pie)
19331  @cfg {number} g_height height of the chart (respected by all elements in the set)
19332  @cfg {number} g_width width of the chart (respected by all elements in the set)
19333  @cfg {Object} title The title of the chart
19334     
19335  -{Array}  values
19336  -opts (object) options for the chart 
19337      o {
19338      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
19339      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
19340      o vgutter (number)
19341      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.
19342      o stacked (boolean) whether or not to tread values as in a stacked bar chart
19343      o to
19344      o stretch (boolean)
19345      o }
19346  -opts (object) options for the pie
19347      o{
19348      o cut
19349      o startAngle (number)
19350      o endAngle (number)
19351      } 
19352  *
19353  * @constructor
19354  * Create a new Input
19355  * @param {Object} config The config object
19356  */
19357
19358 Roo.bootstrap.Graph = function(config){
19359     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
19360     
19361     this.addEvents({
19362         // img events
19363         /**
19364          * @event click
19365          * The img click event for the img.
19366          * @param {Roo.EventObject} e
19367          */
19368         "click" : true
19369     });
19370 };
19371
19372 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
19373     
19374     sm: 4,
19375     md: 5,
19376     graphtype: 'bar',
19377     g_height: 250,
19378     g_width: 400,
19379     g_x: 50,
19380     g_y: 50,
19381     g_r: 30,
19382     opts:{
19383         //g_colors: this.colors,
19384         g_type: 'soft',
19385         g_gutter: '20%'
19386
19387     },
19388     title : false,
19389
19390     getAutoCreate : function(){
19391         
19392         var cfg = {
19393             tag: 'div',
19394             html : null
19395         }
19396         
19397         
19398         return  cfg;
19399     },
19400
19401     onRender : function(ct,position){
19402         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
19403         this.raphael = Raphael(this.el.dom);
19404         
19405                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19406                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19407                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19408                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
19409                 /*
19410                 r.text(160, 10, "Single Series Chart").attr(txtattr);
19411                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
19412                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
19413                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
19414                 
19415                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
19416                 r.barchart(330, 10, 300, 220, data1);
19417                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
19418                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
19419                 */
19420                 
19421                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19422                 // r.barchart(30, 30, 560, 250,  xdata, {
19423                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
19424                 //     axis : "0 0 1 1",
19425                 //     axisxlabels :  xdata
19426                 //     //yvalues : cols,
19427                    
19428                 // });
19429 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19430 //        
19431 //        this.load(null,xdata,{
19432 //                axis : "0 0 1 1",
19433 //                axisxlabels :  xdata
19434 //                });
19435
19436     },
19437
19438     load : function(graphtype,xdata,opts){
19439         this.raphael.clear();
19440         if(!graphtype) {
19441             graphtype = this.graphtype;
19442         }
19443         if(!opts){
19444             opts = this.opts;
19445         }
19446         var r = this.raphael,
19447             fin = function () {
19448                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
19449             },
19450             fout = function () {
19451                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
19452             },
19453             pfin = function() {
19454                 this.sector.stop();
19455                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
19456
19457                 if (this.label) {
19458                     this.label[0].stop();
19459                     this.label[0].attr({ r: 7.5 });
19460                     this.label[1].attr({ "font-weight": 800 });
19461                 }
19462             },
19463             pfout = function() {
19464                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
19465
19466                 if (this.label) {
19467                     this.label[0].animate({ r: 5 }, 500, "bounce");
19468                     this.label[1].attr({ "font-weight": 400 });
19469                 }
19470             };
19471
19472         switch(graphtype){
19473             case 'bar':
19474                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
19475                 break;
19476             case 'hbar':
19477                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
19478                 break;
19479             case 'pie':
19480 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
19481 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
19482 //            
19483                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
19484                 
19485                 break;
19486
19487         }
19488         
19489         if(this.title){
19490             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
19491         }
19492         
19493     },
19494     
19495     setTitle: function(o)
19496     {
19497         this.title = o;
19498     },
19499     
19500     initEvents: function() {
19501         
19502         if(!this.href){
19503             this.el.on('click', this.onClick, this);
19504         }
19505     },
19506     
19507     onClick : function(e)
19508     {
19509         Roo.log('img onclick');
19510         this.fireEvent('click', this, e);
19511     }
19512    
19513 });
19514
19515  
19516 /*
19517  * - LGPL
19518  *
19519  * numberBox
19520  * 
19521  */
19522 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19523
19524 /**
19525  * @class Roo.bootstrap.dash.NumberBox
19526  * @extends Roo.bootstrap.Component
19527  * Bootstrap NumberBox class
19528  * @cfg {String} bgcolor Box background color, such as (aqua | green | yellow | red etc..) Default aqua
19529  * @cfg {String} headline Box headline
19530  * @cfg {String} content Box content
19531  * @cfg {String} icon Box icon
19532  * @cfg {String} footer Footer text
19533  * @cfg {String} fhref Footer href
19534  * 
19535  * @constructor
19536  * Create a new NumberBox
19537  * @param {Object} config The config object
19538  */
19539
19540
19541 Roo.bootstrap.dash.NumberBox = function(config){
19542     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
19543     
19544 };
19545
19546 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
19547     
19548     bgcolor : 'aqua',
19549     headline : '',
19550     content : '',
19551     icon : '',
19552     footer : '',
19553     fhref : '',
19554     ficon : '',
19555     
19556     getAutoCreate : function(){
19557         
19558         var cfg = {
19559             tag : 'div',
19560             cls : 'small-box bg-' + this.bgcolor,
19561             cn : [
19562                 {
19563                     tag : 'div',
19564                     cls : 'inner',
19565                     cn :[
19566                         {
19567                             tag : 'h3',
19568                             cls : 'roo-headline',
19569                             html : this.headline
19570                         },
19571                         {
19572                             tag : 'p',
19573                             cls : 'roo-content',
19574                             html : this.content
19575                         }
19576                     ]
19577                 }
19578             ]
19579         }
19580         
19581         if(this.icon){
19582             cfg.cn.push({
19583                 tag : 'div',
19584                 cls : 'icon',
19585                 cn :[
19586                     {
19587                         tag : 'i',
19588                         cls : 'ion ' + this.icon
19589                     }
19590                 ]
19591             });
19592         }
19593         
19594         if(this.footer){
19595             var footer = {
19596                 tag : 'a',
19597                 cls : 'small-box-footer',
19598                 href : this.fhref || '#',
19599                 html : this.footer
19600             };
19601             
19602             cfg.cn.push(footer);
19603             
19604         }
19605         
19606         return  cfg;
19607     },
19608
19609     onRender : function(ct,position){
19610         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
19611
19612
19613        
19614                 
19615     },
19616
19617     setHeadline: function (value)
19618     {
19619         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
19620     },
19621     
19622     setFooter: function (value, href)
19623     {
19624         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
19625         
19626         if(href){
19627             this.el.select('a.small-box-footer',true).first().attr('href', href);
19628         }
19629         
19630     },
19631
19632     setContent: function (value)
19633     {
19634         this.el.select('.roo-content',true).first().dom.innerHTML = value;
19635     },
19636
19637     initEvents: function() 
19638     {   
19639         
19640     }
19641     
19642 });
19643
19644  
19645 /*
19646  * - LGPL
19647  *
19648  * TabBox
19649  * 
19650  */
19651 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19652
19653 /**
19654  * @class Roo.bootstrap.dash.TabBox
19655  * @extends Roo.bootstrap.Component
19656  * Bootstrap TabBox class
19657  * @cfg {String} title Title of the TabBox
19658  * @cfg {String} icon Icon of the TabBox
19659  * @cfg {Boolean} showtabs (true|false) show the tabs default true
19660  * 
19661  * @constructor
19662  * Create a new TabBox
19663  * @param {Object} config The config object
19664  */
19665
19666
19667 Roo.bootstrap.dash.TabBox = function(config){
19668     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
19669     this.addEvents({
19670         // raw events
19671         /**
19672          * @event addpane
19673          * When a pane is added
19674          * @param {Roo.bootstrap.dash.TabPane} pane
19675          */
19676         "addpane" : true
19677          
19678     });
19679 };
19680
19681 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
19682
19683     title : '',
19684     icon : false,
19685     showtabs : true,
19686     
19687     getChildContainer : function()
19688     {
19689         return this.el.select('.tab-content', true).first();
19690     },
19691     
19692     getAutoCreate : function(){
19693         
19694         var header = {
19695             tag: 'li',
19696             cls: 'pull-left header',
19697             html: this.title,
19698             cn : []
19699         };
19700         
19701         if(this.icon){
19702             header.cn.push({
19703                 tag: 'i',
19704                 cls: 'fa ' + this.icon
19705             });
19706         }
19707         
19708         
19709         var cfg = {
19710             tag: 'div',
19711             cls: 'nav-tabs-custom',
19712             cn: [
19713                 {
19714                     tag: 'ul',
19715                     cls: 'nav nav-tabs pull-right',
19716                     cn: [
19717                         header
19718                     ]
19719                 },
19720                 {
19721                     tag: 'div',
19722                     cls: 'tab-content no-padding',
19723                     cn: []
19724                 }
19725             ]
19726         }
19727
19728         return  cfg;
19729     },
19730     initEvents : function()
19731     {
19732         //Roo.log('add add pane handler');
19733         this.on('addpane', this.onAddPane, this);
19734     },
19735      /**
19736      * Updates the box title
19737      * @param {String} html to set the title to.
19738      */
19739     setTitle : function(value)
19740     {
19741         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
19742     },
19743     onAddPane : function(pane)
19744     {
19745         //Roo.log('addpane');
19746         //Roo.log(pane);
19747         // tabs are rendere left to right..
19748         if(!this.showtabs){
19749             return;
19750         }
19751         
19752         var ctr = this.el.select('.nav-tabs', true).first();
19753          
19754          
19755         var existing = ctr.select('.nav-tab',true);
19756         var qty = existing.getCount();;
19757         
19758         
19759         var tab = ctr.createChild({
19760             tag : 'li',
19761             cls : 'nav-tab' + (qty ? '' : ' active'),
19762             cn : [
19763                 {
19764                     tag : 'a',
19765                     href:'#',
19766                     html : pane.title
19767                 }
19768             ]
19769         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
19770         pane.tab = tab;
19771         
19772         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
19773         if (!qty) {
19774             pane.el.addClass('active');
19775         }
19776         
19777                 
19778     },
19779     onTabClick : function(ev,un,ob,pane)
19780     {
19781         //Roo.log('tab - prev default');
19782         ev.preventDefault();
19783         
19784         
19785         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
19786         pane.tab.addClass('active');
19787         //Roo.log(pane.title);
19788         this.getChildContainer().select('.tab-pane',true).removeClass('active');
19789         // technically we should have a deactivate event.. but maybe add later.
19790         // and it should not de-activate the selected tab...
19791         
19792         pane.el.addClass('active');
19793         pane.fireEvent('activate');
19794         
19795         
19796     }
19797     
19798     
19799 });
19800
19801  
19802 /*
19803  * - LGPL
19804  *
19805  * Tab pane
19806  * 
19807  */
19808 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19809 /**
19810  * @class Roo.bootstrap.TabPane
19811  * @extends Roo.bootstrap.Component
19812  * Bootstrap TabPane class
19813  * @cfg {Boolean} active (false | true) Default false
19814  * @cfg {String} title title of panel
19815
19816  * 
19817  * @constructor
19818  * Create a new TabPane
19819  * @param {Object} config The config object
19820  */
19821
19822 Roo.bootstrap.dash.TabPane = function(config){
19823     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
19824     
19825     this.addEvents({
19826         // raw events
19827         /**
19828          * @event activate
19829          * When a pane is activated
19830          * @param {Roo.bootstrap.dash.TabPane} pane
19831          */
19832         "activate" : true
19833          
19834     });
19835 };
19836
19837 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
19838     
19839     active : false,
19840     title : '',
19841     
19842     // the tabBox that this is attached to.
19843     tab : false,
19844      
19845     getAutoCreate : function() 
19846     {
19847         var cfg = {
19848             tag: 'div',
19849             cls: 'tab-pane'
19850         }
19851         
19852         if(this.active){
19853             cfg.cls += ' active';
19854         }
19855         
19856         return cfg;
19857     },
19858     initEvents  : function()
19859     {
19860         //Roo.log('trigger add pane handler');
19861         this.parent().fireEvent('addpane', this)
19862     },
19863     
19864      /**
19865      * Updates the tab title 
19866      * @param {String} html to set the title to.
19867      */
19868     setTitle: function(str)
19869     {
19870         if (!this.tab) {
19871             return;
19872         }
19873         this.title = str;
19874         this.tab.select('a', true).first().dom.innerHTML = str;
19875         
19876     }
19877     
19878     
19879     
19880 });
19881
19882  
19883
19884
19885  /*
19886  * - LGPL
19887  *
19888  * menu
19889  * 
19890  */
19891 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
19892
19893 /**
19894  * @class Roo.bootstrap.menu.Menu
19895  * @extends Roo.bootstrap.Component
19896  * Bootstrap Menu class - container for Menu
19897  * @cfg {String} html Text of the menu
19898  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
19899  * @cfg {String} icon Font awesome icon
19900  * @cfg {String} pos Menu align to (top | bottom) default bottom
19901  * 
19902  * 
19903  * @constructor
19904  * Create a new Menu
19905  * @param {Object} config The config object
19906  */
19907
19908
19909 Roo.bootstrap.menu.Menu = function(config){
19910     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
19911     
19912     this.addEvents({
19913         /**
19914          * @event beforeshow
19915          * Fires before this menu is displayed
19916          * @param {Roo.bootstrap.menu.Menu} this
19917          */
19918         beforeshow : true,
19919         /**
19920          * @event beforehide
19921          * Fires before this menu is hidden
19922          * @param {Roo.bootstrap.menu.Menu} this
19923          */
19924         beforehide : true,
19925         /**
19926          * @event show
19927          * Fires after this menu is displayed
19928          * @param {Roo.bootstrap.menu.Menu} this
19929          */
19930         show : true,
19931         /**
19932          * @event hide
19933          * Fires after this menu is hidden
19934          * @param {Roo.bootstrap.menu.Menu} this
19935          */
19936         hide : true,
19937         /**
19938          * @event click
19939          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19940          * @param {Roo.bootstrap.menu.Menu} this
19941          * @param {Roo.EventObject} e
19942          */
19943         click : true
19944     });
19945     
19946 };
19947
19948 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
19949     
19950     submenu : false,
19951     html : '',
19952     weight : 'default',
19953     icon : false,
19954     pos : 'bottom',
19955     
19956     
19957     getChildContainer : function() {
19958         if(this.isSubMenu){
19959             return this.el;
19960         }
19961         
19962         return this.el.select('ul.dropdown-menu', true).first();  
19963     },
19964     
19965     getAutoCreate : function()
19966     {
19967         var text = [
19968             {
19969                 tag : 'span',
19970                 cls : 'roo-menu-text',
19971                 html : this.html
19972             }
19973         ];
19974         
19975         if(this.icon){
19976             text.unshift({
19977                 tag : 'i',
19978                 cls : 'fa ' + this.icon
19979             })
19980         }
19981         
19982         
19983         var cfg = {
19984             tag : 'div',
19985             cls : 'btn-group',
19986             cn : [
19987                 {
19988                     tag : 'button',
19989                     cls : 'dropdown-button btn btn-' + this.weight,
19990                     cn : text
19991                 },
19992                 {
19993                     tag : 'button',
19994                     cls : 'dropdown-toggle btn btn-' + this.weight,
19995                     cn : [
19996                         {
19997                             tag : 'span',
19998                             cls : 'caret'
19999                         }
20000                     ]
20001                 },
20002                 {
20003                     tag : 'ul',
20004                     cls : 'dropdown-menu'
20005                 }
20006             ]
20007             
20008         };
20009         
20010         if(this.pos == 'top'){
20011             cfg.cls += ' dropup';
20012         }
20013         
20014         if(this.isSubMenu){
20015             cfg = {
20016                 tag : 'ul',
20017                 cls : 'dropdown-menu'
20018             }
20019         }
20020         
20021         return cfg;
20022     },
20023     
20024     onRender : function(ct, position)
20025     {
20026         this.isSubMenu = ct.hasClass('dropdown-submenu');
20027         
20028         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
20029     },
20030     
20031     initEvents : function() 
20032     {
20033         if(this.isSubMenu){
20034             return;
20035         }
20036         
20037         this.hidden = true;
20038         
20039         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
20040         this.triggerEl.on('click', this.onTriggerPress, this);
20041         
20042         this.buttonEl = this.el.select('button.dropdown-button', true).first();
20043         this.buttonEl.on('click', this.onClick, this);
20044         
20045     },
20046     
20047     list : function()
20048     {
20049         if(this.isSubMenu){
20050             return this.el;
20051         }
20052         
20053         return this.el.select('ul.dropdown-menu', true).first();
20054     },
20055     
20056     onClick : function(e)
20057     {
20058         this.fireEvent("click", this, e);
20059     },
20060     
20061     onTriggerPress  : function(e)
20062     {   
20063         if (this.isVisible()) {
20064             this.hide();
20065         } else {
20066             this.show();
20067         }
20068     },
20069     
20070     isVisible : function(){
20071         return !this.hidden;
20072     },
20073     
20074     show : function()
20075     {
20076         this.fireEvent("beforeshow", this);
20077         
20078         this.hidden = false;
20079         this.el.addClass('open');
20080         
20081         Roo.get(document).on("mouseup", this.onMouseUp, this);
20082         
20083         this.fireEvent("show", this);
20084         
20085         
20086     },
20087     
20088     hide : function()
20089     {
20090         this.fireEvent("beforehide", this);
20091         
20092         this.hidden = true;
20093         this.el.removeClass('open');
20094         
20095         Roo.get(document).un("mouseup", this.onMouseUp);
20096         
20097         this.fireEvent("hide", this);
20098     },
20099     
20100     onMouseUp : function()
20101     {
20102         this.hide();
20103     }
20104     
20105 });
20106
20107  
20108  /*
20109  * - LGPL
20110  *
20111  * menu item
20112  * 
20113  */
20114 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20115
20116 /**
20117  * @class Roo.bootstrap.menu.Item
20118  * @extends Roo.bootstrap.Component
20119  * Bootstrap MenuItem class
20120  * @cfg {Boolean} submenu (true | false) default false
20121  * @cfg {String} html text of the item
20122  * @cfg {String} href the link
20123  * @cfg {Boolean} disable (true | false) default false
20124  * @cfg {Boolean} preventDefault (true | false) default true
20125  * @cfg {String} icon Font awesome icon
20126  * @cfg {String} pos Submenu align to (left | right) default right 
20127  * 
20128  * 
20129  * @constructor
20130  * Create a new Item
20131  * @param {Object} config The config object
20132  */
20133
20134
20135 Roo.bootstrap.menu.Item = function(config){
20136     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
20137     this.addEvents({
20138         /**
20139          * @event mouseover
20140          * Fires when the mouse is hovering over this menu
20141          * @param {Roo.bootstrap.menu.Item} this
20142          * @param {Roo.EventObject} e
20143          */
20144         mouseover : true,
20145         /**
20146          * @event mouseout
20147          * Fires when the mouse exits this menu
20148          * @param {Roo.bootstrap.menu.Item} this
20149          * @param {Roo.EventObject} e
20150          */
20151         mouseout : true,
20152         // raw events
20153         /**
20154          * @event click
20155          * The raw click event for the entire grid.
20156          * @param {Roo.EventObject} e
20157          */
20158         click : true
20159     });
20160 };
20161
20162 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
20163     
20164     submenu : false,
20165     href : '',
20166     html : '',
20167     preventDefault: true,
20168     disable : false,
20169     icon : false,
20170     pos : 'right',
20171     
20172     getAutoCreate : function()
20173     {
20174         var text = [
20175             {
20176                 tag : 'span',
20177                 cls : 'roo-menu-item-text',
20178                 html : this.html
20179             }
20180         ];
20181         
20182         if(this.icon){
20183             text.unshift({
20184                 tag : 'i',
20185                 cls : 'fa ' + this.icon
20186             })
20187         }
20188         
20189         var cfg = {
20190             tag : 'li',
20191             cn : [
20192                 {
20193                     tag : 'a',
20194                     href : this.href || '#',
20195                     cn : text
20196                 }
20197             ]
20198         };
20199         
20200         if(this.disable){
20201             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
20202         }
20203         
20204         if(this.submenu){
20205             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
20206             
20207             if(this.pos == 'left'){
20208                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
20209             }
20210         }
20211         
20212         return cfg;
20213     },
20214     
20215     initEvents : function() 
20216     {
20217         this.el.on('mouseover', this.onMouseOver, this);
20218         this.el.on('mouseout', this.onMouseOut, this);
20219         
20220         this.el.select('a', true).first().on('click', this.onClick, this);
20221         
20222     },
20223     
20224     onClick : function(e)
20225     {
20226         if(this.preventDefault){
20227             e.preventDefault();
20228         }
20229         
20230         this.fireEvent("click", this, e);
20231     },
20232     
20233     onMouseOver : function(e)
20234     {
20235         if(this.submenu && this.pos == 'left'){
20236             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
20237         }
20238         
20239         this.fireEvent("mouseover", this, e);
20240     },
20241     
20242     onMouseOut : function(e)
20243     {
20244         this.fireEvent("mouseout", this, e);
20245     }
20246 });
20247
20248  
20249
20250  /*
20251  * - LGPL
20252  *
20253  * menu separator
20254  * 
20255  */
20256 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20257
20258 /**
20259  * @class Roo.bootstrap.menu.Separator
20260  * @extends Roo.bootstrap.Component
20261  * Bootstrap Separator class
20262  * 
20263  * @constructor
20264  * Create a new Separator
20265  * @param {Object} config The config object
20266  */
20267
20268
20269 Roo.bootstrap.menu.Separator = function(config){
20270     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
20271 };
20272
20273 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
20274     
20275     getAutoCreate : function(){
20276         var cfg = {
20277             tag : 'li',
20278             cls: 'divider'
20279         };
20280         
20281         return cfg;
20282     }
20283    
20284 });
20285
20286  
20287
20288