Roo/form/ComboBox.js
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * 
20  * @constructor
21  * Do not use directly - it does not do anything..
22  * @param {Object} config The config object
23  */
24
25
26
27 Roo.bootstrap.Component = function(config){
28     Roo.bootstrap.Component.superclass.constructor.call(this, config);
29 };
30
31 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
32     
33     
34     allowDomMove : false, // to stop relocations in parent onRender...
35     
36     cls : false,
37     
38     style : false,
39     
40     autoCreate : false,
41     
42     initEvents : function() {  },
43     
44     xattr : false,
45     
46     parentId : false,
47     
48     can_build_overlaid : true,
49     
50     dataId : false,
51     
52     name : false,
53     
54     parent: function() {
55         // returns the parent component..
56         return Roo.ComponentMgr.get(this.parentId)
57         
58         
59     },
60     
61     // private
62     onRender : function(ct, position)
63     {
64        // Roo.log("Call onRender: " + this.xtype);
65         
66         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
67         
68         if(this.el){
69             if (this.el.attr('xtype')) {
70                 this.el.attr('xtypex', this.el.attr('xtype'));
71                 this.el.dom.removeAttribute('xtype');
72                 
73                 this.initEvents();
74             }
75             
76             return;
77         }
78         
79          
80         
81         var cfg = Roo.apply({},  this.getAutoCreate());
82         cfg.id = Roo.id();
83         
84         // fill in the extra attributes 
85         if (this.xattr && typeof(this.xattr) =='object') {
86             for (var i in this.xattr) {
87                 cfg[i] = this.xattr[i];
88             }
89         }
90         
91         if(this.dataId){
92             cfg.dataId = this.dataId;
93         }
94         
95         if (this.cls) {
96             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
97         }
98         
99         if (this.style) { // fixme needs to support more complex style data.
100             cfg.style = this.style;
101         }
102         
103         if(this.name){
104             cfg.name = this.name;
105         }
106         
107         this.el = ct.createChild(cfg, position);
108         
109         if(this.tabIndex !== undefined){
110             this.el.dom.setAttribute('tabIndex', this.tabIndex);
111         }
112         this.initEvents();
113         
114         
115     },
116     
117     getChildContainer : function()
118     {
119         return this.el;
120     },
121     
122     
123     addxtype  : function(tree,cntr)
124     {
125         var cn = this;
126         
127         cn = Roo.factory(tree);
128            
129         cn.parentType = this.xtype; //??
130         cn.parentId = this.id;
131         
132         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
133         
134         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
135         
136         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
137         
138         var build_from_html =  Roo.XComponent.build_from_html;
139           
140         var is_body  = (tree.xtype == 'Body') ;
141           
142         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
143           
144         var self_cntr_el = Roo.get(this[cntr](false));
145         
146         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
147             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
148                 return this.addxtypeChild(tree,cntr);
149             }
150             
151             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
152                 
153             if(echild){
154                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
155             }
156             
157             Roo.log('skipping render');
158             return cn;
159             
160         }
161         
162         var ret = false;
163         
164         while (true) {
165             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
166             
167             if (!echild) {
168                 break;
169             }
170             
171             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
172                 break;
173             }
174             
175             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
176         }
177         return ret;
178     },
179     
180     addxtypeChild : function (tree, cntr)
181     {
182         Roo.log('addxtypeChild:' + cntr);
183         var cn = this;
184         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
185         
186         
187         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
188                     (typeof(tree['flexy:foreach']) != 'undefined');
189           
190         
191         
192         
193         // render the element if it's not BODY.
194         if (tree.xtype != 'Body') {
195            
196             cn = Roo.factory(tree);
197            
198             cn.parentType = this.xtype; //??
199             cn.parentId = this.id;
200             
201             var build_from_html =  Roo.XComponent.build_from_html;
202             
203             
204             // does the container contain child eleemnts with 'xtype' attributes.
205             // that match this xtype..
206             // note - when we render we create these as well..
207             // so we should check to see if body has xtype set.
208             if (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
209                
210                 var self_cntr_el = Roo.get(this[cntr](false));
211                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
212                 
213                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
214                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
215                   
216                   
217                   
218                     cn.el = echild;
219                   //  Roo.log("GOT");
220                     //echild.dom.removeAttribute('xtype');
221                 } else {
222                     Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
223                    
224                 }
225             }
226            
227             
228                
229             // if object has flexy:if - then it may or may not be rendered.
230             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
231                 // skip a flexy if element.
232                 Roo.log('skipping render');
233              } else {
234                  
235                 // actually if flexy:foreach is found, we really want to create 
236                 // multiple copies here...
237                 //Roo.log('render');
238                 //Roo.log(this[cntr]());
239                 cn.render(this[cntr](true));
240              }
241             // then add the element..
242         }
243         
244         
245         // handle the kids..
246         
247         var nitems = [];
248         /*
249         if (typeof (tree.menu) != 'undefined') {
250             tree.menu.parentType = cn.xtype;
251             tree.menu.triggerEl = cn.el;
252             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
253             
254         }
255         */
256         if (!tree.items || !tree.items.length) {
257             cn.items = nitems;
258             return cn;
259         }
260         var items = tree.items;
261         delete tree.items;
262         
263         //Roo.log(items.length);
264             // add the items..
265         for(var i =0;i < items.length;i++) {
266             nitems.push(cn.addxtype(Roo.apply({}, items[i])));
267         }
268         
269         cn.items = nitems;
270         
271         return cn;
272     }
273     
274     
275     
276     
277 });
278
279  /*
280  * - LGPL
281  *
282  * Body
283  * 
284  */
285
286 /**
287  * @class Roo.bootstrap.Body
288  * @extends Roo.bootstrap.Component
289  * Bootstrap Body class
290  * 
291  * @constructor
292  * Create a new body
293  * @param {Object} config The config object
294  */
295
296 Roo.bootstrap.Body = function(config){
297     Roo.bootstrap.Body.superclass.constructor.call(this, config);
298     this.el = Roo.get(document.body);
299     if (this.cls && this.cls.length) {
300         Roo.get(document.body).addClass(this.cls);
301     }
302 };
303
304 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
305       
306         autoCreate : {
307         cls: 'container'
308     },
309     onRender : function(ct, position)
310     {
311        /* Roo.log("Roo.bootstrap.Body - onRender");
312         if (this.cls && this.cls.length) {
313             Roo.get(document.body).addClass(this.cls);
314         }
315         // style??? xttr???
316         */
317     }
318     
319     
320  
321    
322 });
323
324  /*
325  * - LGPL
326  *
327  * button group
328  * 
329  */
330
331
332 /**
333  * @class Roo.bootstrap.ButtonGroup
334  * @extends Roo.bootstrap.Component
335  * Bootstrap ButtonGroup class
336  * @cfg {String} size lg | sm | xs (default empty normal)
337  * @cfg {String} align vertical | justified  (default none)
338  * @cfg {String} direction up | down (default down)
339  * @cfg {Boolean} toolbar false | true
340  * @cfg {Boolean} btn true | false
341  * 
342  * 
343  * @constructor
344  * Create a new Input
345  * @param {Object} config The config object
346  */
347
348 Roo.bootstrap.ButtonGroup = function(config){
349     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
350 };
351
352 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
353     
354     size: '',
355     align: '',
356     direction: '',
357     toolbar: false,
358     btn: true,
359
360     getAutoCreate : function(){
361         var cfg = {
362             cls: 'btn-group',
363             html : null
364         }
365         
366         cfg.html = this.html || cfg.html;
367         
368         if (this.toolbar) {
369             cfg = {
370                 cls: 'btn-toolbar',
371                 html: null
372             }
373             
374             return cfg;
375         }
376         
377         if (['vertical','justified'].indexOf(this.align)!==-1) {
378             cfg.cls = 'btn-group-' + this.align;
379             
380             if (this.align == 'justified') {
381                 console.log(this.items);
382             }
383         }
384         
385         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
386             cfg.cls += ' btn-group-' + this.size;
387         }
388         
389         if (this.direction == 'up') {
390             cfg.cls += ' dropup' ;
391         }
392         
393         return cfg;
394     }
395    
396 });
397
398  /*
399  * - LGPL
400  *
401  * button
402  * 
403  */
404
405 /**
406  * @class Roo.bootstrap.Button
407  * @extends Roo.bootstrap.Component
408  * Bootstrap Button class
409  * @cfg {String} html The button content
410  * @cfg {String} weight default (or empty) | primary | success | info | warning | danger | link
411  * @cfg {String} size empty | lg | sm | xs
412  * @cfg {String} tag empty | a | input | submit
413  * @cfg {String} href empty or href
414  * @cfg {Boolean} disabled false | true
415  * @cfg {Boolean} isClose false | true
416  * @cfg {String} glyphicon empty | adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out
417  * @cfg {String} badge text for badge
418  * @cfg {String} theme default (or empty) | glow
419  * @cfg {Boolean} inverse false | true
420  * @cfg {Boolean} toggle false | true
421  * @cfg {String} ontext text for on toggle state
422  * @cfg {String} offtext text for off toggle state
423  * @cfg {Boolean} defaulton true | false
424  * @cfg {Boolean} preventDefault (true | false) default true
425  * @cfg {Boolean} removeClass true | false remove the standard class..
426  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
427  * 
428  * @constructor
429  * Create a new button
430  * @param {Object} config The config object
431  */
432
433
434 Roo.bootstrap.Button = function(config){
435     Roo.bootstrap.Button.superclass.constructor.call(this, config);
436     this.addEvents({
437         // raw events
438         /**
439          * @event click
440          * When a butotn is pressed
441          * @param {Roo.EventObject} e
442          */
443         "click" : true,
444          /**
445          * @event toggle
446          * After the button has been toggles
447          * @param {Roo.EventObject} e
448          * @param {boolean} pressed (also available as button.pressed)
449          */
450         "toggle" : true
451     });
452 };
453
454 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
455     html: false,
456     active: false,
457     weight: '',
458     size: '',
459     tag: 'button',
460     href: '',
461     disabled: false,
462     isClose: false,
463     glyphicon: '',
464     badge: '',
465     theme: 'default',
466     inverse: false,
467     
468     toggle: false,
469     ontext: 'ON',
470     offtext: 'OFF',
471     defaulton: true,
472     preventDefault: true,
473     removeClass: false,
474     name: false,
475     target: false,
476     
477     
478     pressed : null,
479      
480     
481     getAutoCreate : function(){
482         
483         var cfg = {
484             tag : 'button',
485             cls : 'roo-button',
486             html: ''
487         };
488         
489         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
490             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
491             this.tag = 'button';
492         } else {
493             cfg.tag = this.tag;
494         }
495         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
496         
497         if (this.toggle == true) {
498             cfg={
499                 tag: 'div',
500                 cls: 'slider-frame roo-button',
501                 cn: [
502                     {
503                         tag: 'span',
504                         'data-on-text':'ON',
505                         'data-off-text':'OFF',
506                         cls: 'slider-button',
507                         html: this.offtext
508                     }
509                 ]
510             };
511             
512             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
513                 cfg.cls += ' '+this.weight;
514             }
515             
516             return cfg;
517         }
518         
519         if (this.isClose) {
520             cfg.cls += ' close';
521             
522             cfg["aria-hidden"] = true;
523             
524             cfg.html = "&times;";
525             
526             return cfg;
527         }
528         
529          
530         if (this.theme==='default') {
531             cfg.cls = 'btn roo-button';
532             
533             //if (this.parentType != 'Navbar') {
534             this.weight = this.weight.length ?  this.weight : 'default';
535             //}
536             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
537                 
538                 cfg.cls += ' btn-' + this.weight;
539             }
540         } else if (this.theme==='glow') {
541             
542             cfg.tag = 'a';
543             cfg.cls = 'btn-glow roo-button';
544             
545             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
546                 
547                 cfg.cls += ' ' + this.weight;
548             }
549         }
550    
551         
552         if (this.inverse) {
553             this.cls += ' inverse';
554         }
555         
556         
557         if (this.active) {
558             cfg.cls += ' active';
559         }
560         
561         if (this.disabled) {
562             cfg.disabled = 'disabled';
563         }
564         
565         if (this.items) {
566             Roo.log('changing to ul' );
567             cfg.tag = 'ul';
568             this.glyphicon = 'caret';
569         }
570         
571         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
572          
573         //gsRoo.log(this.parentType);
574         if (this.parentType === 'Navbar' && !this.parent().bar) {
575             Roo.log('changing to li?');
576             
577             cfg.tag = 'li';
578             
579             cfg.cls = '';
580             cfg.cn =  [{
581                 tag : 'a',
582                 cls : 'roo-button',
583                 html : this.html,
584                 href : this.href || '#'
585             }];
586             if (this.menu) {
587                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
588                 cfg.cls += ' dropdown';
589             }   
590             
591             delete cfg.html;
592             
593         }
594         
595        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
596         
597         if (this.glyphicon) {
598             cfg.html = ' ' + cfg.html;
599             
600             cfg.cn = [
601                 {
602                     tag: 'span',
603                     cls: 'glyphicon glyphicon-' + this.glyphicon
604                 }
605             ];
606         }
607         
608         if (this.badge) {
609             cfg.html += ' ';
610             
611             cfg.tag = 'a';
612             
613 //            cfg.cls='btn roo-button';
614             
615             cfg.href=this.href;
616             
617             var value = cfg.html;
618             
619             if(this.glyphicon){
620                 value = {
621                             tag: 'span',
622                             cls: 'glyphicon glyphicon-' + this.glyphicon,
623                             html: this.html
624                         };
625                 
626             }
627             
628             cfg.cn = [
629                 value,
630                 {
631                     tag: 'span',
632                     cls: 'badge',
633                     html: this.badge
634                 }
635             ];
636             
637             cfg.html='';
638         }
639         
640         if (this.menu) {
641             cfg.cls += ' dropdown';
642             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
643         }
644         
645         if (cfg.tag !== 'a' && this.href !== '') {
646             throw "Tag must be a to set href.";
647         } else if (this.href.length > 0) {
648             cfg.href = this.href;
649         }
650         
651         if(this.removeClass){
652             cfg.cls = '';
653         }
654         
655         if(this.target){
656             cfg.target = this.target;
657         }
658         
659         return cfg;
660     },
661     initEvents: function() {
662        // Roo.log('init events?');
663 //        Roo.log(this.el.dom);
664         // add the menu...
665         
666         if (typeof (this.menu) != 'undefined') {
667             this.menu.parentType = this.xtype;
668             this.menu.triggerEl = this.el;
669             this.addxtype(Roo.apply({}, this.menu));
670         }
671
672
673        if (this.el.hasClass('roo-button')) {
674             this.el.on('click', this.onClick, this);
675        } else {
676             this.el.select('.roo-button').on('click', this.onClick, this);
677        }
678        
679        if(this.removeClass){
680            this.el.on('click', this.onClick, this);
681        }
682        
683        this.el.enableDisplayMode();
684         
685     },
686     onClick : function(e)
687     {
688         if (this.disabled) {
689             return;
690         }
691         
692         Roo.log('button on click ');
693         if(this.preventDefault){
694             e.preventDefault();
695         }
696         if (this.pressed === true || this.pressed === false) {
697             this.pressed = !this.pressed;
698             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
699             this.fireEvent('toggle', this, e, this.pressed);
700         }
701         
702         
703         this.fireEvent('click', this, e);
704     },
705     
706     /**
707      * Enables this button
708      */
709     enable : function()
710     {
711         this.disabled = false;
712         this.el.removeClass('disabled');
713     },
714     
715     /**
716      * Disable this button
717      */
718     disable : function()
719     {
720         this.disabled = true;
721         this.el.addClass('disabled');
722     },
723      /**
724      * sets the active state on/off, 
725      * @param {Boolean} state (optional) Force a particular state
726      */
727     setActive : function(v) {
728         
729         this.el[v ? 'addClass' : 'removeClass']('active');
730     },
731      /**
732      * toggles the current active state 
733      */
734     toggleActive : function()
735     {
736        var active = this.el.hasClass('active');
737        this.setActive(!active);
738        
739         
740     },
741     setText : function(str)
742     {
743         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
744     },
745     getText : function()
746     {
747         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
748     },
749     hide: function() {
750        
751      
752         this.el.hide();   
753     },
754     show: function() {
755        
756         this.el.show();   
757     }
758     
759     
760 });
761
762  /*
763  * - LGPL
764  *
765  * column
766  * 
767  */
768
769 /**
770  * @class Roo.bootstrap.Column
771  * @extends Roo.bootstrap.Component
772  * Bootstrap Column class
773  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
774  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
775  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
776  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
777  * @cfg {Boolean} hidden (true|false) hide the element
778  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
779  * @cfg {String} fa (ban|check|...) font awesome icon
780  * @cfg {String} icon (info-sign|check|...) glyphicon name
781
782  * @cfg {String} html content of column.
783  * 
784  * @constructor
785  * Create a new Column
786  * @param {Object} config The config object
787  */
788
789 Roo.bootstrap.Column = function(config){
790     Roo.bootstrap.Column.superclass.constructor.call(this, config);
791 };
792
793 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
794     
795     xs: false,
796     sm: false,
797     md: false,
798     lg: false,
799     html: '',
800     offset: 0,
801     alert: false,
802     fa: false,
803     icon : false,
804     hidden : false,
805     
806     getAutoCreate : function(){
807         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
808         
809         cfg = {
810             tag: 'div',
811             cls: 'column'
812         };
813         
814         var settings=this;
815         ['xs','sm','md','lg'].map(function(size){
816             Roo.log( size + ':' + settings[size]);
817             if (settings[size] === false) {
818                 return;
819             }
820             Roo.log(settings[size]);
821             if (!settings[size]) { // 0 = hidden
822                 cfg.cls += ' hidden-' + size;
823                 return;
824             }
825             cfg.cls += ' col-' + size + '-' + settings[size];
826             
827         });
828         
829         if (this.hidden) {
830             cfg.cls += ' hidden';
831         }
832         
833         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
834             cfg.cls +=' alert alert-' + this.alert;
835         }
836         
837         
838         if (this.html.length) {
839             cfg.html = this.html;
840         }
841         if (this.fa) {
842             cfg.html = '<i class="fa fa-'+this.fa + '"></i>' + (cfg.html || '');
843         }
844         if (this.icon) {
845             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + + (cfg.html || '')
846         }
847         
848         return cfg;
849     }
850    
851 });
852
853  
854
855  /*
856  * - LGPL
857  *
858  * page container.
859  * 
860  */
861
862
863 /**
864  * @class Roo.bootstrap.Container
865  * @extends Roo.bootstrap.Component
866  * Bootstrap Container class
867  * @cfg {Boolean} jumbotron is it a jumbotron element
868  * @cfg {String} html content of element
869  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
870  * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
871  * @cfg {String} header content of header (for panel)
872  * @cfg {String} footer content of footer (for panel)
873  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
874  * @cfg {String} tag (header|aside|section) type of HTML tag.
875  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
876  * @cfg {String} fa (ban|check|...) font awesome icon
877  * @cfg {String} icon (info-sign|check|...) glyphicon name
878  * @cfg {Boolean} hidden (true|false) hide the element
879
880  *     
881  * @constructor
882  * Create a new Container
883  * @param {Object} config The config object
884  */
885
886 Roo.bootstrap.Container = function(config){
887     Roo.bootstrap.Container.superclass.constructor.call(this, config);
888 };
889
890 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
891     
892     jumbotron : false,
893     well: '',
894     panel : '',
895     header: '',
896     footer : '',
897     sticky: '',
898     tag : false,
899     alert : false,
900     fa: false,
901     icon : false,
902   
903      
904     getChildContainer : function() {
905         
906         if(!this.el){
907             return false;
908         }
909         
910         if (this.panel.length) {
911             return this.el.select('.panel-body',true).first();
912         }
913         
914         return this.el;
915     },
916     
917     
918     getAutoCreate : function(){
919         
920         var cfg = {
921             tag : this.tag || 'div',
922             html : '',
923             cls : ''
924         };
925         if (this.jumbotron) {
926             cfg.cls = 'jumbotron';
927         }
928         
929         
930         
931         // - this is applied by the parent..
932         //if (this.cls) {
933         //    cfg.cls = this.cls + '';
934         //}
935         
936         if (this.sticky.length) {
937             
938             var bd = Roo.get(document.body);
939             if (!bd.hasClass('bootstrap-sticky')) {
940                 bd.addClass('bootstrap-sticky');
941                 Roo.select('html',true).setStyle('height', '100%');
942             }
943              
944             cfg.cls += 'bootstrap-sticky-' + this.sticky;
945         }
946         
947         
948         if (this.well.length) {
949             switch (this.well) {
950                 case 'lg':
951                 case 'sm':
952                     cfg.cls +=' well well-' +this.well;
953                     break;
954                 default:
955                     cfg.cls +=' well';
956                     break;
957             }
958         }
959         
960         if (this.hidden) {
961             cfg.cls += ' hidden';
962         }
963         
964         
965         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
966             cfg.cls +=' alert alert-' + this.alert;
967         }
968         
969         var body = cfg;
970         
971         if (this.panel.length) {
972             cfg.cls += ' panel panel-' + this.panel;
973             cfg.cn = [];
974             if (this.header.length) {
975                 cfg.cn.push({
976                     
977                     cls : 'panel-heading',
978                     cn : [{
979                         tag: 'h3',
980                         cls : 'panel-title',
981                         html : this.header
982                     }]
983                     
984                 });
985             }
986             body = false;
987             cfg.cn.push({
988                 cls : 'panel-body',
989                 html : this.html
990             });
991             
992             
993             if (this.footer.length) {
994                 cfg.cn.push({
995                     cls : 'panel-footer',
996                     html : this.footer
997                     
998                 });
999             }
1000             
1001         }
1002         
1003         if (body) {
1004             body.html = this.html || cfg.html;
1005             // prefix with the icons..
1006             if (this.fa) {
1007                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1008             }
1009             if (this.icon) {
1010                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1011             }
1012             
1013             
1014         }
1015         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1016             cfg.cls =  'container';
1017         }
1018         
1019         return cfg;
1020     },
1021     
1022     titleEl : function()
1023     {
1024         if(!this.el || !this.panel.length || !this.header.length){
1025             return;
1026         }
1027         
1028         return this.el.select('.panel-title',true).first();
1029     },
1030     
1031     setTitle : function(v)
1032     {
1033         var titleEl = this.titleEl();
1034         
1035         if(!titleEl){
1036             return;
1037         }
1038         
1039         titleEl.dom.innerHTML = v;
1040     },
1041     
1042     getTitle : function()
1043     {
1044         
1045         var titleEl = this.titleEl();
1046         
1047         if(!titleEl){
1048             return '';
1049         }
1050         
1051         return titleEl.dom.innerHTML;
1052     }
1053    
1054 });
1055
1056  /*
1057  * - LGPL
1058  *
1059  * image
1060  * 
1061  */
1062
1063
1064 /**
1065  * @class Roo.bootstrap.Img
1066  * @extends Roo.bootstrap.Component
1067  * Bootstrap Img class
1068  * @cfg {Boolean} imgResponsive false | true
1069  * @cfg {String} border rounded | circle | thumbnail
1070  * @cfg {String} src image source
1071  * @cfg {String} alt image alternative text
1072  * @cfg {String} href a tag href
1073  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1074  * 
1075  * @constructor
1076  * Create a new Input
1077  * @param {Object} config The config object
1078  */
1079
1080 Roo.bootstrap.Img = function(config){
1081     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1082     
1083     this.addEvents({
1084         // img events
1085         /**
1086          * @event click
1087          * The img click event for the img.
1088          * @param {Roo.EventObject} e
1089          */
1090         "click" : true
1091     });
1092 };
1093
1094 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1095     
1096     imgResponsive: true,
1097     border: '',
1098     src: '',
1099     href: false,
1100     target: false,
1101
1102     getAutoCreate : function(){
1103         
1104         var cfg = {
1105             tag: 'img',
1106             cls: (this.imgResponsive) ? 'img-responsive' : '',
1107             html : null
1108         }
1109         
1110         cfg.html = this.html || cfg.html;
1111         
1112         cfg.src = this.src || cfg.src;
1113         
1114         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1115             cfg.cls += ' img-' + this.border;
1116         }
1117         
1118         if(this.alt){
1119             cfg.alt = this.alt;
1120         }
1121         
1122         if(this.href){
1123             var a = {
1124                 tag: 'a',
1125                 href: this.href,
1126                 cn: [
1127                     cfg
1128                 ]
1129             }
1130             
1131             if(this.target){
1132                 a.target = this.target;
1133             }
1134             
1135         }
1136         
1137         
1138         return (this.href) ? a : cfg;
1139     },
1140     
1141     initEvents: function() {
1142         
1143         if(!this.href){
1144             this.el.on('click', this.onClick, this);
1145         }
1146     },
1147     
1148     onClick : function(e)
1149     {
1150         Roo.log('img onclick');
1151         this.fireEvent('click', this, e);
1152     }
1153    
1154 });
1155
1156  /*
1157  * - LGPL
1158  *
1159  * image
1160  * 
1161  */
1162
1163
1164 /**
1165  * @class Roo.bootstrap.Link
1166  * @extends Roo.bootstrap.Component
1167  * Bootstrap Link Class
1168  * @cfg {String} alt image alternative text
1169  * @cfg {String} href a tag href
1170  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1171  * @cfg {String} html the content of the link.
1172  * @cfg {Boolean} preventDefault (true | false) default false
1173
1174  * 
1175  * @constructor
1176  * Create a new Input
1177  * @param {Object} config The config object
1178  */
1179
1180 Roo.bootstrap.Link = function(config){
1181     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1182     
1183     this.addEvents({
1184         // img events
1185         /**
1186          * @event click
1187          * The img click event for the img.
1188          * @param {Roo.EventObject} e
1189          */
1190         "click" : true
1191     });
1192 };
1193
1194 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1195     
1196     href: false,
1197     target: false,
1198     preventDefault: false,
1199
1200     getAutoCreate : function(){
1201         
1202         var cfg = {
1203             tag: 'a',
1204             html : this.html || 'html-missing'
1205         }
1206         
1207         
1208         if(this.alt){
1209             cfg.alt = this.alt;
1210         }
1211         cfg.href = this.href || '#';
1212         if(this.target){
1213             cfg.target = this.target;
1214         }
1215         
1216         return cfg;
1217     },
1218     
1219     initEvents: function() {
1220         
1221         if(!this.href || this.preventDefault){
1222             this.el.on('click', this.onClick, this);
1223         }
1224     },
1225     
1226     onClick : function(e)
1227     {
1228         if(this.preventDefault){
1229             e.preventDefault();
1230         }
1231         //Roo.log('img onclick');
1232         this.fireEvent('click', this, e);
1233     }
1234    
1235 });
1236
1237  /*
1238  * - LGPL
1239  *
1240  * header
1241  * 
1242  */
1243
1244 /**
1245  * @class Roo.bootstrap.Header
1246  * @extends Roo.bootstrap.Component
1247  * Bootstrap Header class
1248  * @cfg {String} html content of header
1249  * @cfg {Number} level (1|2|3|4|5|6) default 1
1250  * 
1251  * @constructor
1252  * Create a new Header
1253  * @param {Object} config The config object
1254  */
1255
1256
1257 Roo.bootstrap.Header  = function(config){
1258     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1259 };
1260
1261 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1262     
1263     //href : false,
1264     html : false,
1265     level : 1,
1266     
1267     
1268     
1269     getAutoCreate : function(){
1270         
1271         var cfg = {
1272             tag: 'h' + (1 *this.level),
1273             html: this.html || 'fill in html'
1274         } ;
1275         
1276         return cfg;
1277     }
1278    
1279 });
1280
1281  
1282
1283  /*
1284  * Based on:
1285  * Ext JS Library 1.1.1
1286  * Copyright(c) 2006-2007, Ext JS, LLC.
1287  *
1288  * Originally Released Under LGPL - original licence link has changed is not relivant.
1289  *
1290  * Fork - LGPL
1291  * <script type="text/javascript">
1292  */
1293  
1294 /**
1295  * @class Roo.bootstrap.MenuMgr
1296  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1297  * @singleton
1298  */
1299 Roo.bootstrap.MenuMgr = function(){
1300    var menus, active, groups = {}, attached = false, lastShow = new Date();
1301
1302    // private - called when first menu is created
1303    function init(){
1304        menus = {};
1305        active = new Roo.util.MixedCollection();
1306        Roo.get(document).addKeyListener(27, function(){
1307            if(active.length > 0){
1308                hideAll();
1309            }
1310        });
1311    }
1312
1313    // private
1314    function hideAll(){
1315        if(active && active.length > 0){
1316            var c = active.clone();
1317            c.each(function(m){
1318                m.hide();
1319            });
1320        }
1321    }
1322
1323    // private
1324    function onHide(m){
1325        active.remove(m);
1326        if(active.length < 1){
1327            Roo.get(document).un("mouseup", onMouseDown);
1328             
1329            attached = false;
1330        }
1331    }
1332
1333    // private
1334    function onShow(m){
1335        var last = active.last();
1336        lastShow = new Date();
1337        active.add(m);
1338        if(!attached){
1339           Roo.get(document).on("mouseup", onMouseDown);
1340            
1341            attached = true;
1342        }
1343        if(m.parentMenu){
1344           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1345           m.parentMenu.activeChild = m;
1346        }else if(last && last.isVisible()){
1347           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1348        }
1349    }
1350
1351    // private
1352    function onBeforeHide(m){
1353        if(m.activeChild){
1354            m.activeChild.hide();
1355        }
1356        if(m.autoHideTimer){
1357            clearTimeout(m.autoHideTimer);
1358            delete m.autoHideTimer;
1359        }
1360    }
1361
1362    // private
1363    function onBeforeShow(m){
1364        var pm = m.parentMenu;
1365        if(!pm && !m.allowOtherMenus){
1366            hideAll();
1367        }else if(pm && pm.activeChild && active != m){
1368            pm.activeChild.hide();
1369        }
1370    }
1371
1372    // private
1373    function onMouseDown(e){
1374         Roo.log("on MouseDown");
1375         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
1376            hideAll();
1377         }
1378         
1379         
1380    }
1381
1382    // private
1383    function onBeforeCheck(mi, state){
1384        if(state){
1385            var g = groups[mi.group];
1386            for(var i = 0, l = g.length; i < l; i++){
1387                if(g[i] != mi){
1388                    g[i].setChecked(false);
1389                }
1390            }
1391        }
1392    }
1393
1394    return {
1395
1396        /**
1397         * Hides all menus that are currently visible
1398         */
1399        hideAll : function(){
1400             hideAll();  
1401        },
1402
1403        // private
1404        register : function(menu){
1405            if(!menus){
1406                init();
1407            }
1408            menus[menu.id] = menu;
1409            menu.on("beforehide", onBeforeHide);
1410            menu.on("hide", onHide);
1411            menu.on("beforeshow", onBeforeShow);
1412            menu.on("show", onShow);
1413            var g = menu.group;
1414            if(g && menu.events["checkchange"]){
1415                if(!groups[g]){
1416                    groups[g] = [];
1417                }
1418                groups[g].push(menu);
1419                menu.on("checkchange", onCheck);
1420            }
1421        },
1422
1423         /**
1424          * Returns a {@link Roo.menu.Menu} object
1425          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1426          * be used to generate and return a new Menu instance.
1427          */
1428        get : function(menu){
1429            if(typeof menu == "string"){ // menu id
1430                return menus[menu];
1431            }else if(menu.events){  // menu instance
1432                return menu;
1433            }
1434            /*else if(typeof menu.length == 'number'){ // array of menu items?
1435                return new Roo.bootstrap.Menu({items:menu});
1436            }else{ // otherwise, must be a config
1437                return new Roo.bootstrap.Menu(menu);
1438            }
1439            */
1440            return false;
1441        },
1442
1443        // private
1444        unregister : function(menu){
1445            delete menus[menu.id];
1446            menu.un("beforehide", onBeforeHide);
1447            menu.un("hide", onHide);
1448            menu.un("beforeshow", onBeforeShow);
1449            menu.un("show", onShow);
1450            var g = menu.group;
1451            if(g && menu.events["checkchange"]){
1452                groups[g].remove(menu);
1453                menu.un("checkchange", onCheck);
1454            }
1455        },
1456
1457        // private
1458        registerCheckable : function(menuItem){
1459            var g = menuItem.group;
1460            if(g){
1461                if(!groups[g]){
1462                    groups[g] = [];
1463                }
1464                groups[g].push(menuItem);
1465                menuItem.on("beforecheckchange", onBeforeCheck);
1466            }
1467        },
1468
1469        // private
1470        unregisterCheckable : function(menuItem){
1471            var g = menuItem.group;
1472            if(g){
1473                groups[g].remove(menuItem);
1474                menuItem.un("beforecheckchange", onBeforeCheck);
1475            }
1476        }
1477    };
1478 }();/*
1479  * - LGPL
1480  *
1481  * menu
1482  * 
1483  */
1484
1485 /**
1486  * @class Roo.bootstrap.Menu
1487  * @extends Roo.bootstrap.Component
1488  * Bootstrap Menu class - container for MenuItems
1489  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1490  * 
1491  * @constructor
1492  * Create a new Menu
1493  * @param {Object} config The config object
1494  */
1495
1496
1497 Roo.bootstrap.Menu = function(config){
1498     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1499     if (this.registerMenu) {
1500         Roo.bootstrap.MenuMgr.register(this);
1501     }
1502     this.addEvents({
1503         /**
1504          * @event beforeshow
1505          * Fires before this menu is displayed
1506          * @param {Roo.menu.Menu} this
1507          */
1508         beforeshow : true,
1509         /**
1510          * @event beforehide
1511          * Fires before this menu is hidden
1512          * @param {Roo.menu.Menu} this
1513          */
1514         beforehide : true,
1515         /**
1516          * @event show
1517          * Fires after this menu is displayed
1518          * @param {Roo.menu.Menu} this
1519          */
1520         show : true,
1521         /**
1522          * @event hide
1523          * Fires after this menu is hidden
1524          * @param {Roo.menu.Menu} this
1525          */
1526         hide : true,
1527         /**
1528          * @event click
1529          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1530          * @param {Roo.menu.Menu} this
1531          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1532          * @param {Roo.EventObject} e
1533          */
1534         click : true,
1535         /**
1536          * @event mouseover
1537          * Fires when the mouse is hovering over this menu
1538          * @param {Roo.menu.Menu} this
1539          * @param {Roo.EventObject} e
1540          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1541          */
1542         mouseover : true,
1543         /**
1544          * @event mouseout
1545          * Fires when the mouse exits this menu
1546          * @param {Roo.menu.Menu} this
1547          * @param {Roo.EventObject} e
1548          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1549          */
1550         mouseout : true,
1551         /**
1552          * @event itemclick
1553          * Fires when a menu item contained in this menu is clicked
1554          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1555          * @param {Roo.EventObject} e
1556          */
1557         itemclick: true
1558     });
1559     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1560 };
1561
1562 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1563     
1564    /// html : false,
1565     //align : '',
1566     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1567     type: false,
1568     /**
1569      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1570      */
1571     registerMenu : true,
1572     
1573     menuItems :false, // stores the menu items..
1574     
1575     hidden:true,
1576     
1577     parentMenu : false,
1578     
1579     getChildContainer : function() {
1580         return this.el;  
1581     },
1582     
1583     getAutoCreate : function(){
1584          
1585         //if (['right'].indexOf(this.align)!==-1) {
1586         //    cfg.cn[1].cls += ' pull-right'
1587         //}
1588         
1589         
1590         var cfg = {
1591             tag : 'ul',
1592             cls : 'dropdown-menu' ,
1593             style : 'z-index:1000'
1594             
1595         }
1596         
1597         if (this.type === 'submenu') {
1598             cfg.cls = 'submenu active';
1599         }
1600         if (this.type === 'treeview') {
1601             cfg.cls = 'treeview-menu';
1602         }
1603         
1604         return cfg;
1605     },
1606     initEvents : function() {
1607         
1608        // Roo.log("ADD event");
1609        // Roo.log(this.triggerEl.dom);
1610         this.triggerEl.on('click', this.onTriggerPress, this);
1611         this.triggerEl.addClass('dropdown-toggle');
1612         this.el.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
1613
1614         this.el.on("mouseover", this.onMouseOver, this);
1615         this.el.on("mouseout", this.onMouseOut, this);
1616         
1617         
1618     },
1619     findTargetItem : function(e){
1620         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
1621         if(!t){
1622             return false;
1623         }
1624         //Roo.log(t);         Roo.log(t.id);
1625         if(t && t.id){
1626             //Roo.log(this.menuitems);
1627             return this.menuitems.get(t.id);
1628             
1629             //return this.items.get(t.menuItemId);
1630         }
1631         
1632         return false;
1633     },
1634     onClick : function(e){
1635         Roo.log("menu.onClick");
1636         var t = this.findTargetItem(e);
1637         if(!t){
1638             return;
1639         }
1640         Roo.log(e);
1641         /*
1642         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
1643             if(t == this.activeItem && t.shouldDeactivate(e)){
1644                 this.activeItem.deactivate();
1645                 delete this.activeItem;
1646                 return;
1647             }
1648             if(t.canActivate){
1649                 this.setActiveItem(t, true);
1650             }
1651             return;
1652             
1653             
1654         }
1655         */
1656         Roo.log('pass click event');
1657         
1658         t.onClick(e);
1659         
1660         this.fireEvent("click", this, t, e);
1661         
1662         this.hide();
1663     },
1664      onMouseOver : function(e){
1665         var t  = this.findTargetItem(e);
1666         //Roo.log(t);
1667         //if(t){
1668         //    if(t.canActivate && !t.disabled){
1669         //        this.setActiveItem(t, true);
1670         //    }
1671         //}
1672         
1673         this.fireEvent("mouseover", this, e, t);
1674     },
1675     isVisible : function(){
1676         return !this.hidden;
1677     },
1678      onMouseOut : function(e){
1679         var t  = this.findTargetItem(e);
1680         
1681         //if(t ){
1682         //    if(t == this.activeItem && t.shouldDeactivate(e)){
1683         //        this.activeItem.deactivate();
1684         //        delete this.activeItem;
1685         //    }
1686         //}
1687         this.fireEvent("mouseout", this, e, t);
1688     },
1689     
1690     
1691     /**
1692      * Displays this menu relative to another element
1693      * @param {String/HTMLElement/Roo.Element} element The element to align to
1694      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
1695      * the element (defaults to this.defaultAlign)
1696      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1697      */
1698     show : function(el, pos, parentMenu){
1699         this.parentMenu = parentMenu;
1700         if(!this.el){
1701             this.render();
1702         }
1703         this.fireEvent("beforeshow", this);
1704         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
1705     },
1706      /**
1707      * Displays this menu at a specific xy position
1708      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
1709      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1710      */
1711     showAt : function(xy, parentMenu, /* private: */_e){
1712         this.parentMenu = parentMenu;
1713         if(!this.el){
1714             this.render();
1715         }
1716         if(_e !== false){
1717             this.fireEvent("beforeshow", this);
1718             
1719             //xy = this.el.adjustForConstraints(xy);
1720         }
1721         //this.el.setXY(xy);
1722         //this.el.show();
1723         this.hideMenuItems();
1724         this.hidden = false;
1725         this.triggerEl.addClass('open');
1726         this.focus();
1727         this.fireEvent("show", this);
1728     },
1729     
1730     focus : function(){
1731         return;
1732         if(!this.hidden){
1733             this.doFocus.defer(50, this);
1734         }
1735     },
1736
1737     doFocus : function(){
1738         if(!this.hidden){
1739             this.focusEl.focus();
1740         }
1741     },
1742
1743     /**
1744      * Hides this menu and optionally all parent menus
1745      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
1746      */
1747     hide : function(deep){
1748         
1749         this.hideMenuItems();
1750         if(this.el && this.isVisible()){
1751             this.fireEvent("beforehide", this);
1752             if(this.activeItem){
1753                 this.activeItem.deactivate();
1754                 this.activeItem = null;
1755             }
1756             this.triggerEl.removeClass('open');;
1757             this.hidden = true;
1758             this.fireEvent("hide", this);
1759         }
1760         if(deep === true && this.parentMenu){
1761             this.parentMenu.hide(true);
1762         }
1763     },
1764     
1765     onTriggerPress  : function(e)
1766     {
1767         
1768         Roo.log('trigger press');
1769         //Roo.log(e.getTarget());
1770        // Roo.log(this.triggerEl.dom);
1771         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
1772             return;
1773         }
1774         if (this.isVisible()) {
1775             Roo.log('hide');
1776             this.hide();
1777         } else {
1778             this.show(this.triggerEl, false, false);
1779         }
1780         
1781         
1782     },
1783     
1784          
1785        
1786     
1787     hideMenuItems : function()
1788     {
1789         //$(backdrop).remove()
1790         Roo.select('.open',true).each(function(aa) {
1791             
1792             aa.removeClass('open');
1793           //var parent = getParent($(this))
1794           //var relatedTarget = { relatedTarget: this }
1795           
1796            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
1797           //if (e.isDefaultPrevented()) return
1798            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
1799         })
1800     },
1801     addxtypeChild : function (tree, cntr) {
1802         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
1803           
1804         this.menuitems.add(comp);
1805         return comp;
1806
1807     },
1808     getEl : function()
1809     {
1810         Roo.log(this.el);
1811         return this.el;
1812     }
1813 });
1814
1815  
1816  /*
1817  * - LGPL
1818  *
1819  * menu item
1820  * 
1821  */
1822
1823
1824 /**
1825  * @class Roo.bootstrap.MenuItem
1826  * @extends Roo.bootstrap.Component
1827  * Bootstrap MenuItem class
1828  * @cfg {String} html the menu label
1829  * @cfg {String} href the link
1830  * @cfg {Boolean} preventDefault (true | false) default true
1831  * 
1832  * 
1833  * @constructor
1834  * Create a new MenuItem
1835  * @param {Object} config The config object
1836  */
1837
1838
1839 Roo.bootstrap.MenuItem = function(config){
1840     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
1841     this.addEvents({
1842         // raw events
1843         /**
1844          * @event click
1845          * The raw click event for the entire grid.
1846          * @param {Roo.EventObject} e
1847          */
1848         "click" : true
1849     });
1850 };
1851
1852 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
1853     
1854     href : false,
1855     html : false,
1856     preventDefault: true,
1857     
1858     getAutoCreate : function(){
1859         var cfg= {
1860             tag: 'li',
1861             cls: 'dropdown-menu-item',
1862             cn: [
1863                     {
1864                         tag : 'a',
1865                         href : '#',
1866                         html : 'Link'
1867                     }
1868                 ]
1869         };
1870         if (this.parent().type == 'treeview') {
1871             cfg.cls = 'treeview-menu';
1872         }
1873         
1874         cfg.cn[0].href = this.href || cfg.cn[0].href ;
1875         cfg.cn[0].html = this.html || cfg.cn[0].html ;
1876         return cfg;
1877     },
1878     
1879     initEvents: function() {
1880         
1881         //this.el.select('a').on('click', this.onClick, this);
1882         
1883     },
1884     onClick : function(e)
1885     {
1886         Roo.log('item on click ');
1887         //if(this.preventDefault){
1888         //    e.preventDefault();
1889         //}
1890         //this.parent().hideMenuItems();
1891         
1892         this.fireEvent('click', this, e);
1893     },
1894     getEl : function()
1895     {
1896         return this.el;
1897     }
1898 });
1899
1900  
1901
1902  /*
1903  * - LGPL
1904  *
1905  * menu separator
1906  * 
1907  */
1908
1909
1910 /**
1911  * @class Roo.bootstrap.MenuSeparator
1912  * @extends Roo.bootstrap.Component
1913  * Bootstrap MenuSeparator class
1914  * 
1915  * @constructor
1916  * Create a new MenuItem
1917  * @param {Object} config The config object
1918  */
1919
1920
1921 Roo.bootstrap.MenuSeparator = function(config){
1922     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
1923 };
1924
1925 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
1926     
1927     getAutoCreate : function(){
1928         var cfg = {
1929             cls: 'divider',
1930             tag : 'li'
1931         };
1932         
1933         return cfg;
1934     }
1935    
1936 });
1937
1938  
1939
1940  
1941 /*
1942 <div class="modal fade">
1943   <div class="modal-dialog">
1944     <div class="modal-content">
1945       <div class="modal-header">
1946         <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
1947         <h4 class="modal-title">Modal title</h4>
1948       </div>
1949       <div class="modal-body">
1950         <p>One fine body&hellip;</p>
1951       </div>
1952       <div class="modal-footer">
1953         <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
1954         <button type="button" class="btn btn-primary">Save changes</button>
1955       </div>
1956     </div><!-- /.modal-content -->
1957   </div><!-- /.modal-dialog -->
1958 </div><!-- /.modal -->
1959 */
1960 /*
1961  * - LGPL
1962  *
1963  * page contgainer.
1964  * 
1965  */
1966
1967 /**
1968  * @class Roo.bootstrap.Modal
1969  * @extends Roo.bootstrap.Component
1970  * Bootstrap Modal class
1971  * @cfg {String} title Title of dialog
1972  * @cfg {Boolean} specificTitle (true|false) default false
1973  * @cfg {Array} buttons Array of buttons or standard button set..
1974  * @cfg {String} buttonPosition (left|right|center) default right
1975  * 
1976  * @constructor
1977  * Create a new Modal Dialog
1978  * @param {Object} config The config object
1979  */
1980
1981 Roo.bootstrap.Modal = function(config){
1982     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
1983     this.addEvents({
1984         // raw events
1985         /**
1986          * @event btnclick
1987          * The raw btnclick event for the button
1988          * @param {Roo.EventObject} e
1989          */
1990         "btnclick" : true
1991     });
1992     this.buttons = this.buttons || [];
1993 };
1994
1995 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
1996     
1997     title : 'test dialog',
1998    
1999     buttons : false,
2000     
2001     // set on load...
2002     body:  false,
2003     
2004     specificTitle: false,
2005     
2006     buttonPosition: 'right',
2007     
2008     onRender : function(ct, position)
2009     {
2010         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2011      
2012         if(!this.el){
2013             var cfg = Roo.apply({},  this.getAutoCreate());
2014             cfg.id = Roo.id();
2015             //if(!cfg.name){
2016             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2017             //}
2018             //if (!cfg.name.length) {
2019             //    delete cfg.name;
2020            // }
2021             if (this.cls) {
2022                 cfg.cls += ' ' + this.cls;
2023             }
2024             if (this.style) {
2025                 cfg.style = this.style;
2026             }
2027             this.el = Roo.get(document.body).createChild(cfg, position);
2028         }
2029         //var type = this.el.dom.type;
2030         
2031         if(this.tabIndex !== undefined){
2032             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2033         }
2034         
2035         
2036         
2037         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2038         this.maskEl.enableDisplayMode("block");
2039         this.maskEl.hide();
2040         //this.el.addClass("x-dlg-modal");
2041     
2042         if (this.buttons.length) {
2043             Roo.each(this.buttons, function(bb) {
2044                 b = Roo.apply({}, bb);
2045                 b.xns = b.xns || Roo.bootstrap;
2046                 b.xtype = b.xtype || 'Button';
2047                 if (typeof(b.listeners) == 'undefined') {
2048                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2049                 }
2050                 
2051                 var btn = Roo.factory(b);
2052                 
2053                 btn.onRender(this.el.select('.modal-footer div').first());
2054                 
2055             },this);
2056         }
2057         // render the children.
2058         var nitems = [];
2059         
2060         if(typeof(this.items) != 'undefined'){
2061             var items = this.items;
2062             delete this.items;
2063
2064             for(var i =0;i < items.length;i++) {
2065                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2066             }
2067         }
2068         
2069         this.items = nitems;
2070         
2071         this.body = this.el.select('.modal-body',true).first();
2072         this.close = this.el.select('.modal-header .close', true).first();
2073         this.footer = this.el.select('.modal-footer',true).first();
2074         this.initEvents();
2075         //this.el.addClass([this.fieldClass, this.cls]);
2076         
2077     },
2078     getAutoCreate : function(){
2079         
2080         
2081         var bdy = {
2082                 cls : 'modal-body',
2083                 html : this.html || ''
2084         };
2085         
2086         var title = {
2087             tag: 'h4',
2088             cls : 'modal-title',
2089             html : this.title
2090         };
2091         
2092         if(this.specificTitle){
2093             title = this.title;
2094         };
2095         
2096         return modal = {
2097             cls: "modal fade",
2098             style : 'display: none',
2099             cn : [
2100                 {
2101                     cls: "modal-dialog",
2102                     cn : [
2103                         {
2104                             cls : "modal-content",
2105                             cn : [
2106                                 {
2107                                     cls : 'modal-header',
2108                                     cn : [
2109                                         {
2110                                             tag: 'button',
2111                                             cls : 'close',
2112                                             html : '&times'
2113                                         },
2114                                         title
2115                                     ]
2116                                 },
2117                                 bdy,
2118                                 {
2119                                     cls : 'modal-footer',
2120                                     cn : [
2121                                         {
2122                                             tag: 'div',
2123                                             cls: 'btn-' + this.buttonPosition
2124                                         }
2125                                     ]
2126                                     
2127                                 }
2128                                 
2129                                 
2130                             ]
2131                             
2132                         }
2133                     ]
2134                         
2135                 }
2136             ]
2137             
2138             
2139         };
2140           
2141     },
2142     getChildContainer : function() {
2143          
2144          return this.el.select('.modal-body',true).first();
2145         
2146     },
2147     getButtonContainer : function() {
2148          return this.el.select('.modal-footer div',true).first();
2149         
2150     },
2151     initEvents : function()
2152     {
2153         this.el.select('.modal-header .close').on('click', this.hide, this);
2154 //        
2155 //        this.addxtype(this);
2156     },
2157     show : function() {
2158         
2159         if (!this.rendered) {
2160             this.render();
2161         }
2162        
2163         this.el.addClass('on');
2164         this.el.removeClass('fade');
2165         this.el.setStyle('display', 'block');
2166         Roo.get(document.body).addClass("x-body-masked");
2167         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2168         this.maskEl.show();
2169         this.el.setStyle('zIndex', '10001');
2170         this.fireEvent('show', this);
2171         
2172         
2173     },
2174     hide : function()
2175     {
2176         Roo.log('Modal hide?!');
2177         this.maskEl.hide();
2178         Roo.get(document.body).removeClass("x-body-masked");
2179         this.el.removeClass('on');
2180         this.el.addClass('fade');
2181         this.el.setStyle('display', 'none');
2182         this.fireEvent('hide', this);
2183     },
2184     
2185     addButton : function(str, cb)
2186     {
2187          
2188         
2189         var b = Roo.apply({}, { html : str } );
2190         b.xns = b.xns || Roo.bootstrap;
2191         b.xtype = b.xtype || 'Button';
2192         if (typeof(b.listeners) == 'undefined') {
2193             b.listeners = { click : cb.createDelegate(this)  };
2194         }
2195         
2196         var btn = Roo.factory(b);
2197            
2198         btn.onRender(this.el.select('.modal-footer div').first());
2199         
2200         return btn;   
2201        
2202     },
2203     
2204     setDefaultButton : function(btn)
2205     {
2206         //this.el.select('.modal-footer').()
2207     },
2208     resizeTo: function(w,h)
2209     {
2210         // skip..
2211     },
2212     setContentSize  : function(w, h)
2213     {
2214         
2215     },
2216     onButtonClick: function(btn,e)
2217     {
2218         //Roo.log([a,b,c]);
2219         this.fireEvent('btnclick', btn.name, e);
2220     },
2221     setTitle: function(str) {
2222         this.el.select('.modal-title',true).first().dom.innerHTML = str;
2223         
2224     }
2225 });
2226
2227
2228 Roo.apply(Roo.bootstrap.Modal,  {
2229     /**
2230          * Button config that displays a single OK button
2231          * @type Object
2232          */
2233         OK :  [{
2234             name : 'ok',
2235             weight : 'primary',
2236             html : 'OK'
2237         }], 
2238         /**
2239          * Button config that displays Yes and No buttons
2240          * @type Object
2241          */
2242         YESNO : [
2243             {
2244                 name  : 'no',
2245                 html : 'No'
2246             },
2247             {
2248                 name  :'yes',
2249                 weight : 'primary',
2250                 html : 'Yes'
2251             }
2252         ],
2253         
2254         /**
2255          * Button config that displays OK and Cancel buttons
2256          * @type Object
2257          */
2258         OKCANCEL : [
2259             {
2260                name : 'cancel',
2261                 html : 'Cancel'
2262             },
2263             {
2264                 name : 'ok',
2265                 weight : 'primary',
2266                 html : 'OK'
2267             }
2268         ],
2269         /**
2270          * Button config that displays Yes, No and Cancel buttons
2271          * @type Object
2272          */
2273         YESNOCANCEL : [
2274             {
2275                 name : 'yes',
2276                 weight : 'primary',
2277                 html : 'Yes'
2278             },
2279             {
2280                 name : 'no',
2281                 html : 'No'
2282             },
2283             {
2284                 name : 'cancel',
2285                 html : 'Cancel'
2286             }
2287         ]
2288 });
2289  /*
2290  * - LGPL
2291  *
2292  * messagebox - can be used as a replace
2293  * 
2294  */
2295 /**
2296  * @class Roo.MessageBox
2297  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2298  * Example usage:
2299  *<pre><code>
2300 // Basic alert:
2301 Roo.Msg.alert('Status', 'Changes saved successfully.');
2302
2303 // Prompt for user data:
2304 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2305     if (btn == 'ok'){
2306         // process text value...
2307     }
2308 });
2309
2310 // Show a dialog using config options:
2311 Roo.Msg.show({
2312    title:'Save Changes?',
2313    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2314    buttons: Roo.Msg.YESNOCANCEL,
2315    fn: processResult,
2316    animEl: 'elId'
2317 });
2318 </code></pre>
2319  * @singleton
2320  */
2321 Roo.bootstrap.MessageBox = function(){
2322     var dlg, opt, mask, waitTimer;
2323     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2324     var buttons, activeTextEl, bwidth;
2325
2326     
2327     // private
2328     var handleButton = function(button){
2329         dlg.hide();
2330         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2331     };
2332
2333     // private
2334     var handleHide = function(){
2335         if(opt && opt.cls){
2336             dlg.el.removeClass(opt.cls);
2337         }
2338         //if(waitTimer){
2339         //    Roo.TaskMgr.stop(waitTimer);
2340         //    waitTimer = null;
2341         //}
2342     };
2343
2344     // private
2345     var updateButtons = function(b){
2346         var width = 0;
2347         if(!b){
2348             buttons["ok"].hide();
2349             buttons["cancel"].hide();
2350             buttons["yes"].hide();
2351             buttons["no"].hide();
2352             //dlg.footer.dom.style.display = 'none';
2353             return width;
2354         }
2355         dlg.footer.dom.style.display = '';
2356         for(var k in buttons){
2357             if(typeof buttons[k] != "function"){
2358                 if(b[k]){
2359                     buttons[k].show();
2360                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2361                     width += buttons[k].el.getWidth()+15;
2362                 }else{
2363                     buttons[k].hide();
2364                 }
2365             }
2366         }
2367         return width;
2368     };
2369
2370     // private
2371     var handleEsc = function(d, k, e){
2372         if(opt && opt.closable !== false){
2373             dlg.hide();
2374         }
2375         if(e){
2376             e.stopEvent();
2377         }
2378     };
2379
2380     return {
2381         /**
2382          * Returns a reference to the underlying {@link Roo.BasicDialog} element
2383          * @return {Roo.BasicDialog} The BasicDialog element
2384          */
2385         getDialog : function(){
2386            if(!dlg){
2387                 dlg = new Roo.bootstrap.Modal( {
2388                     //draggable: true,
2389                     //resizable:false,
2390                     //constraintoviewport:false,
2391                     //fixedcenter:true,
2392                     //collapsible : false,
2393                     //shim:true,
2394                     //modal: true,
2395                   //  width:400,
2396                   //  height:100,
2397                     //buttonAlign:"center",
2398                     closeClick : function(){
2399                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2400                             handleButton("no");
2401                         }else{
2402                             handleButton("cancel");
2403                         }
2404                     }
2405                 });
2406                 dlg.render();
2407                 dlg.on("hide", handleHide);
2408                 mask = dlg.mask;
2409                 //dlg.addKeyListener(27, handleEsc);
2410                 buttons = {};
2411                 this.buttons = buttons;
2412                 var bt = this.buttonText;
2413                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2414                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2415                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2416                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2417                 Roo.log(buttons)
2418                 bodyEl = dlg.body.createChild({
2419
2420                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2421                         '<textarea class="roo-mb-textarea"></textarea>' +
2422                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
2423                 });
2424                 msgEl = bodyEl.dom.firstChild;
2425                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2426                 textboxEl.enableDisplayMode();
2427                 textboxEl.addKeyListener([10,13], function(){
2428                     if(dlg.isVisible() && opt && opt.buttons){
2429                         if(opt.buttons.ok){
2430                             handleButton("ok");
2431                         }else if(opt.buttons.yes){
2432                             handleButton("yes");
2433                         }
2434                     }
2435                 });
2436                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2437                 textareaEl.enableDisplayMode();
2438                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2439                 progressEl.enableDisplayMode();
2440                 var pf = progressEl.dom.firstChild;
2441                 if (pf) {
2442                     pp = Roo.get(pf.firstChild);
2443                     pp.setHeight(pf.offsetHeight);
2444                 }
2445                 
2446             }
2447             return dlg;
2448         },
2449
2450         /**
2451          * Updates the message box body text
2452          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2453          * the XHTML-compliant non-breaking space character '&amp;#160;')
2454          * @return {Roo.MessageBox} This message box
2455          */
2456         updateText : function(text){
2457             if(!dlg.isVisible() && !opt.width){
2458                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2459             }
2460             msgEl.innerHTML = text || '&#160;';
2461       
2462             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2463             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2464             var w = Math.max(
2465                     Math.min(opt.width || cw , this.maxWidth), 
2466                     Math.max(opt.minWidth || this.minWidth, bwidth)
2467             );
2468             if(opt.prompt){
2469                 activeTextEl.setWidth(w);
2470             }
2471             if(dlg.isVisible()){
2472                 dlg.fixedcenter = false;
2473             }
2474             // to big, make it scroll. = But as usual stupid IE does not support
2475             // !important..
2476             
2477             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2478                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2479                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2480             } else {
2481                 bodyEl.dom.style.height = '';
2482                 bodyEl.dom.style.overflowY = '';
2483             }
2484             if (cw > w) {
2485                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2486             } else {
2487                 bodyEl.dom.style.overflowX = '';
2488             }
2489             
2490             dlg.setContentSize(w, bodyEl.getHeight());
2491             if(dlg.isVisible()){
2492                 dlg.fixedcenter = true;
2493             }
2494             return this;
2495         },
2496
2497         /**
2498          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
2499          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2500          * @param {Number} value Any number between 0 and 1 (e.g., .5)
2501          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2502          * @return {Roo.MessageBox} This message box
2503          */
2504         updateProgress : function(value, text){
2505             if(text){
2506                 this.updateText(text);
2507             }
2508             if (pp) { // weird bug on my firefox - for some reason this is not defined
2509                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2510             }
2511             return this;
2512         },        
2513
2514         /**
2515          * Returns true if the message box is currently displayed
2516          * @return {Boolean} True if the message box is visible, else false
2517          */
2518         isVisible : function(){
2519             return dlg && dlg.isVisible();  
2520         },
2521
2522         /**
2523          * Hides the message box if it is displayed
2524          */
2525         hide : function(){
2526             if(this.isVisible()){
2527                 dlg.hide();
2528             }  
2529         },
2530
2531         /**
2532          * Displays a new message box, or reinitializes an existing message box, based on the config options
2533          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2534          * The following config object properties are supported:
2535          * <pre>
2536 Property    Type             Description
2537 ----------  ---------------  ------------------------------------------------------------------------------------
2538 animEl            String/Element   An id or Element from which the message box should animate as it opens and
2539                                    closes (defaults to undefined)
2540 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
2541                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
2542 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
2543                                    progress and wait dialogs will ignore this property and always hide the
2544                                    close button as they can only be closed programmatically.
2545 cls               String           A custom CSS class to apply to the message box element
2546 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
2547                                    displayed (defaults to 75)
2548 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
2549                                    function will be btn (the name of the button that was clicked, if applicable,
2550                                    e.g. "ok"), and text (the value of the active text field, if applicable).
2551                                    Progress and wait dialogs will ignore this option since they do not respond to
2552                                    user actions and can only be closed programmatically, so any required function
2553                                    should be called by the same code after it closes the dialog.
2554 icon              String           A CSS class that provides a background image to be used as an icon for
2555                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
2556 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
2557 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
2558 modal             Boolean          False to allow user interaction with the page while the message box is
2559                                    displayed (defaults to true)
2560 msg               String           A string that will replace the existing message box body text (defaults
2561                                    to the XHTML-compliant non-breaking space character '&#160;')
2562 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
2563 progress          Boolean          True to display a progress bar (defaults to false)
2564 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
2565 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
2566 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
2567 title             String           The title text
2568 value             String           The string value to set into the active textbox element if displayed
2569 wait              Boolean          True to display a progress bar (defaults to false)
2570 width             Number           The width of the dialog in pixels
2571 </pre>
2572          *
2573          * Example usage:
2574          * <pre><code>
2575 Roo.Msg.show({
2576    title: 'Address',
2577    msg: 'Please enter your address:',
2578    width: 300,
2579    buttons: Roo.MessageBox.OKCANCEL,
2580    multiline: true,
2581    fn: saveAddress,
2582    animEl: 'addAddressBtn'
2583 });
2584 </code></pre>
2585          * @param {Object} config Configuration options
2586          * @return {Roo.MessageBox} This message box
2587          */
2588         show : function(options)
2589         {
2590             
2591             // this causes nightmares if you show one dialog after another
2592             // especially on callbacks..
2593              
2594             if(this.isVisible()){
2595                 
2596                 this.hide();
2597                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
2598                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
2599                 Roo.log("New Dialog Message:" +  options.msg )
2600                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
2601                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
2602                 
2603             }
2604             var d = this.getDialog();
2605             opt = options;
2606             d.setTitle(opt.title || "&#160;");
2607             d.close.setDisplayed(opt.closable !== false);
2608             activeTextEl = textboxEl;
2609             opt.prompt = opt.prompt || (opt.multiline ? true : false);
2610             if(opt.prompt){
2611                 if(opt.multiline){
2612                     textboxEl.hide();
2613                     textareaEl.show();
2614                     textareaEl.setHeight(typeof opt.multiline == "number" ?
2615                         opt.multiline : this.defaultTextHeight);
2616                     activeTextEl = textareaEl;
2617                 }else{
2618                     textboxEl.show();
2619                     textareaEl.hide();
2620                 }
2621             }else{
2622                 textboxEl.hide();
2623                 textareaEl.hide();
2624             }
2625             progressEl.setDisplayed(opt.progress === true);
2626             this.updateProgress(0);
2627             activeTextEl.dom.value = opt.value || "";
2628             if(opt.prompt){
2629                 dlg.setDefaultButton(activeTextEl);
2630             }else{
2631                 var bs = opt.buttons;
2632                 var db = null;
2633                 if(bs && bs.ok){
2634                     db = buttons["ok"];
2635                 }else if(bs && bs.yes){
2636                     db = buttons["yes"];
2637                 }
2638                 dlg.setDefaultButton(db);
2639             }
2640             bwidth = updateButtons(opt.buttons);
2641             this.updateText(opt.msg);
2642             if(opt.cls){
2643                 d.el.addClass(opt.cls);
2644             }
2645             d.proxyDrag = opt.proxyDrag === true;
2646             d.modal = opt.modal !== false;
2647             d.mask = opt.modal !== false ? mask : false;
2648             if(!d.isVisible()){
2649                 // force it to the end of the z-index stack so it gets a cursor in FF
2650                 document.body.appendChild(dlg.el.dom);
2651                 d.animateTarget = null;
2652                 d.show(options.animEl);
2653             }
2654             return this;
2655         },
2656
2657         /**
2658          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
2659          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
2660          * and closing the message box when the process is complete.
2661          * @param {String} title The title bar text
2662          * @param {String} msg The message box body text
2663          * @return {Roo.MessageBox} This message box
2664          */
2665         progress : function(title, msg){
2666             this.show({
2667                 title : title,
2668                 msg : msg,
2669                 buttons: false,
2670                 progress:true,
2671                 closable:false,
2672                 minWidth: this.minProgressWidth,
2673                 modal : true
2674             });
2675             return this;
2676         },
2677
2678         /**
2679          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
2680          * If a callback function is passed it will be called after the user clicks the button, and the
2681          * id of the button that was clicked will be passed as the only parameter to the callback
2682          * (could also be the top-right close button).
2683          * @param {String} title The title bar text
2684          * @param {String} msg The message box body text
2685          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2686          * @param {Object} scope (optional) The scope of the callback function
2687          * @return {Roo.MessageBox} This message box
2688          */
2689         alert : function(title, msg, fn, scope){
2690             this.show({
2691                 title : title,
2692                 msg : msg,
2693                 buttons: this.OK,
2694                 fn: fn,
2695                 scope : scope,
2696                 modal : true
2697             });
2698             return this;
2699         },
2700
2701         /**
2702          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
2703          * interaction while waiting for a long-running process to complete that does not have defined intervals.
2704          * You are responsible for closing the message box when the process is complete.
2705          * @param {String} msg The message box body text
2706          * @param {String} title (optional) The title bar text
2707          * @return {Roo.MessageBox} This message box
2708          */
2709         wait : function(msg, title){
2710             this.show({
2711                 title : title,
2712                 msg : msg,
2713                 buttons: false,
2714                 closable:false,
2715                 progress:true,
2716                 modal:true,
2717                 width:300,
2718                 wait:true
2719             });
2720             waitTimer = Roo.TaskMgr.start({
2721                 run: function(i){
2722                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
2723                 },
2724                 interval: 1000
2725             });
2726             return this;
2727         },
2728
2729         /**
2730          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
2731          * If a callback function is passed it will be called after the user clicks either button, and the id of the
2732          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
2733          * @param {String} title The title bar text
2734          * @param {String} msg The message box body text
2735          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2736          * @param {Object} scope (optional) The scope of the callback function
2737          * @return {Roo.MessageBox} This message box
2738          */
2739         confirm : function(title, msg, fn, scope){
2740             this.show({
2741                 title : title,
2742                 msg : msg,
2743                 buttons: this.YESNO,
2744                 fn: fn,
2745                 scope : scope,
2746                 modal : true
2747             });
2748             return this;
2749         },
2750
2751         /**
2752          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
2753          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
2754          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
2755          * (could also be the top-right close button) and the text that was entered will be passed as the two
2756          * parameters to the callback.
2757          * @param {String} title The title bar text
2758          * @param {String} msg The message box body text
2759          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2760          * @param {Object} scope (optional) The scope of the callback function
2761          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
2762          * property, or the height in pixels to create the textbox (defaults to false / single-line)
2763          * @return {Roo.MessageBox} This message box
2764          */
2765         prompt : function(title, msg, fn, scope, multiline){
2766             this.show({
2767                 title : title,
2768                 msg : msg,
2769                 buttons: this.OKCANCEL,
2770                 fn: fn,
2771                 minWidth:250,
2772                 scope : scope,
2773                 prompt:true,
2774                 multiline: multiline,
2775                 modal : true
2776             });
2777             return this;
2778         },
2779
2780         /**
2781          * Button config that displays a single OK button
2782          * @type Object
2783          */
2784         OK : {ok:true},
2785         /**
2786          * Button config that displays Yes and No buttons
2787          * @type Object
2788          */
2789         YESNO : {yes:true, no:true},
2790         /**
2791          * Button config that displays OK and Cancel buttons
2792          * @type Object
2793          */
2794         OKCANCEL : {ok:true, cancel:true},
2795         /**
2796          * Button config that displays Yes, No and Cancel buttons
2797          * @type Object
2798          */
2799         YESNOCANCEL : {yes:true, no:true, cancel:true},
2800
2801         /**
2802          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
2803          * @type Number
2804          */
2805         defaultTextHeight : 75,
2806         /**
2807          * The maximum width in pixels of the message box (defaults to 600)
2808          * @type Number
2809          */
2810         maxWidth : 600,
2811         /**
2812          * The minimum width in pixels of the message box (defaults to 100)
2813          * @type Number
2814          */
2815         minWidth : 100,
2816         /**
2817          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
2818          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
2819          * @type Number
2820          */
2821         minProgressWidth : 250,
2822         /**
2823          * An object containing the default button text strings that can be overriden for localized language support.
2824          * Supported properties are: ok, cancel, yes and no.
2825          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
2826          * @type Object
2827          */
2828         buttonText : {
2829             ok : "OK",
2830             cancel : "Cancel",
2831             yes : "Yes",
2832             no : "No"
2833         }
2834     };
2835 }();
2836
2837 /**
2838  * Shorthand for {@link Roo.MessageBox}
2839  */
2840 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox 
2841 Roo.Msg = Roo.Msg || Roo.MessageBox;
2842 /*
2843  * - LGPL
2844  *
2845  * navbar
2846  * 
2847  */
2848
2849 /**
2850  * @class Roo.bootstrap.Navbar
2851  * @extends Roo.bootstrap.Component
2852  * Bootstrap Navbar class
2853
2854  * @constructor
2855  * Create a new Navbar
2856  * @param {Object} config The config object
2857  */
2858
2859
2860 Roo.bootstrap.Navbar = function(config){
2861     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
2862     
2863 };
2864
2865 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
2866     
2867     
2868    
2869     // private
2870     navItems : false,
2871     loadMask : false,
2872     
2873     
2874     getAutoCreate : function(){
2875         
2876         
2877         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
2878         
2879     },
2880     
2881     initEvents :function ()
2882     {
2883         //Roo.log(this.el.select('.navbar-toggle',true));
2884         this.el.select('.navbar-toggle',true).on('click', function() {
2885            // Roo.log('click');
2886             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
2887         }, this);
2888         
2889         var mark = {
2890             tag: "div",
2891             cls:"x-dlg-mask"
2892         }
2893         
2894         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
2895         
2896         var size = this.el.getSize();
2897         this.maskEl.setSize(size.width, size.height);
2898         this.maskEl.enableDisplayMode("block");
2899         this.maskEl.hide();
2900         
2901         if(this.loadMask){
2902             this.maskEl.show();
2903         }
2904     },
2905     
2906     
2907     getChildContainer : function()
2908     {
2909         if (this.el.select('.collapse').getCount()) {
2910             return this.el.select('.collapse',true).first();
2911         }
2912         
2913         return this.el;
2914     },
2915     
2916     mask : function()
2917     {
2918         this.maskEl.show();
2919     },
2920     
2921     unmask : function()
2922     {
2923         this.maskEl.hide();
2924     } 
2925     
2926     
2927     
2928     
2929 });
2930
2931
2932
2933  
2934
2935  /*
2936  * - LGPL
2937  *
2938  * navbar
2939  * 
2940  */
2941
2942 /**
2943  * @class Roo.bootstrap.NavSimplebar
2944  * @extends Roo.bootstrap.Navbar
2945  * Bootstrap Sidebar class
2946  *
2947  * @cfg {Boolean} inverse is inverted color
2948  * 
2949  * @cfg {String} type (nav | pills | tabs)
2950  * @cfg {Boolean} arrangement stacked | justified
2951  * @cfg {String} align (left | right) alignment
2952  * 
2953  * @cfg {Boolean} main (true|false) main nav bar? default false
2954  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
2955  * 
2956  * @cfg {String} tag (header|footer|nav|div) default is nav 
2957
2958  * 
2959  * 
2960  * 
2961  * @constructor
2962  * Create a new Sidebar
2963  * @param {Object} config The config object
2964  */
2965
2966
2967 Roo.bootstrap.NavSimplebar = function(config){
2968     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
2969 };
2970
2971 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
2972     
2973     inverse: false,
2974     
2975     type: false,
2976     arrangement: '',
2977     align : false,
2978     
2979     
2980     
2981     main : false,
2982     
2983     
2984     tag : false,
2985     
2986     
2987     getAutoCreate : function(){
2988         
2989         
2990         var cfg = {
2991             tag : this.tag || 'div',
2992             cls : 'navbar'
2993         };
2994           
2995         
2996         cfg.cn = [
2997             {
2998                 cls: 'nav',
2999                 tag : 'ul'
3000             }
3001         ];
3002         
3003          
3004         this.type = this.type || 'nav';
3005         if (['tabs','pills'].indexOf(this.type)!==-1) {
3006             cfg.cn[0].cls += ' nav-' + this.type
3007         
3008         
3009         } else {
3010             if (this.type!=='nav') {
3011                 Roo.log('nav type must be nav/tabs/pills')
3012             }
3013             cfg.cn[0].cls += ' navbar-nav'
3014         }
3015         
3016         
3017         
3018         
3019         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3020             cfg.cn[0].cls += ' nav-' + this.arrangement;
3021         }
3022         
3023         
3024         if (this.align === 'right') {
3025             cfg.cn[0].cls += ' navbar-right';
3026         }
3027         
3028         if (this.inverse) {
3029             cfg.cls += ' navbar-inverse';
3030             
3031         }
3032         
3033         
3034         return cfg;
3035     
3036         
3037     }
3038     
3039     
3040     
3041 });
3042
3043
3044
3045  
3046
3047  
3048        /*
3049  * - LGPL
3050  *
3051  * navbar
3052  * 
3053  */
3054
3055 /**
3056  * @class Roo.bootstrap.NavHeaderbar
3057  * @extends Roo.bootstrap.NavSimplebar
3058  * Bootstrap Sidebar class
3059  *
3060  * @cfg {String} brand what is brand
3061  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3062  * @cfg {String} brand_href href of the brand
3063  * @cfg {Boolean} srButton generate the sr-only button (true | false) default true
3064  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3065  * 
3066  * @constructor
3067  * Create a new Sidebar
3068  * @param {Object} config The config object
3069  */
3070
3071
3072 Roo.bootstrap.NavHeaderbar = function(config){
3073     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3074 };
3075
3076 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3077     
3078     position: '',
3079     brand: '',
3080     brand_href: false,
3081     srButton : true,
3082     autohide : false,
3083     
3084     getAutoCreate : function(){
3085         
3086         var   cfg = {
3087             tag: this.nav || 'nav',
3088             cls: 'navbar',
3089             role: 'navigation',
3090             cn: []
3091         };
3092         
3093         if(this.srButton){
3094             cfg.cn.push({
3095                 tag: 'div',
3096                 cls: 'navbar-header',
3097                 cn: [
3098                     {
3099                         tag: 'button',
3100                         type: 'button',
3101                         cls: 'navbar-toggle',
3102                         'data-toggle': 'collapse',
3103                         cn: [
3104                             {
3105                                 tag: 'span',
3106                                 cls: 'sr-only',
3107                                 html: 'Toggle navigation'
3108                             },
3109                             {
3110                                 tag: 'span',
3111                                 cls: 'icon-bar'
3112                             },
3113                             {
3114                                 tag: 'span',
3115                                 cls: 'icon-bar'
3116                             },
3117                             {
3118                                 tag: 'span',
3119                                 cls: 'icon-bar'
3120                             }
3121                         ]
3122                     }
3123                 ]
3124             });
3125         }
3126         
3127         cfg.cn.push({
3128             tag: 'div',
3129             cls: 'collapse navbar-collapse',
3130             cn : []
3131         });
3132         
3133         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3134         
3135         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3136             cfg.cls += ' navbar-' + this.position;
3137             
3138             // tag can override this..
3139             
3140             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3141         }
3142         
3143         if (this.brand !== '') {
3144             cfg.cn[0].cn.push({
3145                 tag: 'a',
3146                 href: this.brand_href ? this.brand_href : '#',
3147                 cls: 'navbar-brand',
3148                 cn: [
3149                 this.brand
3150                 ]
3151             });
3152         }
3153         
3154         if(this.main){
3155             cfg.cls += ' main-nav';
3156         }
3157         
3158         
3159         return cfg;
3160
3161         
3162     },
3163     initEvents : function()
3164     {
3165         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3166         
3167         if (this.autohide) {
3168             
3169             var prevScroll = 0;
3170             var ft = this.el;
3171             
3172             Roo.get(document).on('scroll',function(e) {
3173                 var ns = Roo.get(document).getScroll().top;
3174                 var os = prevScroll;
3175                 prevScroll = ns;
3176                 
3177                 if(ns > os){
3178                     ft.removeClass('slideDown');
3179                     ft.addClass('slideUp');
3180                     return;
3181                 }
3182                 ft.removeClass('slideUp');
3183                 ft.addClass('slideDown');
3184                  
3185               
3186           },this);
3187         }
3188     }    
3189           
3190       
3191     
3192     
3193 });
3194
3195
3196
3197  
3198
3199  /*
3200  * - LGPL
3201  *
3202  * navbar
3203  * 
3204  */
3205
3206 /**
3207  * @class Roo.bootstrap.NavSidebar
3208  * @extends Roo.bootstrap.Navbar
3209  * Bootstrap Sidebar class
3210  * 
3211  * @constructor
3212  * Create a new Sidebar
3213  * @param {Object} config The config object
3214  */
3215
3216
3217 Roo.bootstrap.NavSidebar = function(config){
3218     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3219 };
3220
3221 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3222     
3223     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3224     
3225     getAutoCreate : function(){
3226         
3227         
3228         return  {
3229             tag: 'div',
3230             cls: 'sidebar sidebar-nav'
3231         };
3232     
3233         
3234     }
3235     
3236     
3237     
3238 });
3239
3240
3241
3242  
3243
3244  /*
3245  * - LGPL
3246  *
3247  * nav group
3248  * 
3249  */
3250
3251 /**
3252  * @class Roo.bootstrap.NavGroup
3253  * @extends Roo.bootstrap.Component
3254  * Bootstrap NavGroup class
3255  * @cfg {String} align left | right
3256  * @cfg {Boolean} inverse false | true
3257  * @cfg {String} type (nav|pills|tab) default nav
3258  * @cfg {String} navId - reference Id for navbar.
3259
3260  * 
3261  * @constructor
3262  * Create a new nav group
3263  * @param {Object} config The config object
3264  */
3265
3266 Roo.bootstrap.NavGroup = function(config){
3267     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3268     this.navItems = [];
3269    
3270     Roo.bootstrap.NavGroup.register(this);
3271      this.addEvents({
3272         /**
3273              * @event changed
3274              * Fires when the active item changes
3275              * @param {Roo.bootstrap.NavGroup} this
3276              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3277              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3278          */
3279         'changed': true
3280      });
3281     
3282 };
3283
3284 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3285     
3286     align: '',
3287     inverse: false,
3288     form: false,
3289     type: 'nav',
3290     navId : '',
3291     // private
3292     
3293     navItems : false, 
3294     
3295     getAutoCreate : function()
3296     {
3297         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3298         
3299         cfg = {
3300             tag : 'ul',
3301             cls: 'nav' 
3302         }
3303         
3304         if (['tabs','pills'].indexOf(this.type)!==-1) {
3305             cfg.cls += ' nav-' + this.type
3306         } else {
3307             if (this.type!=='nav') {
3308                 Roo.log('nav type must be nav/tabs/pills')
3309             }
3310             cfg.cls += ' navbar-nav'
3311         }
3312         
3313         if (this.parent().sidebar) {
3314             cfg = {
3315                 tag: 'ul',
3316                 cls: 'dashboard-menu sidebar-menu'
3317             }
3318             
3319             return cfg;
3320         }
3321         
3322         if (this.form === true) {
3323             cfg = {
3324                 tag: 'form',
3325                 cls: 'navbar-form'
3326             }
3327             
3328             if (this.align === 'right') {
3329                 cfg.cls += ' navbar-right';
3330             } else {
3331                 cfg.cls += ' navbar-left';
3332             }
3333         }
3334         
3335         if (this.align === 'right') {
3336             cfg.cls += ' navbar-right';
3337         }
3338         
3339         if (this.inverse) {
3340             cfg.cls += ' navbar-inverse';
3341             
3342         }
3343         
3344         
3345         return cfg;
3346     },
3347     /**
3348     * sets the active Navigation item
3349     * @param {Roo.bootstrap.NavItem} the new current navitem
3350     */
3351     setActiveItem : function(item)
3352     {
3353         var prev = false;
3354         Roo.each(this.navItems, function(v){
3355             if (v == item) {
3356                 return ;
3357             }
3358             if (v.isActive()) {
3359                 v.setActive(false, true);
3360                 prev = v;
3361                 
3362             }
3363             
3364         });
3365
3366         item.setActive(true, true);
3367         this.fireEvent('changed', this, item, prev);
3368         
3369         
3370     },
3371     /**
3372     * gets the active Navigation item
3373     * @return {Roo.bootstrap.NavItem} the current navitem
3374     */
3375     getActive : function()
3376     {
3377         
3378         var prev = false;
3379         Roo.each(this.navItems, function(v){
3380             
3381             if (v.isActive()) {
3382                 prev = v;
3383                 
3384             }
3385             
3386         });
3387         return prev;
3388     },
3389     
3390     indexOfNav : function()
3391     {
3392         
3393         var prev = false;
3394         Roo.each(this.navItems, function(v,i){
3395             
3396             if (v.isActive()) {
3397                 prev = i;
3398                 
3399             }
3400             
3401         });
3402         return prev;
3403     },
3404     /**
3405     * adds a Navigation item
3406     * @param {Roo.bootstrap.NavItem} the navitem to add
3407     */
3408     addItem : function(cfg)
3409     {
3410         var cn = new Roo.bootstrap.NavItem(cfg);
3411         this.register(cn);
3412         cn.parentId = this.id;
3413         cn.onRender(this.el, null);
3414         return cn;
3415     },
3416     /**
3417     * register a Navigation item
3418     * @param {Roo.bootstrap.NavItem} the navitem to add
3419     */
3420     register : function(item)
3421     {
3422         this.navItems.push( item);
3423         item.navId = this.navId;
3424     
3425     },
3426   
3427     
3428     getNavItem: function(tabId)
3429     {
3430         var ret = false;
3431         Roo.each(this.navItems, function(e) {
3432             if (e.tabId == tabId) {
3433                ret =  e;
3434                return false;
3435             }
3436             return true;
3437             
3438         });
3439         return ret;
3440     },
3441     
3442     setActiveNext : function()
3443     {
3444         var i = this.indexOfNav(this.getActive());
3445         if (i > this.navItems.length) {
3446             return;
3447         }
3448         this.setActiveItem(this.navItems[i+1]);
3449     },
3450     setActivePrev : function()
3451     {
3452         var i = this.indexOfNav(this.getActive());
3453         if (i  < 1) {
3454             return;
3455         }
3456         this.setActiveItem(this.navItems[i-1]);
3457     },
3458     clearWasActive : function(except) {
3459         Roo.each(this.navItems, function(e) {
3460             if (e.tabId != except.tabId && e.was_active) {
3461                e.was_active = false;
3462                return false;
3463             }
3464             return true;
3465             
3466         });
3467     },
3468     getWasActive : function ()
3469     {
3470         var r = false;
3471         Roo.each(this.navItems, function(e) {
3472             if (e.was_active) {
3473                r = e;
3474                return false;
3475             }
3476             return true;
3477             
3478         });
3479         return r;
3480     }
3481     
3482     
3483 });
3484
3485  
3486 Roo.apply(Roo.bootstrap.NavGroup, {
3487     
3488     groups: {},
3489      /**
3490     * register a Navigation Group
3491     * @param {Roo.bootstrap.NavGroup} the navgroup to add
3492     */
3493     register : function(navgrp)
3494     {
3495         this.groups[navgrp.navId] = navgrp;
3496         
3497     },
3498     /**
3499     * fetch a Navigation Group based on the navigation ID
3500     * @param {string} the navgroup to add
3501     * @returns {Roo.bootstrap.NavGroup} the navgroup 
3502     */
3503     get: function(navId) {
3504         if (typeof(this.groups[navId]) == 'undefined') {
3505             return false;
3506             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
3507         }
3508         return this.groups[navId] ;
3509     }
3510     
3511     
3512     
3513 });
3514
3515  /*
3516  * - LGPL
3517  *
3518  * row
3519  * 
3520  */
3521
3522 /**
3523  * @class Roo.bootstrap.NavItem
3524  * @extends Roo.bootstrap.Component
3525  * Bootstrap Navbar.NavItem class
3526  * @cfg {String} href  link to
3527  * @cfg {String} html content of button
3528  * @cfg {String} badge text inside badge
3529  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
3530  * @cfg {String} glyphicon name of glyphicon
3531  * @cfg {String} icon name of font awesome icon
3532  * @cfg {Boolean} active Is item active
3533  * @cfg {Boolean} disabled Is item disabled
3534  
3535  * @cfg {Boolean} preventDefault (true | false) default false
3536  * @cfg {String} tabId the tab that this item activates.
3537  * @cfg {String} tagtype (a|span) render as a href or span?
3538   
3539  * @constructor
3540  * Create a new Navbar Item
3541  * @param {Object} config The config object
3542  */
3543 Roo.bootstrap.NavItem = function(config){
3544     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
3545     this.addEvents({
3546         // raw events
3547         /**
3548          * @event click
3549          * The raw click event for the entire grid.
3550          * @param {Roo.EventObject} e
3551          */
3552         "click" : true,
3553          /**
3554             * @event changed
3555             * Fires when the active item active state changes
3556             * @param {Roo.bootstrap.NavItem} this
3557             * @param {boolean} state the new state
3558              
3559          */
3560         'changed': true
3561     });
3562    
3563 };
3564
3565 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
3566     
3567     href: false,
3568     html: '',
3569     badge: '',
3570     icon: false,
3571     glyphicon: false,
3572     active: false,
3573     preventDefault : false,
3574     tabId : false,
3575     tagtype : 'a',
3576     disabled : false,
3577     
3578     was_active : false,
3579     
3580     getAutoCreate : function(){
3581          
3582         var cfg = {
3583             tag: 'li',
3584             cls: 'nav-item'
3585             
3586         }
3587         if (this.active) {
3588             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
3589         }
3590         if (this.disabled) {
3591             cfg.cls += ' disabled';
3592         }
3593         
3594         if (this.href || this.html || this.glyphicon || this.icon) {
3595             cfg.cn = [
3596                 {
3597                     tag: this.tagtype,
3598                     href : this.href || "#",
3599                     html: this.html || ''
3600                 }
3601             ];
3602             
3603             if (this.icon) {
3604                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
3605             }
3606
3607             if(this.glyphicon) {
3608                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
3609             }
3610             
3611             if (this.menu) {
3612                 
3613                 cfg.cn[0].html += " <span class='caret'></span>";
3614              
3615             }
3616             
3617             if (this.badge !== '') {
3618                  
3619                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
3620             }
3621         }
3622         
3623         
3624         
3625         return cfg;
3626     },
3627     initEvents: function() {
3628        // Roo.log('init events?');
3629        // Roo.log(this.el.dom);
3630         if (typeof (this.menu) != 'undefined') {
3631             this.menu.parentType = this.xtype;
3632             this.menu.triggerEl = this.el;
3633             this.addxtype(Roo.apply({}, this.menu));
3634         }
3635
3636        
3637         this.el.select('a',true).on('click', this.onClick, this);
3638         // at this point parent should be available..
3639         this.parent().register(this);
3640     },
3641     
3642     onClick : function(e)
3643     {
3644          
3645         if(this.preventDefault){
3646             e.preventDefault();
3647         }
3648         if (this.disabled) {
3649             return;
3650         }
3651         
3652         var tg = Roo.bootstrap.TabGroup.get(this.navId);
3653         if (tg && tg.transition) {
3654             Roo.log("waiting for the transitionend");
3655             return;
3656         }
3657         
3658         Roo.log("fire event clicked");
3659         if(this.fireEvent('click', this, e) === false){
3660             return;
3661         };
3662         
3663         if (['tabs','pills'].indexOf(this.parent().type)!==-1) {
3664             if (typeof(this.parent().setActiveItem) !== 'undefined') {
3665                 this.parent().setActiveItem(this);
3666             }
3667         } 
3668     },
3669     
3670     isActive: function () {
3671         return this.active
3672     },
3673     setActive : function(state, fire, is_was_active)
3674     {
3675         if (this.active && !state & this.navId) {
3676             this.was_active = true;
3677             var nv = Roo.bootstrap.NavGroup.get(this.navId);
3678             if (nv) {
3679                 nv.clearWasActive(this);
3680             }
3681             
3682         }
3683         this.active = state;
3684         
3685         if (!state ) {
3686             this.el.removeClass('active');
3687         } else if (!this.el.hasClass('active')) {
3688             this.el.addClass('active');
3689         }
3690         if (fire) {
3691             this.fireEvent('changed', this, state);
3692         }
3693         
3694         // show a panel if it's registered and related..
3695         
3696         if (!this.navId || !this.tabId || !state || is_was_active) {
3697             return;
3698         }
3699         
3700         var tg = Roo.bootstrap.TabGroup.get(this.navId);
3701         if (!tg) {
3702             return;
3703         }
3704         var pan = tg.getPanelByName(this.tabId);
3705         if (!pan) {
3706             return;
3707         }
3708         // if we can not flip to new panel - go back to old nav highlight..
3709         if (false == tg.showPanel(pan)) {
3710             var nv = Roo.bootstrap.NavGroup.get(this.navId);
3711             if (nv) {
3712                 var onav = nv.getWasActive();
3713                 if (onav) {
3714                     onav.setActive(true, false, true);
3715                 }
3716             }
3717             
3718         }
3719         
3720         
3721         
3722     },
3723      // this should not be here...
3724     setDisabled : function(state)
3725     {
3726         this.disabled = state;
3727         if (!state ) {
3728             this.el.removeClass('disabled');
3729         } else if (!this.el.hasClass('disabled')) {
3730             this.el.addClass('disabled');
3731         }
3732         
3733     }
3734 });
3735  
3736
3737  /*
3738  * - LGPL
3739  *
3740  * sidebar item
3741  *
3742  *  li
3743  *    <span> icon </span>
3744  *    <span> text </span>
3745  *    <span>badge </span>
3746  */
3747
3748 /**
3749  * @class Roo.bootstrap.NavSidebarItem
3750  * @extends Roo.bootstrap.NavItem
3751  * Bootstrap Navbar.NavSidebarItem class
3752  * @constructor
3753  * Create a new Navbar Button
3754  * @param {Object} config The config object
3755  */
3756 Roo.bootstrap.NavSidebarItem = function(config){
3757     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
3758     this.addEvents({
3759         // raw events
3760         /**
3761          * @event click
3762          * The raw click event for the entire grid.
3763          * @param {Roo.EventObject} e
3764          */
3765         "click" : true,
3766          /**
3767             * @event changed
3768             * Fires when the active item active state changes
3769             * @param {Roo.bootstrap.NavSidebarItem} this
3770             * @param {boolean} state the new state
3771              
3772          */
3773         'changed': true
3774     });
3775    
3776 };
3777
3778 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
3779     
3780     
3781     getAutoCreate : function(){
3782         
3783         
3784         var a = {
3785                 tag: 'a',
3786                 href : this.href || '#',
3787                 cls: '',
3788                 html : '',
3789                 cn : []
3790         };
3791         var cfg = {
3792             tag: 'li',
3793             cls: '',
3794             cn: [ a ]
3795         }
3796         var span = {
3797             tag: 'span',
3798             html : this.html || ''
3799         }
3800         
3801         
3802         if (this.active) {
3803             cfg.cls += ' active';
3804         }
3805         
3806         // left icon..
3807         if (this.glyphicon || this.icon) {
3808             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
3809             a.cn.push({ tag : 'i', cls : c }) ;
3810         }
3811         // html..
3812         a.cn.push(span);
3813         // then badge..
3814         if (this.badge !== '') {
3815             a.cn.push({ tag: 'span',  cls : 'badge pull-right ' + (this.badgecls || ''), html: this.badge }); 
3816         }
3817         // fi
3818         if (this.menu) {
3819             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
3820             a.cls += 'dropdown-toggle treeview' ;
3821             
3822         }
3823         
3824         
3825         
3826         return cfg;
3827          
3828            
3829     }
3830    
3831      
3832  
3833 });
3834  
3835
3836  /*
3837  * - LGPL
3838  *
3839  * row
3840  * 
3841  */
3842
3843 /**
3844  * @class Roo.bootstrap.Row
3845  * @extends Roo.bootstrap.Component
3846  * Bootstrap Row class (contains columns...)
3847  * 
3848  * @constructor
3849  * Create a new Row
3850  * @param {Object} config The config object
3851  */
3852
3853 Roo.bootstrap.Row = function(config){
3854     Roo.bootstrap.Row.superclass.constructor.call(this, config);
3855 };
3856
3857 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
3858     
3859     getAutoCreate : function(){
3860        return {
3861             cls: 'row clearfix'
3862        };
3863     }
3864     
3865     
3866 });
3867
3868  
3869
3870  /*
3871  * - LGPL
3872  *
3873  * element
3874  * 
3875  */
3876
3877 /**
3878  * @class Roo.bootstrap.Element
3879  * @extends Roo.bootstrap.Component
3880  * Bootstrap Element class
3881  * @cfg {String} html contents of the element
3882  * @cfg {String} tag tag of the element
3883  * @cfg {String} cls class of the element
3884  * 
3885  * @constructor
3886  * Create a new Element
3887  * @param {Object} config The config object
3888  */
3889
3890 Roo.bootstrap.Element = function(config){
3891     Roo.bootstrap.Element.superclass.constructor.call(this, config);
3892 };
3893
3894 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
3895     
3896     tag: 'div',
3897     cls: '',
3898     html: '',
3899      
3900     
3901     getAutoCreate : function(){
3902         
3903         var cfg = {
3904             tag: this.tag,
3905             cls: this.cls,
3906             html: this.html
3907         }
3908         
3909         
3910         
3911         return cfg;
3912     }
3913    
3914 });
3915
3916  
3917
3918  /*
3919  * - LGPL
3920  *
3921  * pagination
3922  * 
3923  */
3924
3925 /**
3926  * @class Roo.bootstrap.Pagination
3927  * @extends Roo.bootstrap.Component
3928  * Bootstrap Pagination class
3929  * @cfg {String} size xs | sm | md | lg
3930  * @cfg {Boolean} inverse false | true
3931  * 
3932  * @constructor
3933  * Create a new Pagination
3934  * @param {Object} config The config object
3935  */
3936
3937 Roo.bootstrap.Pagination = function(config){
3938     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
3939 };
3940
3941 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
3942     
3943     cls: false,
3944     size: false,
3945     inverse: false,
3946     
3947     getAutoCreate : function(){
3948         var cfg = {
3949             tag: 'ul',
3950                 cls: 'pagination'
3951         };
3952         if (this.inverse) {
3953             cfg.cls += ' inverse';
3954         }
3955         if (this.html) {
3956             cfg.html=this.html;
3957         }
3958         if (this.cls) {
3959             cfg.cls += " " + this.cls;
3960         }
3961         return cfg;
3962     }
3963    
3964 });
3965
3966  
3967
3968  /*
3969  * - LGPL
3970  *
3971  * Pagination item
3972  * 
3973  */
3974
3975
3976 /**
3977  * @class Roo.bootstrap.PaginationItem
3978  * @extends Roo.bootstrap.Component
3979  * Bootstrap PaginationItem class
3980  * @cfg {String} html text
3981  * @cfg {String} href the link
3982  * @cfg {Boolean} preventDefault (true | false) default true
3983  * @cfg {Boolean} active (true | false) default false
3984  * 
3985  * 
3986  * @constructor
3987  * Create a new PaginationItem
3988  * @param {Object} config The config object
3989  */
3990
3991
3992 Roo.bootstrap.PaginationItem = function(config){
3993     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
3994     this.addEvents({
3995         // raw events
3996         /**
3997          * @event click
3998          * The raw click event for the entire grid.
3999          * @param {Roo.EventObject} e
4000          */
4001         "click" : true
4002     });
4003 };
4004
4005 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4006     
4007     href : false,
4008     html : false,
4009     preventDefault: true,
4010     active : false,
4011     cls : false,
4012     
4013     getAutoCreate : function(){
4014         var cfg= {
4015             tag: 'li',
4016             cn: [
4017                 {
4018                     tag : 'a',
4019                     href : this.href ? this.href : '#',
4020                     html : this.html ? this.html : ''
4021                 }
4022             ]
4023         };
4024         
4025         if(this.cls){
4026             cfg.cls = this.cls;
4027         }
4028         
4029         if(this.active){
4030             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4031         }
4032         
4033         return cfg;
4034     },
4035     
4036     initEvents: function() {
4037         
4038         this.el.on('click', this.onClick, this);
4039         
4040     },
4041     onClick : function(e)
4042     {
4043         Roo.log('PaginationItem on click ');
4044         if(this.preventDefault){
4045             e.preventDefault();
4046         }
4047         
4048         this.fireEvent('click', this, e);
4049     }
4050    
4051 });
4052
4053  
4054
4055  /*
4056  * - LGPL
4057  *
4058  * slider
4059  * 
4060  */
4061
4062
4063 /**
4064  * @class Roo.bootstrap.Slider
4065  * @extends Roo.bootstrap.Component
4066  * Bootstrap Slider class
4067  *    
4068  * @constructor
4069  * Create a new Slider
4070  * @param {Object} config The config object
4071  */
4072
4073 Roo.bootstrap.Slider = function(config){
4074     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4075 };
4076
4077 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
4078     
4079     getAutoCreate : function(){
4080         
4081         var cfg = {
4082             tag: 'div',
4083             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4084             cn: [
4085                 {
4086                     tag: 'a',
4087                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
4088                 }
4089             ]
4090         }
4091         
4092         return cfg;
4093     }
4094    
4095 });
4096
4097  /*
4098  * Based on:
4099  * Ext JS Library 1.1.1
4100  * Copyright(c) 2006-2007, Ext JS, LLC.
4101  *
4102  * Originally Released Under LGPL - original licence link has changed is not relivant.
4103  *
4104  * Fork - LGPL
4105  * <script type="text/javascript">
4106  */
4107  
4108
4109 /**
4110  * @class Roo.grid.ColumnModel
4111  * @extends Roo.util.Observable
4112  * This is the default implementation of a ColumnModel used by the Grid. It defines
4113  * the columns in the grid.
4114  * <br>Usage:<br>
4115  <pre><code>
4116  var colModel = new Roo.grid.ColumnModel([
4117         {header: "Ticker", width: 60, sortable: true, locked: true},
4118         {header: "Company Name", width: 150, sortable: true},
4119         {header: "Market Cap.", width: 100, sortable: true},
4120         {header: "$ Sales", width: 100, sortable: true, renderer: money},
4121         {header: "Employees", width: 100, sortable: true, resizable: false}
4122  ]);
4123  </code></pre>
4124  * <p>
4125  
4126  * The config options listed for this class are options which may appear in each
4127  * individual column definition.
4128  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4129  * @constructor
4130  * @param {Object} config An Array of column config objects. See this class's
4131  * config objects for details.
4132 */
4133 Roo.grid.ColumnModel = function(config){
4134         /**
4135      * The config passed into the constructor
4136      */
4137     this.config = config;
4138     this.lookup = {};
4139
4140     // if no id, create one
4141     // if the column does not have a dataIndex mapping,
4142     // map it to the order it is in the config
4143     for(var i = 0, len = config.length; i < len; i++){
4144         var c = config[i];
4145         if(typeof c.dataIndex == "undefined"){
4146             c.dataIndex = i;
4147         }
4148         if(typeof c.renderer == "string"){
4149             c.renderer = Roo.util.Format[c.renderer];
4150         }
4151         if(typeof c.id == "undefined"){
4152             c.id = Roo.id();
4153         }
4154         if(c.editor && c.editor.xtype){
4155             c.editor  = Roo.factory(c.editor, Roo.grid);
4156         }
4157         if(c.editor && c.editor.isFormField){
4158             c.editor = new Roo.grid.GridEditor(c.editor);
4159         }
4160         this.lookup[c.id] = c;
4161     }
4162
4163     /**
4164      * The width of columns which have no width specified (defaults to 100)
4165      * @type Number
4166      */
4167     this.defaultWidth = 100;
4168
4169     /**
4170      * Default sortable of columns which have no sortable specified (defaults to false)
4171      * @type Boolean
4172      */
4173     this.defaultSortable = false;
4174
4175     this.addEvents({
4176         /**
4177              * @event widthchange
4178              * Fires when the width of a column changes.
4179              * @param {ColumnModel} this
4180              * @param {Number} columnIndex The column index
4181              * @param {Number} newWidth The new width
4182              */
4183             "widthchange": true,
4184         /**
4185              * @event headerchange
4186              * Fires when the text of a header changes.
4187              * @param {ColumnModel} this
4188              * @param {Number} columnIndex The column index
4189              * @param {Number} newText The new header text
4190              */
4191             "headerchange": true,
4192         /**
4193              * @event hiddenchange
4194              * Fires when a column is hidden or "unhidden".
4195              * @param {ColumnModel} this
4196              * @param {Number} columnIndex The column index
4197              * @param {Boolean} hidden true if hidden, false otherwise
4198              */
4199             "hiddenchange": true,
4200             /**
4201          * @event columnmoved
4202          * Fires when a column is moved.
4203          * @param {ColumnModel} this
4204          * @param {Number} oldIndex
4205          * @param {Number} newIndex
4206          */
4207         "columnmoved" : true,
4208         /**
4209          * @event columlockchange
4210          * Fires when a column's locked state is changed
4211          * @param {ColumnModel} this
4212          * @param {Number} colIndex
4213          * @param {Boolean} locked true if locked
4214          */
4215         "columnlockchange" : true
4216     });
4217     Roo.grid.ColumnModel.superclass.constructor.call(this);
4218 };
4219 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4220     /**
4221      * @cfg {String} header The header text to display in the Grid view.
4222      */
4223     /**
4224      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4225      * {@link Roo.data.Record} definition from which to draw the column's value. If not
4226      * specified, the column's index is used as an index into the Record's data Array.
4227      */
4228     /**
4229      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4230      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4231      */
4232     /**
4233      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4234      * Defaults to the value of the {@link #defaultSortable} property.
4235      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4236      */
4237     /**
4238      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
4239      */
4240     /**
4241      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
4242      */
4243     /**
4244      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4245      */
4246     /**
4247      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4248      */
4249     /**
4250      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4251      * given the cell's data value. See {@link #setRenderer}. If not specified, the
4252      * default renderer uses the raw data value. If an object is returned (bootstrap only)
4253      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4254      */
4255        /**
4256      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
4257      */
4258     /**
4259      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
4260      */
4261
4262     /**
4263      * Returns the id of the column at the specified index.
4264      * @param {Number} index The column index
4265      * @return {String} the id
4266      */
4267     getColumnId : function(index){
4268         return this.config[index].id;
4269     },
4270
4271     /**
4272      * Returns the column for a specified id.
4273      * @param {String} id The column id
4274      * @return {Object} the column
4275      */
4276     getColumnById : function(id){
4277         return this.lookup[id];
4278     },
4279
4280     
4281     /**
4282      * Returns the column for a specified dataIndex.
4283      * @param {String} dataIndex The column dataIndex
4284      * @return {Object|Boolean} the column or false if not found
4285      */
4286     getColumnByDataIndex: function(dataIndex){
4287         var index = this.findColumnIndex(dataIndex);
4288         return index > -1 ? this.config[index] : false;
4289     },
4290     
4291     /**
4292      * Returns the index for a specified column id.
4293      * @param {String} id The column id
4294      * @return {Number} the index, or -1 if not found
4295      */
4296     getIndexById : function(id){
4297         for(var i = 0, len = this.config.length; i < len; i++){
4298             if(this.config[i].id == id){
4299                 return i;
4300             }
4301         }
4302         return -1;
4303     },
4304     
4305     /**
4306      * Returns the index for a specified column dataIndex.
4307      * @param {String} dataIndex The column dataIndex
4308      * @return {Number} the index, or -1 if not found
4309      */
4310     
4311     findColumnIndex : function(dataIndex){
4312         for(var i = 0, len = this.config.length; i < len; i++){
4313             if(this.config[i].dataIndex == dataIndex){
4314                 return i;
4315             }
4316         }
4317         return -1;
4318     },
4319     
4320     
4321     moveColumn : function(oldIndex, newIndex){
4322         var c = this.config[oldIndex];
4323         this.config.splice(oldIndex, 1);
4324         this.config.splice(newIndex, 0, c);
4325         this.dataMap = null;
4326         this.fireEvent("columnmoved", this, oldIndex, newIndex);
4327     },
4328
4329     isLocked : function(colIndex){
4330         return this.config[colIndex].locked === true;
4331     },
4332
4333     setLocked : function(colIndex, value, suppressEvent){
4334         if(this.isLocked(colIndex) == value){
4335             return;
4336         }
4337         this.config[colIndex].locked = value;
4338         if(!suppressEvent){
4339             this.fireEvent("columnlockchange", this, colIndex, value);
4340         }
4341     },
4342
4343     getTotalLockedWidth : function(){
4344         var totalWidth = 0;
4345         for(var i = 0; i < this.config.length; i++){
4346             if(this.isLocked(i) && !this.isHidden(i)){
4347                 this.totalWidth += this.getColumnWidth(i);
4348             }
4349         }
4350         return totalWidth;
4351     },
4352
4353     getLockedCount : function(){
4354         for(var i = 0, len = this.config.length; i < len; i++){
4355             if(!this.isLocked(i)){
4356                 return i;
4357             }
4358         }
4359     },
4360
4361     /**
4362      * Returns the number of columns.
4363      * @return {Number}
4364      */
4365     getColumnCount : function(visibleOnly){
4366         if(visibleOnly === true){
4367             var c = 0;
4368             for(var i = 0, len = this.config.length; i < len; i++){
4369                 if(!this.isHidden(i)){
4370                     c++;
4371                 }
4372             }
4373             return c;
4374         }
4375         return this.config.length;
4376     },
4377
4378     /**
4379      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
4380      * @param {Function} fn
4381      * @param {Object} scope (optional)
4382      * @return {Array} result
4383      */
4384     getColumnsBy : function(fn, scope){
4385         var r = [];
4386         for(var i = 0, len = this.config.length; i < len; i++){
4387             var c = this.config[i];
4388             if(fn.call(scope||this, c, i) === true){
4389                 r[r.length] = c;
4390             }
4391         }
4392         return r;
4393     },
4394
4395     /**
4396      * Returns true if the specified column is sortable.
4397      * @param {Number} col The column index
4398      * @return {Boolean}
4399      */
4400     isSortable : function(col){
4401         if(typeof this.config[col].sortable == "undefined"){
4402             return this.defaultSortable;
4403         }
4404         return this.config[col].sortable;
4405     },
4406
4407     /**
4408      * Returns the rendering (formatting) function defined for the column.
4409      * @param {Number} col The column index.
4410      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
4411      */
4412     getRenderer : function(col){
4413         if(!this.config[col].renderer){
4414             return Roo.grid.ColumnModel.defaultRenderer;
4415         }
4416         return this.config[col].renderer;
4417     },
4418
4419     /**
4420      * Sets the rendering (formatting) function for a column.
4421      * @param {Number} col The column index
4422      * @param {Function} fn The function to use to process the cell's raw data
4423      * to return HTML markup for the grid view. The render function is called with
4424      * the following parameters:<ul>
4425      * <li>Data value.</li>
4426      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
4427      * <li>css A CSS style string to apply to the table cell.</li>
4428      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
4429      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
4430      * <li>Row index</li>
4431      * <li>Column index</li>
4432      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
4433      */
4434     setRenderer : function(col, fn){
4435         this.config[col].renderer = fn;
4436     },
4437
4438     /**
4439      * Returns the width for the specified column.
4440      * @param {Number} col The column index
4441      * @return {Number}
4442      */
4443     getColumnWidth : function(col){
4444         return this.config[col].width * 1 || this.defaultWidth;
4445     },
4446
4447     /**
4448      * Sets the width for a column.
4449      * @param {Number} col The column index
4450      * @param {Number} width The new width
4451      */
4452     setColumnWidth : function(col, width, suppressEvent){
4453         this.config[col].width = width;
4454         this.totalWidth = null;
4455         if(!suppressEvent){
4456              this.fireEvent("widthchange", this, col, width);
4457         }
4458     },
4459
4460     /**
4461      * Returns the total width of all columns.
4462      * @param {Boolean} includeHidden True to include hidden column widths
4463      * @return {Number}
4464      */
4465     getTotalWidth : function(includeHidden){
4466         if(!this.totalWidth){
4467             this.totalWidth = 0;
4468             for(var i = 0, len = this.config.length; i < len; i++){
4469                 if(includeHidden || !this.isHidden(i)){
4470                     this.totalWidth += this.getColumnWidth(i);
4471                 }
4472             }
4473         }
4474         return this.totalWidth;
4475     },
4476
4477     /**
4478      * Returns the header for the specified column.
4479      * @param {Number} col The column index
4480      * @return {String}
4481      */
4482     getColumnHeader : function(col){
4483         return this.config[col].header;
4484     },
4485
4486     /**
4487      * Sets the header for a column.
4488      * @param {Number} col The column index
4489      * @param {String} header The new header
4490      */
4491     setColumnHeader : function(col, header){
4492         this.config[col].header = header;
4493         this.fireEvent("headerchange", this, col, header);
4494     },
4495
4496     /**
4497      * Returns the tooltip for the specified column.
4498      * @param {Number} col The column index
4499      * @return {String}
4500      */
4501     getColumnTooltip : function(col){
4502             return this.config[col].tooltip;
4503     },
4504     /**
4505      * Sets the tooltip for a column.
4506      * @param {Number} col The column index
4507      * @param {String} tooltip The new tooltip
4508      */
4509     setColumnTooltip : function(col, tooltip){
4510             this.config[col].tooltip = tooltip;
4511     },
4512
4513     /**
4514      * Returns the dataIndex for the specified column.
4515      * @param {Number} col The column index
4516      * @return {Number}
4517      */
4518     getDataIndex : function(col){
4519         return this.config[col].dataIndex;
4520     },
4521
4522     /**
4523      * Sets the dataIndex for a column.
4524      * @param {Number} col The column index
4525      * @param {Number} dataIndex The new dataIndex
4526      */
4527     setDataIndex : function(col, dataIndex){
4528         this.config[col].dataIndex = dataIndex;
4529     },
4530
4531     
4532     
4533     /**
4534      * Returns true if the cell is editable.
4535      * @param {Number} colIndex The column index
4536      * @param {Number} rowIndex The row index
4537      * @return {Boolean}
4538      */
4539     isCellEditable : function(colIndex, rowIndex){
4540         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
4541     },
4542
4543     /**
4544      * Returns the editor defined for the cell/column.
4545      * return false or null to disable editing.
4546      * @param {Number} colIndex The column index
4547      * @param {Number} rowIndex The row index
4548      * @return {Object}
4549      */
4550     getCellEditor : function(colIndex, rowIndex){
4551         return this.config[colIndex].editor;
4552     },
4553
4554     /**
4555      * Sets if a column is editable.
4556      * @param {Number} col The column index
4557      * @param {Boolean} editable True if the column is editable
4558      */
4559     setEditable : function(col, editable){
4560         this.config[col].editable = editable;
4561     },
4562
4563
4564     /**
4565      * Returns true if the column is hidden.
4566      * @param {Number} colIndex The column index
4567      * @return {Boolean}
4568      */
4569     isHidden : function(colIndex){
4570         return this.config[colIndex].hidden;
4571     },
4572
4573
4574     /**
4575      * Returns true if the column width cannot be changed
4576      */
4577     isFixed : function(colIndex){
4578         return this.config[colIndex].fixed;
4579     },
4580
4581     /**
4582      * Returns true if the column can be resized
4583      * @return {Boolean}
4584      */
4585     isResizable : function(colIndex){
4586         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
4587     },
4588     /**
4589      * Sets if a column is hidden.
4590      * @param {Number} colIndex The column index
4591      * @param {Boolean} hidden True if the column is hidden
4592      */
4593     setHidden : function(colIndex, hidden){
4594         this.config[colIndex].hidden = hidden;
4595         this.totalWidth = null;
4596         this.fireEvent("hiddenchange", this, colIndex, hidden);
4597     },
4598
4599     /**
4600      * Sets the editor for a column.
4601      * @param {Number} col The column index
4602      * @param {Object} editor The editor object
4603      */
4604     setEditor : function(col, editor){
4605         this.config[col].editor = editor;
4606     }
4607 });
4608
4609 Roo.grid.ColumnModel.defaultRenderer = function(value){
4610         if(typeof value == "string" && value.length < 1){
4611             return "&#160;";
4612         }
4613         return value;
4614 };
4615
4616 // Alias for backwards compatibility
4617 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
4618 /*
4619  * Based on:
4620  * Ext JS Library 1.1.1
4621  * Copyright(c) 2006-2007, Ext JS, LLC.
4622  *
4623  * Originally Released Under LGPL - original licence link has changed is not relivant.
4624  *
4625  * Fork - LGPL
4626  * <script type="text/javascript">
4627  */
4628  
4629 /**
4630  * @class Roo.LoadMask
4631  * A simple utility class for generically masking elements while loading data.  If the element being masked has
4632  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
4633  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
4634  * element's UpdateManager load indicator and will be destroyed after the initial load.
4635  * @constructor
4636  * Create a new LoadMask
4637  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
4638  * @param {Object} config The config object
4639  */
4640 Roo.LoadMask = function(el, config){
4641     this.el = Roo.get(el);
4642     Roo.apply(this, config);
4643     if(this.store){
4644         this.store.on('beforeload', this.onBeforeLoad, this);
4645         this.store.on('load', this.onLoad, this);
4646         this.store.on('loadexception', this.onLoadException, this);
4647         this.removeMask = false;
4648     }else{
4649         var um = this.el.getUpdateManager();
4650         um.showLoadIndicator = false; // disable the default indicator
4651         um.on('beforeupdate', this.onBeforeLoad, this);
4652         um.on('update', this.onLoad, this);
4653         um.on('failure', this.onLoad, this);
4654         this.removeMask = true;
4655     }
4656 };
4657
4658 Roo.LoadMask.prototype = {
4659     /**
4660      * @cfg {Boolean} removeMask
4661      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
4662      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
4663      */
4664     /**
4665      * @cfg {String} msg
4666      * The text to display in a centered loading message box (defaults to 'Loading...')
4667      */
4668     msg : 'Loading...',
4669     /**
4670      * @cfg {String} msgCls
4671      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
4672      */
4673     msgCls : 'x-mask-loading',
4674
4675     /**
4676      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
4677      * @type Boolean
4678      */
4679     disabled: false,
4680
4681     /**
4682      * Disables the mask to prevent it from being displayed
4683      */
4684     disable : function(){
4685        this.disabled = true;
4686     },
4687
4688     /**
4689      * Enables the mask so that it can be displayed
4690      */
4691     enable : function(){
4692         this.disabled = false;
4693     },
4694     
4695     onLoadException : function()
4696     {
4697         Roo.log(arguments);
4698         
4699         if (typeof(arguments[3]) != 'undefined') {
4700             Roo.MessageBox.alert("Error loading",arguments[3]);
4701         } 
4702         /*
4703         try {
4704             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
4705                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
4706             }   
4707         } catch(e) {
4708             
4709         }
4710         */
4711     
4712         
4713         
4714         this.el.unmask(this.removeMask);
4715     },
4716     // private
4717     onLoad : function()
4718     {
4719         this.el.unmask(this.removeMask);
4720     },
4721
4722     // private
4723     onBeforeLoad : function(){
4724         if(!this.disabled){
4725             this.el.mask(this.msg, this.msgCls);
4726         }
4727     },
4728
4729     // private
4730     destroy : function(){
4731         if(this.store){
4732             this.store.un('beforeload', this.onBeforeLoad, this);
4733             this.store.un('load', this.onLoad, this);
4734             this.store.un('loadexception', this.onLoadException, this);
4735         }else{
4736             var um = this.el.getUpdateManager();
4737             um.un('beforeupdate', this.onBeforeLoad, this);
4738             um.un('update', this.onLoad, this);
4739             um.un('failure', this.onLoad, this);
4740         }
4741     }
4742 };/*
4743  * - LGPL
4744  *
4745  * table
4746  * 
4747  */
4748
4749 /**
4750  * @class Roo.bootstrap.Table
4751  * @extends Roo.bootstrap.Component
4752  * Bootstrap Table class
4753  * @cfg {String} cls table class
4754  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
4755  * @cfg {String} bgcolor Specifies the background color for a table
4756  * @cfg {Number} border Specifies whether the table cells should have borders or not
4757  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
4758  * @cfg {Number} cellspacing Specifies the space between cells
4759  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
4760  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
4761  * @cfg {String} sortable Specifies that the table should be sortable
4762  * @cfg {String} summary Specifies a summary of the content of a table
4763  * @cfg {Number} width Specifies the width of a table
4764  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
4765  * 
4766  * @cfg {boolean} striped Should the rows be alternative striped
4767  * @cfg {boolean} bordered Add borders to the table
4768  * @cfg {boolean} hover Add hover highlighting
4769  * @cfg {boolean} condensed Format condensed
4770  * @cfg {boolean} responsive Format condensed
4771  * @cfg {Boolean} loadMask (true|false) default false
4772  * @cfg {Boolean} tfoot (true|false) generate tfoot, default true
4773  * @cfg {Boolean} thead (true|false) generate thead, default true
4774  * @cfg {Boolean} RowSelection (true|false) default false
4775  * @cfg {Boolean} CellSelection (true|false) default false
4776  *
4777  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
4778  
4779  * 
4780  * @constructor
4781  * Create a new Table
4782  * @param {Object} config The config object
4783  */
4784
4785 Roo.bootstrap.Table = function(config){
4786     Roo.bootstrap.Table.superclass.constructor.call(this, config);
4787     
4788     if (this.sm) {
4789         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
4790         this.sm = this.selModel;
4791         this.sm.xmodule = this.xmodule || false;
4792     }
4793     if (this.cm && typeof(this.cm.config) == 'undefined') {
4794         this.colModel = new Roo.grid.ColumnModel(this.cm);
4795         this.cm = this.colModel;
4796         this.cm.xmodule = this.xmodule || false;
4797     }
4798     if (this.store) {
4799         this.store= Roo.factory(this.store, Roo.data);
4800         this.ds = this.store;
4801         this.ds.xmodule = this.xmodule || false;
4802          
4803     }
4804     if (this.footer && this.store) {
4805         this.footer.dataSource = this.ds;
4806         this.footer = Roo.factory(this.footer);
4807     }
4808     
4809     /** @private */
4810     this.addEvents({
4811         /**
4812          * @event cellclick
4813          * Fires when a cell is clicked
4814          * @param {Roo.bootstrap.Table} this
4815          * @param {Roo.Element} el
4816          * @param {Number} rowIndex
4817          * @param {Number} columnIndex
4818          * @param {Roo.EventObject} e
4819          */
4820         "cellclick" : true,
4821         /**
4822          * @event celldblclick
4823          * Fires when a cell is double clicked
4824          * @param {Roo.bootstrap.Table} this
4825          * @param {Roo.Element} el
4826          * @param {Number} rowIndex
4827          * @param {Number} columnIndex
4828          * @param {Roo.EventObject} e
4829          */
4830         "celldblclick" : true,
4831         /**
4832          * @event rowclick
4833          * Fires when a row is clicked
4834          * @param {Roo.bootstrap.Table} this
4835          * @param {Roo.Element} el
4836          * @param {Number} rowIndex
4837          * @param {Roo.EventObject} e
4838          */
4839         "rowclick" : true,
4840         /**
4841          * @event rowdblclick
4842          * Fires when a row is double clicked
4843          * @param {Roo.bootstrap.Table} this
4844          * @param {Roo.Element} el
4845          * @param {Number} rowIndex
4846          * @param {Roo.EventObject} e
4847          */
4848         "rowdblclick" : true,
4849         /**
4850          * @event mouseover
4851          * Fires when a mouseover occur
4852          * @param {Roo.bootstrap.Table} this
4853          * @param {Roo.Element} el
4854          * @param {Number} rowIndex
4855          * @param {Number} columnIndex
4856          * @param {Roo.EventObject} e
4857          */
4858         "mouseover" : true,
4859         /**
4860          * @event mouseout
4861          * Fires when a mouseout occur
4862          * @param {Roo.bootstrap.Table} this
4863          * @param {Roo.Element} el
4864          * @param {Number} rowIndex
4865          * @param {Number} columnIndex
4866          * @param {Roo.EventObject} e
4867          */
4868         "mouseout" : true,
4869         /**
4870          * @event rowclass
4871          * Fires when a row is rendered, so you can change add a style to it.
4872          * @param {Roo.bootstrap.Table} this
4873          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
4874          */
4875         'rowclass' : true
4876         
4877     });
4878 };
4879
4880 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
4881     
4882     cls: false,
4883     align: false,
4884     bgcolor: false,
4885     border: false,
4886     cellpadding: false,
4887     cellspacing: false,
4888     frame: false,
4889     rules: false,
4890     sortable: false,
4891     summary: false,
4892     width: false,
4893     striped : false,
4894     bordered: false,
4895     hover:  false,
4896     condensed : false,
4897     responsive : false,
4898     sm : false,
4899     cm : false,
4900     store : false,
4901     loadMask : false,
4902     tfoot : true,
4903     thead : true,
4904     RowSelection : false,
4905     CellSelection : false,
4906     layout : false,
4907     
4908     // Roo.Element - the tbody
4909     mainBody: false, 
4910     
4911     getAutoCreate : function(){
4912         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
4913         
4914         cfg = {
4915             tag: 'table',
4916             cls : 'table',
4917             cn : []
4918         }
4919             
4920         if (this.striped) {
4921             cfg.cls += ' table-striped';
4922         }
4923         
4924         if (this.hover) {
4925             cfg.cls += ' table-hover';
4926         }
4927         if (this.bordered) {
4928             cfg.cls += ' table-bordered';
4929         }
4930         if (this.condensed) {
4931             cfg.cls += ' table-condensed';
4932         }
4933         if (this.responsive) {
4934             cfg.cls += ' table-responsive';
4935         }
4936         
4937         if (this.cls) {
4938             cfg.cls+=  ' ' +this.cls;
4939         }
4940         
4941         // this lot should be simplifed...
4942         
4943         if (this.align) {
4944             cfg.align=this.align;
4945         }
4946         if (this.bgcolor) {
4947             cfg.bgcolor=this.bgcolor;
4948         }
4949         if (this.border) {
4950             cfg.border=this.border;
4951         }
4952         if (this.cellpadding) {
4953             cfg.cellpadding=this.cellpadding;
4954         }
4955         if (this.cellspacing) {
4956             cfg.cellspacing=this.cellspacing;
4957         }
4958         if (this.frame) {
4959             cfg.frame=this.frame;
4960         }
4961         if (this.rules) {
4962             cfg.rules=this.rules;
4963         }
4964         if (this.sortable) {
4965             cfg.sortable=this.sortable;
4966         }
4967         if (this.summary) {
4968             cfg.summary=this.summary;
4969         }
4970         if (this.width) {
4971             cfg.width=this.width;
4972         }
4973         if (this.layout) {
4974             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
4975         }
4976         
4977         if(this.store || this.cm){
4978             if(this.thead){
4979                 cfg.cn.push(this.renderHeader());
4980             }
4981             
4982             cfg.cn.push(this.renderBody());
4983             
4984             if(this.tfoot){
4985                 cfg.cn.push(this.renderFooter());
4986             }
4987             
4988             cfg.cls+=  ' TableGrid';
4989         }
4990         
4991         return { cn : [ cfg ] };
4992     },
4993     
4994     initEvents : function()
4995     {   
4996         if(!this.store || !this.cm){
4997             return;
4998         }
4999         
5000         //Roo.log('initEvents with ds!!!!');
5001         
5002         this.mainBody = this.el.select('tbody', true).first();
5003         
5004         
5005         var _this = this;
5006         
5007         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5008             e.on('click', _this.sort, _this);
5009         });
5010         
5011         this.el.on("click", this.onClick, this);
5012         this.el.on("dblclick", this.onDblClick, this);
5013         
5014         this.parent().el.setStyle('position', 'relative');
5015         if (this.footer) {
5016             this.footer.parentId = this.id;
5017             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
5018         }
5019         
5020         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5021         
5022         this.store.on('load', this.onLoad, this);
5023         this.store.on('beforeload', this.onBeforeLoad, this);
5024         this.store.on('update', this.onUpdate, this);
5025         
5026     },
5027     
5028     onMouseover : function(e, el)
5029     {
5030         var cell = Roo.get(el);
5031         
5032         if(!cell){
5033             return;
5034         }
5035         
5036         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5037             cell = cell.findParent('td', false, true);
5038         }
5039         
5040         var row = cell.findParent('tr', false, true);
5041         var cellIndex = cell.dom.cellIndex;
5042         var rowIndex = row.dom.rowIndex - 1; // start from 0
5043         
5044         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5045         
5046     },
5047     
5048     onMouseout : function(e, el)
5049     {
5050         var cell = Roo.get(el);
5051         
5052         if(!cell){
5053             return;
5054         }
5055         
5056         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5057             cell = cell.findParent('td', false, true);
5058         }
5059         
5060         var row = cell.findParent('tr', false, true);
5061         var cellIndex = cell.dom.cellIndex;
5062         var rowIndex = row.dom.rowIndex - 1; // start from 0
5063         
5064         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5065         
5066     },
5067     
5068     onClick : function(e, el)
5069     {
5070         var cell = Roo.get(el);
5071         
5072         if(!cell || (!this.CellSelection && !this.RowSelection)){
5073             return;
5074         }
5075         
5076         
5077         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5078             cell = cell.findParent('td', false, true);
5079         }
5080         
5081         var row = cell.findParent('tr', false, true);
5082         var cellIndex = cell.dom.cellIndex;
5083         var rowIndex = row.dom.rowIndex - 1;
5084         
5085         if(this.CellSelection){
5086             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5087         }
5088         
5089         if(this.RowSelection){
5090             this.fireEvent('rowclick', this, row, rowIndex, e);
5091         }
5092         
5093         
5094     },
5095     
5096     onDblClick : function(e,el)
5097     {
5098         var cell = Roo.get(el);
5099         
5100         if(!cell || (!this.CellSelection && !this.RowSelection)){
5101             return;
5102         }
5103         
5104         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5105             cell = cell.findParent('td', false, true);
5106         }
5107         
5108         var row = cell.findParent('tr', false, true);
5109         var cellIndex = cell.dom.cellIndex;
5110         var rowIndex = row.dom.rowIndex - 1;
5111         
5112         if(this.CellSelection){
5113             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5114         }
5115         
5116         if(this.RowSelection){
5117             this.fireEvent('rowdblclick', this, row, rowIndex, e);
5118         }
5119     },
5120     
5121     sort : function(e,el)
5122     {
5123         var col = Roo.get(el)
5124         
5125         if(!col.hasClass('sortable')){
5126             return;
5127         }
5128         
5129         var sort = col.attr('sort');
5130         var dir = 'ASC';
5131         
5132         if(col.hasClass('glyphicon-arrow-up')){
5133             dir = 'DESC';
5134         }
5135         
5136         this.store.sortInfo = {field : sort, direction : dir};
5137         
5138         if (this.footer) {
5139             Roo.log("calling footer first");
5140             this.footer.onClick('first');
5141         } else {
5142         
5143             this.store.load({ params : { start : 0 } });
5144         }
5145     },
5146     
5147     renderHeader : function()
5148     {
5149         var header = {
5150             tag: 'thead',
5151             cn : []
5152         };
5153         
5154         var cm = this.cm;
5155         
5156         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5157             
5158             var config = cm.config[i];
5159                     
5160             var c = {
5161                 tag: 'th',
5162                 style : '',
5163                 html: cm.getColumnHeader(i)
5164             };
5165             
5166             if(typeof(config.hidden) != 'undefined' && config.hidden){
5167                 c.style += ' display:none;';
5168             }
5169             
5170             if(typeof(config.dataIndex) != 'undefined'){
5171                 c.sort = config.dataIndex;
5172             }
5173             
5174             if(typeof(config.sortable) != 'undefined' && config.sortable){
5175                 c.cls = 'sortable';
5176             }
5177             
5178             if(typeof(config.align) != 'undefined' && config.align.length){
5179                 c.style += ' text-align:' + config.align + ';';
5180             }
5181             
5182             if(typeof(config.width) != 'undefined'){
5183                 c.style += ' width:' + config.width + 'px;';
5184             }
5185             
5186             header.cn.push(c)
5187         }
5188         
5189         return header;
5190     },
5191     
5192     renderBody : function()
5193     {
5194         var body = {
5195             tag: 'tbody',
5196             cn : [
5197                 {
5198                     tag: 'tr',
5199                     cn : [
5200                         {
5201                             tag : 'td',
5202                             colspan :  this.cm.getColumnCount()
5203                         }
5204                     ]
5205                 }
5206             ]
5207         };
5208         
5209         return body;
5210     },
5211     
5212     renderFooter : function()
5213     {
5214         var footer = {
5215             tag: 'tfoot',
5216             cn : [
5217                 {
5218                     tag: 'tr',
5219                     cn : [
5220                         {
5221                             tag : 'td',
5222                             colspan :  this.cm.getColumnCount()
5223                         }
5224                     ]
5225                 }
5226             ]
5227         };
5228         
5229         return footer;
5230     },
5231     
5232     
5233     
5234     onLoad : function()
5235     {
5236         Roo.log('ds onload');
5237         this.clear();
5238         
5239         var _this = this;
5240         var cm = this.cm;
5241         var ds = this.store;
5242         
5243         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5244             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
5245             
5246             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
5247                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
5248             }
5249             
5250             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
5251                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
5252             }
5253         });
5254         
5255         var tbody =  this.mainBody;
5256               
5257         if(ds.getCount() > 0){
5258             ds.data.each(function(d,rowIndex){
5259                 var row =  this.renderRow(cm, ds, rowIndex);
5260                 
5261                 tbody.createChild(row);
5262                 
5263                 var _this = this;
5264                 
5265                 if(row.cellObjects.length){
5266                     Roo.each(row.cellObjects, function(r){
5267                         _this.renderCellObject(r);
5268                     })
5269                 }
5270                 
5271             }, this);
5272         }
5273         
5274         Roo.each(this.el.select('tbody td', true).elements, function(e){
5275             e.on('mouseover', _this.onMouseover, _this);
5276         });
5277         
5278         Roo.each(this.el.select('tbody td', true).elements, function(e){
5279             e.on('mouseout', _this.onMouseout, _this);
5280         });
5281
5282         //if(this.loadMask){
5283         //    this.maskEl.hide();
5284         //}
5285     },
5286     
5287     
5288     onUpdate : function(ds,record)
5289     {
5290         this.refreshRow(record);
5291     },
5292     onRemove : function(ds, record, index, isUpdate){
5293         if(isUpdate !== true){
5294             this.fireEvent("beforerowremoved", this, index, record);
5295         }
5296         var bt = this.mainBody.dom;
5297         if(bt.rows[index]){
5298             bt.removeChild(bt.rows[index]);
5299         }
5300         
5301         if(isUpdate !== true){
5302             //this.stripeRows(index);
5303             //this.syncRowHeights(index, index);
5304             //this.layout();
5305             this.fireEvent("rowremoved", this, index, record);
5306         }
5307     },
5308     
5309     
5310     refreshRow : function(record){
5311         var ds = this.store, index;
5312         if(typeof record == 'number'){
5313             index = record;
5314             record = ds.getAt(index);
5315         }else{
5316             index = ds.indexOf(record);
5317         }
5318         this.insertRow(ds, index, true);
5319         this.onRemove(ds, record, index+1, true);
5320         //this.syncRowHeights(index, index);
5321         //this.layout();
5322         this.fireEvent("rowupdated", this, index, record);
5323     },
5324     
5325     insertRow : function(dm, rowIndex, isUpdate){
5326         
5327         if(!isUpdate){
5328             this.fireEvent("beforerowsinserted", this, rowIndex);
5329         }
5330             //var s = this.getScrollState();
5331         var row = this.renderRow(this.cm, this.store, rowIndex);
5332         // insert before rowIndex..
5333         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
5334         
5335         var _this = this;
5336                 
5337         if(row.cellObjects.length){
5338             Roo.each(row.cellObjects, function(r){
5339                 _this.renderCellObject(r);
5340             })
5341         }
5342             
5343         if(!isUpdate){
5344             this.fireEvent("rowsinserted", this, rowIndex);
5345             //this.syncRowHeights(firstRow, lastRow);
5346             //this.stripeRows(firstRow);
5347             //this.layout();
5348         }
5349         
5350     },
5351     
5352     
5353     getRowDom : function(rowIndex)
5354     {
5355         // not sure if I need to check this.. but let's do it anyway..
5356         return (this.mainBody.dom.rows && (rowIndex-1) < this.mainBody.dom.rows.length ) ?
5357                 this.mainBody.dom.rows[rowIndex] : false
5358     },
5359     // returns the object tree for a tr..
5360   
5361     
5362     renderRow : function(cm, ds, rowIndex) {
5363         
5364         var d = ds.getAt(rowIndex);
5365         
5366         var row = {
5367             tag : 'tr',
5368             cn : []
5369         };
5370             
5371         var cellObjects = [];
5372         
5373         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5374             var config = cm.config[i];
5375             
5376             var renderer = cm.getRenderer(i);
5377             var value = '';
5378             var id = false;
5379             
5380             if(typeof(renderer) !== 'undefined'){
5381                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
5382             }
5383             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
5384             // and are rendered into the cells after the row is rendered - using the id for the element.
5385             
5386             if(typeof(value) === 'object'){
5387                 id = Roo.id();
5388                 cellObjects.push({
5389                     container : id,
5390                     cfg : value 
5391                 })
5392             }
5393             
5394             var rowcfg = {
5395                 record: d,
5396                 rowIndex : rowIndex,
5397                 colIndex : i,
5398                 rowClass : ''
5399             }
5400
5401             this.fireEvent('rowclass', this, rowcfg);
5402             
5403             var td = {
5404                 tag: 'td',
5405                 cls : rowcfg.rowClass,
5406                 style: '',
5407                 html: (typeof(value) === 'object') ? '' : value
5408             };
5409             
5410             if (id) {
5411                 td.id = id;
5412             }
5413             
5414             if(typeof(config.hidden) != 'undefined' && config.hidden){
5415                 td.style += ' display:none;';
5416             }
5417             
5418             if(typeof(config.align) != 'undefined' && config.align.length){
5419                 td.style += ' text-align:' + config.align + ';';
5420             }
5421             
5422             if(typeof(config.width) != 'undefined'){
5423                 td.style += ' width:' +  config.width + 'px;';
5424             }
5425              
5426             row.cn.push(td);
5427            
5428         }
5429         
5430         row.cellObjects = cellObjects;
5431         
5432         return row;
5433           
5434     },
5435     
5436     
5437     
5438     onBeforeLoad : function()
5439     {
5440         //Roo.log('ds onBeforeLoad');
5441         
5442         //this.clear();
5443         
5444         //if(this.loadMask){
5445         //    this.maskEl.show();
5446         //}
5447     },
5448     
5449     clear : function()
5450     {
5451         this.el.select('tbody', true).first().dom.innerHTML = '';
5452     },
5453     
5454     getSelectionModel : function(){
5455         if(!this.selModel){
5456             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
5457         }
5458         return this.selModel;
5459     },
5460     /*
5461      * Render the Roo.bootstrap object from renderder
5462      */
5463     renderCellObject : function(r)
5464     {
5465         var _this = this;
5466         
5467         var t = r.cfg.render(r.container);
5468         
5469         if(r.cfg.cn){
5470             Roo.each(r.cfg.cn, function(c){
5471                 var child = {
5472                     container: t.getChildContainer(),
5473                     cfg: c
5474                 }
5475                 _this.renderCellObject(child);
5476             })
5477         }
5478     }
5479    
5480 });
5481
5482  
5483
5484  /*
5485  * - LGPL
5486  *
5487  * table cell
5488  * 
5489  */
5490
5491 /**
5492  * @class Roo.bootstrap.TableCell
5493  * @extends Roo.bootstrap.Component
5494  * Bootstrap TableCell class
5495  * @cfg {String} html cell contain text
5496  * @cfg {String} cls cell class
5497  * @cfg {String} tag cell tag (td|th) default td
5498  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
5499  * @cfg {String} align Aligns the content in a cell
5500  * @cfg {String} axis Categorizes cells
5501  * @cfg {String} bgcolor Specifies the background color of a cell
5502  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5503  * @cfg {Number} colspan Specifies the number of columns a cell should span
5504  * @cfg {String} headers Specifies one or more header cells a cell is related to
5505  * @cfg {Number} height Sets the height of a cell
5506  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
5507  * @cfg {Number} rowspan Sets the number of rows a cell should span
5508  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
5509  * @cfg {String} valign Vertical aligns the content in a cell
5510  * @cfg {Number} width Specifies the width of a cell
5511  * 
5512  * @constructor
5513  * Create a new TableCell
5514  * @param {Object} config The config object
5515  */
5516
5517 Roo.bootstrap.TableCell = function(config){
5518     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
5519 };
5520
5521 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
5522     
5523     html: false,
5524     cls: false,
5525     tag: false,
5526     abbr: false,
5527     align: false,
5528     axis: false,
5529     bgcolor: false,
5530     charoff: false,
5531     colspan: false,
5532     headers: false,
5533     height: false,
5534     nowrap: false,
5535     rowspan: false,
5536     scope: false,
5537     valign: false,
5538     width: false,
5539     
5540     
5541     getAutoCreate : function(){
5542         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
5543         
5544         cfg = {
5545             tag: 'td'
5546         }
5547         
5548         if(this.tag){
5549             cfg.tag = this.tag;
5550         }
5551         
5552         if (this.html) {
5553             cfg.html=this.html
5554         }
5555         if (this.cls) {
5556             cfg.cls=this.cls
5557         }
5558         if (this.abbr) {
5559             cfg.abbr=this.abbr
5560         }
5561         if (this.align) {
5562             cfg.align=this.align
5563         }
5564         if (this.axis) {
5565             cfg.axis=this.axis
5566         }
5567         if (this.bgcolor) {
5568             cfg.bgcolor=this.bgcolor
5569         }
5570         if (this.charoff) {
5571             cfg.charoff=this.charoff
5572         }
5573         if (this.colspan) {
5574             cfg.colspan=this.colspan
5575         }
5576         if (this.headers) {
5577             cfg.headers=this.headers
5578         }
5579         if (this.height) {
5580             cfg.height=this.height
5581         }
5582         if (this.nowrap) {
5583             cfg.nowrap=this.nowrap
5584         }
5585         if (this.rowspan) {
5586             cfg.rowspan=this.rowspan
5587         }
5588         if (this.scope) {
5589             cfg.scope=this.scope
5590         }
5591         if (this.valign) {
5592             cfg.valign=this.valign
5593         }
5594         if (this.width) {
5595             cfg.width=this.width
5596         }
5597         
5598         
5599         return cfg;
5600     }
5601    
5602 });
5603
5604  
5605
5606  /*
5607  * - LGPL
5608  *
5609  * table row
5610  * 
5611  */
5612
5613 /**
5614  * @class Roo.bootstrap.TableRow
5615  * @extends Roo.bootstrap.Component
5616  * Bootstrap TableRow class
5617  * @cfg {String} cls row class
5618  * @cfg {String} align Aligns the content in a table row
5619  * @cfg {String} bgcolor Specifies a background color for a table row
5620  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5621  * @cfg {String} valign Vertical aligns the content in a table row
5622  * 
5623  * @constructor
5624  * Create a new TableRow
5625  * @param {Object} config The config object
5626  */
5627
5628 Roo.bootstrap.TableRow = function(config){
5629     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
5630 };
5631
5632 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
5633     
5634     cls: false,
5635     align: false,
5636     bgcolor: false,
5637     charoff: false,
5638     valign: false,
5639     
5640     getAutoCreate : function(){
5641         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
5642         
5643         cfg = {
5644             tag: 'tr'
5645         }
5646             
5647         if(this.cls){
5648             cfg.cls = this.cls;
5649         }
5650         if(this.align){
5651             cfg.align = this.align;
5652         }
5653         if(this.bgcolor){
5654             cfg.bgcolor = this.bgcolor;
5655         }
5656         if(this.charoff){
5657             cfg.charoff = this.charoff;
5658         }
5659         if(this.valign){
5660             cfg.valign = this.valign;
5661         }
5662         
5663         return cfg;
5664     }
5665    
5666 });
5667
5668  
5669
5670  /*
5671  * - LGPL
5672  *
5673  * table body
5674  * 
5675  */
5676
5677 /**
5678  * @class Roo.bootstrap.TableBody
5679  * @extends Roo.bootstrap.Component
5680  * Bootstrap TableBody class
5681  * @cfg {String} cls element class
5682  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
5683  * @cfg {String} align Aligns the content inside the element
5684  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
5685  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
5686  * 
5687  * @constructor
5688  * Create a new TableBody
5689  * @param {Object} config The config object
5690  */
5691
5692 Roo.bootstrap.TableBody = function(config){
5693     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
5694 };
5695
5696 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
5697     
5698     cls: false,
5699     tag: false,
5700     align: false,
5701     charoff: false,
5702     valign: false,
5703     
5704     getAutoCreate : function(){
5705         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
5706         
5707         cfg = {
5708             tag: 'tbody'
5709         }
5710             
5711         if (this.cls) {
5712             cfg.cls=this.cls
5713         }
5714         if(this.tag){
5715             cfg.tag = this.tag;
5716         }
5717         
5718         if(this.align){
5719             cfg.align = this.align;
5720         }
5721         if(this.charoff){
5722             cfg.charoff = this.charoff;
5723         }
5724         if(this.valign){
5725             cfg.valign = this.valign;
5726         }
5727         
5728         return cfg;
5729     }
5730     
5731     
5732 //    initEvents : function()
5733 //    {
5734 //        
5735 //        if(!this.store){
5736 //            return;
5737 //        }
5738 //        
5739 //        this.store = Roo.factory(this.store, Roo.data);
5740 //        this.store.on('load', this.onLoad, this);
5741 //        
5742 //        this.store.load();
5743 //        
5744 //    },
5745 //    
5746 //    onLoad: function () 
5747 //    {   
5748 //        this.fireEvent('load', this);
5749 //    }
5750 //    
5751 //   
5752 });
5753
5754  
5755
5756  /*
5757  * Based on:
5758  * Ext JS Library 1.1.1
5759  * Copyright(c) 2006-2007, Ext JS, LLC.
5760  *
5761  * Originally Released Under LGPL - original licence link has changed is not relivant.
5762  *
5763  * Fork - LGPL
5764  * <script type="text/javascript">
5765  */
5766
5767 // as we use this in bootstrap.
5768 Roo.namespace('Roo.form');
5769  /**
5770  * @class Roo.form.Action
5771  * Internal Class used to handle form actions
5772  * @constructor
5773  * @param {Roo.form.BasicForm} el The form element or its id
5774  * @param {Object} config Configuration options
5775  */
5776
5777  
5778  
5779 // define the action interface
5780 Roo.form.Action = function(form, options){
5781     this.form = form;
5782     this.options = options || {};
5783 };
5784 /**
5785  * Client Validation Failed
5786  * @const 
5787  */
5788 Roo.form.Action.CLIENT_INVALID = 'client';
5789 /**
5790  * Server Validation Failed
5791  * @const 
5792  */
5793 Roo.form.Action.SERVER_INVALID = 'server';
5794  /**
5795  * Connect to Server Failed
5796  * @const 
5797  */
5798 Roo.form.Action.CONNECT_FAILURE = 'connect';
5799 /**
5800  * Reading Data from Server Failed
5801  * @const 
5802  */
5803 Roo.form.Action.LOAD_FAILURE = 'load';
5804
5805 Roo.form.Action.prototype = {
5806     type : 'default',
5807     failureType : undefined,
5808     response : undefined,
5809     result : undefined,
5810
5811     // interface method
5812     run : function(options){
5813
5814     },
5815
5816     // interface method
5817     success : function(response){
5818
5819     },
5820
5821     // interface method
5822     handleResponse : function(response){
5823
5824     },
5825
5826     // default connection failure
5827     failure : function(response){
5828         
5829         this.response = response;
5830         this.failureType = Roo.form.Action.CONNECT_FAILURE;
5831         this.form.afterAction(this, false);
5832     },
5833
5834     processResponse : function(response){
5835         this.response = response;
5836         if(!response.responseText){
5837             return true;
5838         }
5839         this.result = this.handleResponse(response);
5840         return this.result;
5841     },
5842
5843     // utility functions used internally
5844     getUrl : function(appendParams){
5845         var url = this.options.url || this.form.url || this.form.el.dom.action;
5846         if(appendParams){
5847             var p = this.getParams();
5848             if(p){
5849                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
5850             }
5851         }
5852         return url;
5853     },
5854
5855     getMethod : function(){
5856         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
5857     },
5858
5859     getParams : function(){
5860         var bp = this.form.baseParams;
5861         var p = this.options.params;
5862         if(p){
5863             if(typeof p == "object"){
5864                 p = Roo.urlEncode(Roo.applyIf(p, bp));
5865             }else if(typeof p == 'string' && bp){
5866                 p += '&' + Roo.urlEncode(bp);
5867             }
5868         }else if(bp){
5869             p = Roo.urlEncode(bp);
5870         }
5871         return p;
5872     },
5873
5874     createCallback : function(){
5875         return {
5876             success: this.success,
5877             failure: this.failure,
5878             scope: this,
5879             timeout: (this.form.timeout*1000),
5880             upload: this.form.fileUpload ? this.success : undefined
5881         };
5882     }
5883 };
5884
5885 Roo.form.Action.Submit = function(form, options){
5886     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
5887 };
5888
5889 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
5890     type : 'submit',
5891
5892     haveProgress : false,
5893     uploadComplete : false,
5894     
5895     // uploadProgress indicator.
5896     uploadProgress : function()
5897     {
5898         if (!this.form.progressUrl) {
5899             return;
5900         }
5901         
5902         if (!this.haveProgress) {
5903             Roo.MessageBox.progress("Uploading", "Uploading");
5904         }
5905         if (this.uploadComplete) {
5906            Roo.MessageBox.hide();
5907            return;
5908         }
5909         
5910         this.haveProgress = true;
5911    
5912         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
5913         
5914         var c = new Roo.data.Connection();
5915         c.request({
5916             url : this.form.progressUrl,
5917             params: {
5918                 id : uid
5919             },
5920             method: 'GET',
5921             success : function(req){
5922                //console.log(data);
5923                 var rdata = false;
5924                 var edata;
5925                 try  {
5926                    rdata = Roo.decode(req.responseText)
5927                 } catch (e) {
5928                     Roo.log("Invalid data from server..");
5929                     Roo.log(edata);
5930                     return;
5931                 }
5932                 if (!rdata || !rdata.success) {
5933                     Roo.log(rdata);
5934                     Roo.MessageBox.alert(Roo.encode(rdata));
5935                     return;
5936                 }
5937                 var data = rdata.data;
5938                 
5939                 if (this.uploadComplete) {
5940                    Roo.MessageBox.hide();
5941                    return;
5942                 }
5943                    
5944                 if (data){
5945                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
5946                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
5947                     );
5948                 }
5949                 this.uploadProgress.defer(2000,this);
5950             },
5951        
5952             failure: function(data) {
5953                 Roo.log('progress url failed ');
5954                 Roo.log(data);
5955             },
5956             scope : this
5957         });
5958            
5959     },
5960     
5961     
5962     run : function()
5963     {
5964         // run get Values on the form, so it syncs any secondary forms.
5965         this.form.getValues();
5966         
5967         var o = this.options;
5968         var method = this.getMethod();
5969         var isPost = method == 'POST';
5970         if(o.clientValidation === false || this.form.isValid()){
5971             
5972             if (this.form.progressUrl) {
5973                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
5974                     (new Date() * 1) + '' + Math.random());
5975                     
5976             } 
5977             
5978             
5979             Roo.Ajax.request(Roo.apply(this.createCallback(), {
5980                 form:this.form.el.dom,
5981                 url:this.getUrl(!isPost),
5982                 method: method,
5983                 params:isPost ? this.getParams() : null,
5984                 isUpload: this.form.fileUpload
5985             }));
5986             
5987             this.uploadProgress();
5988
5989         }else if (o.clientValidation !== false){ // client validation failed
5990             this.failureType = Roo.form.Action.CLIENT_INVALID;
5991             this.form.afterAction(this, false);
5992         }
5993     },
5994
5995     success : function(response)
5996     {
5997         this.uploadComplete= true;
5998         if (this.haveProgress) {
5999             Roo.MessageBox.hide();
6000         }
6001         
6002         
6003         var result = this.processResponse(response);
6004         if(result === true || result.success){
6005             this.form.afterAction(this, true);
6006             return;
6007         }
6008         if(result.errors){
6009             this.form.markInvalid(result.errors);
6010             this.failureType = Roo.form.Action.SERVER_INVALID;
6011         }
6012         this.form.afterAction(this, false);
6013     },
6014     failure : function(response)
6015     {
6016         this.uploadComplete= true;
6017         if (this.haveProgress) {
6018             Roo.MessageBox.hide();
6019         }
6020         
6021         this.response = response;
6022         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6023         this.form.afterAction(this, false);
6024     },
6025     
6026     handleResponse : function(response){
6027         if(this.form.errorReader){
6028             var rs = this.form.errorReader.read(response);
6029             var errors = [];
6030             if(rs.records){
6031                 for(var i = 0, len = rs.records.length; i < len; i++) {
6032                     var r = rs.records[i];
6033                     errors[i] = r.data;
6034                 }
6035             }
6036             if(errors.length < 1){
6037                 errors = null;
6038             }
6039             return {
6040                 success : rs.success,
6041                 errors : errors
6042             };
6043         }
6044         var ret = false;
6045         try {
6046             ret = Roo.decode(response.responseText);
6047         } catch (e) {
6048             ret = {
6049                 success: false,
6050                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6051                 errors : []
6052             };
6053         }
6054         return ret;
6055         
6056     }
6057 });
6058
6059
6060 Roo.form.Action.Load = function(form, options){
6061     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6062     this.reader = this.form.reader;
6063 };
6064
6065 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6066     type : 'load',
6067
6068     run : function(){
6069         
6070         Roo.Ajax.request(Roo.apply(
6071                 this.createCallback(), {
6072                     method:this.getMethod(),
6073                     url:this.getUrl(false),
6074                     params:this.getParams()
6075         }));
6076     },
6077
6078     success : function(response){
6079         
6080         var result = this.processResponse(response);
6081         if(result === true || !result.success || !result.data){
6082             this.failureType = Roo.form.Action.LOAD_FAILURE;
6083             this.form.afterAction(this, false);
6084             return;
6085         }
6086         this.form.clearInvalid();
6087         this.form.setValues(result.data);
6088         this.form.afterAction(this, true);
6089     },
6090
6091     handleResponse : function(response){
6092         if(this.form.reader){
6093             var rs = this.form.reader.read(response);
6094             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
6095             return {
6096                 success : rs.success,
6097                 data : data
6098             };
6099         }
6100         return Roo.decode(response.responseText);
6101     }
6102 });
6103
6104 Roo.form.Action.ACTION_TYPES = {
6105     'load' : Roo.form.Action.Load,
6106     'submit' : Roo.form.Action.Submit
6107 };/*
6108  * - LGPL
6109  *
6110  * form
6111  * 
6112  */
6113
6114 /**
6115  * @class Roo.bootstrap.Form
6116  * @extends Roo.bootstrap.Component
6117  * Bootstrap Form class
6118  * @cfg {String} method  GET | POST (default POST)
6119  * @cfg {String} labelAlign top | left (default top)
6120   * @cfg {String} align left  | right - for navbars
6121
6122  * 
6123  * @constructor
6124  * Create a new Form
6125  * @param {Object} config The config object
6126  */
6127
6128
6129 Roo.bootstrap.Form = function(config){
6130     Roo.bootstrap.Form.superclass.constructor.call(this, config);
6131     this.addEvents({
6132         /**
6133          * @event clientvalidation
6134          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
6135          * @param {Form} this
6136          * @param {Boolean} valid true if the form has passed client-side validation
6137          */
6138         clientvalidation: true,
6139         /**
6140          * @event beforeaction
6141          * Fires before any action is performed. Return false to cancel the action.
6142          * @param {Form} this
6143          * @param {Action} action The action to be performed
6144          */
6145         beforeaction: true,
6146         /**
6147          * @event actionfailed
6148          * Fires when an action fails.
6149          * @param {Form} this
6150          * @param {Action} action The action that failed
6151          */
6152         actionfailed : true,
6153         /**
6154          * @event actioncomplete
6155          * Fires when an action is completed.
6156          * @param {Form} this
6157          * @param {Action} action The action that completed
6158          */
6159         actioncomplete : true
6160     });
6161     
6162 };
6163
6164 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
6165       
6166      /**
6167      * @cfg {String} method
6168      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
6169      */
6170     method : 'POST',
6171     /**
6172      * @cfg {String} url
6173      * The URL to use for form actions if one isn't supplied in the action options.
6174      */
6175     /**
6176      * @cfg {Boolean} fileUpload
6177      * Set to true if this form is a file upload.
6178      */
6179      
6180     /**
6181      * @cfg {Object} baseParams
6182      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
6183      */
6184       
6185     /**
6186      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
6187      */
6188     timeout: 30,
6189     /**
6190      * @cfg {Sting} align (left|right) for navbar forms
6191      */
6192     align : 'left',
6193
6194     // private
6195     activeAction : null,
6196  
6197     /**
6198      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
6199      * element by passing it or its id or mask the form itself by passing in true.
6200      * @type Mixed
6201      */
6202     waitMsgTarget : false,
6203     
6204      
6205     
6206     /**
6207      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
6208      * element by passing it or its id or mask the form itself by passing in true.
6209      * @type Mixed
6210      */
6211     
6212     getAutoCreate : function(){
6213         
6214         var cfg = {
6215             tag: 'form',
6216             method : this.method || 'POST',
6217             id : this.id || Roo.id(),
6218             cls : ''
6219         }
6220         if (this.parent().xtype.match(/^Nav/)) {
6221             cfg.cls = 'navbar-form navbar-' + this.align;
6222             
6223         }
6224         
6225         if (this.labelAlign == 'left' ) {
6226             cfg.cls += ' form-horizontal';
6227         }
6228         
6229         
6230         return cfg;
6231     },
6232     initEvents : function()
6233     {
6234         this.el.on('submit', this.onSubmit, this);
6235         // this was added as random key presses on the form where triggering form submit.
6236         this.el.on('keypress', function(e) {
6237             if (e.getCharCode() != 13) {
6238                 return true;
6239             }
6240             // we might need to allow it for textareas.. and some other items.
6241             // check e.getTarget().
6242             
6243             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
6244                 return true;
6245             }
6246         
6247             Roo.log("keypress blocked");
6248             
6249             e.preventDefault();
6250             return false;
6251         });
6252         
6253     },
6254     // private
6255     onSubmit : function(e){
6256         e.stopEvent();
6257     },
6258     
6259      /**
6260      * Returns true if client-side validation on the form is successful.
6261      * @return Boolean
6262      */
6263     isValid : function(){
6264         var items = this.getItems();
6265         var valid = true;
6266         items.each(function(f){
6267            if(!f.validate()){
6268                valid = false;
6269                
6270            }
6271         });
6272         return valid;
6273     },
6274     /**
6275      * Returns true if any fields in this form have changed since their original load.
6276      * @return Boolean
6277      */
6278     isDirty : function(){
6279         var dirty = false;
6280         var items = this.getItems();
6281         items.each(function(f){
6282            if(f.isDirty()){
6283                dirty = true;
6284                return false;
6285            }
6286            return true;
6287         });
6288         return dirty;
6289     },
6290      /**
6291      * Performs a predefined action (submit or load) or custom actions you define on this form.
6292      * @param {String} actionName The name of the action type
6293      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
6294      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
6295      * accept other config options):
6296      * <pre>
6297 Property          Type             Description
6298 ----------------  ---------------  ----------------------------------------------------------------------------------
6299 url               String           The url for the action (defaults to the form's url)
6300 method            String           The form method to use (defaults to the form's method, or POST if not defined)
6301 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
6302 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
6303                                    validate the form on the client (defaults to false)
6304      * </pre>
6305      * @return {BasicForm} this
6306      */
6307     doAction : function(action, options){
6308         if(typeof action == 'string'){
6309             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
6310         }
6311         if(this.fireEvent('beforeaction', this, action) !== false){
6312             this.beforeAction(action);
6313             action.run.defer(100, action);
6314         }
6315         return this;
6316     },
6317     
6318     // private
6319     beforeAction : function(action){
6320         var o = action.options;
6321         
6322         // not really supported yet.. ??
6323         
6324         //if(this.waitMsgTarget === true){
6325             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6326         //}else if(this.waitMsgTarget){
6327         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
6328         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
6329         //}else {
6330         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
6331        // }
6332          
6333     },
6334
6335     // private
6336     afterAction : function(action, success){
6337         this.activeAction = null;
6338         var o = action.options;
6339         
6340         //if(this.waitMsgTarget === true){
6341             this.el.unmask();
6342         //}else if(this.waitMsgTarget){
6343         //    this.waitMsgTarget.unmask();
6344         //}else{
6345         //    Roo.MessageBox.updateProgress(1);
6346         //    Roo.MessageBox.hide();
6347        // }
6348         // 
6349         if(success){
6350             if(o.reset){
6351                 this.reset();
6352             }
6353             Roo.callback(o.success, o.scope, [this, action]);
6354             this.fireEvent('actioncomplete', this, action);
6355             
6356         }else{
6357             
6358             // failure condition..
6359             // we have a scenario where updates need confirming.
6360             // eg. if a locking scenario exists..
6361             // we look for { errors : { needs_confirm : true }} in the response.
6362             if (
6363                 (typeof(action.result) != 'undefined')  &&
6364                 (typeof(action.result.errors) != 'undefined')  &&
6365                 (typeof(action.result.errors.needs_confirm) != 'undefined')
6366            ){
6367                 var _t = this;
6368                 Roo.log("not supported yet");
6369                  /*
6370                 
6371                 Roo.MessageBox.confirm(
6372                     "Change requires confirmation",
6373                     action.result.errorMsg,
6374                     function(r) {
6375                         if (r != 'yes') {
6376                             return;
6377                         }
6378                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
6379                     }
6380                     
6381                 );
6382                 */
6383                 
6384                 
6385                 return;
6386             }
6387             
6388             Roo.callback(o.failure, o.scope, [this, action]);
6389             // show an error message if no failed handler is set..
6390             if (!this.hasListener('actionfailed')) {
6391                 Roo.log("need to add dialog support");
6392                 /*
6393                 Roo.MessageBox.alert("Error",
6394                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
6395                         action.result.errorMsg :
6396                         "Saving Failed, please check your entries or try again"
6397                 );
6398                 */
6399             }
6400             
6401             this.fireEvent('actionfailed', this, action);
6402         }
6403         
6404     },
6405     /**
6406      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
6407      * @param {String} id The value to search for
6408      * @return Field
6409      */
6410     findField : function(id){
6411         var items = this.getItems();
6412         var field = items.get(id);
6413         if(!field){
6414              items.each(function(f){
6415                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
6416                     field = f;
6417                     return false;
6418                 }
6419                 return true;
6420             });
6421         }
6422         return field || null;
6423     },
6424      /**
6425      * Mark fields in this form invalid in bulk.
6426      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
6427      * @return {BasicForm} this
6428      */
6429     markInvalid : function(errors){
6430         if(errors instanceof Array){
6431             for(var i = 0, len = errors.length; i < len; i++){
6432                 var fieldError = errors[i];
6433                 var f = this.findField(fieldError.id);
6434                 if(f){
6435                     f.markInvalid(fieldError.msg);
6436                 }
6437             }
6438         }else{
6439             var field, id;
6440             for(id in errors){
6441                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
6442                     field.markInvalid(errors[id]);
6443                 }
6444             }
6445         }
6446         //Roo.each(this.childForms || [], function (f) {
6447         //    f.markInvalid(errors);
6448         //});
6449         
6450         return this;
6451     },
6452
6453     /**
6454      * Set values for fields in this form in bulk.
6455      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
6456      * @return {BasicForm} this
6457      */
6458     setValues : function(values){
6459         if(values instanceof Array){ // array of objects
6460             for(var i = 0, len = values.length; i < len; i++){
6461                 var v = values[i];
6462                 var f = this.findField(v.id);
6463                 if(f){
6464                     f.setValue(v.value);
6465                     if(this.trackResetOnLoad){
6466                         f.originalValue = f.getValue();
6467                     }
6468                 }
6469             }
6470         }else{ // object hash
6471             var field, id;
6472             for(id in values){
6473                 if(typeof values[id] != 'function' && (field = this.findField(id))){
6474                     
6475                     if (field.setFromData && 
6476                         field.valueField && 
6477                         field.displayField &&
6478                         // combos' with local stores can 
6479                         // be queried via setValue()
6480                         // to set their value..
6481                         (field.store && !field.store.isLocal)
6482                         ) {
6483                         // it's a combo
6484                         var sd = { };
6485                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
6486                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
6487                         field.setFromData(sd);
6488                         
6489                     } else {
6490                         field.setValue(values[id]);
6491                     }
6492                     
6493                     
6494                     if(this.trackResetOnLoad){
6495                         field.originalValue = field.getValue();
6496                     }
6497                 }
6498             }
6499         }
6500          
6501         //Roo.each(this.childForms || [], function (f) {
6502         //    f.setValues(values);
6503         //});
6504                 
6505         return this;
6506     },
6507
6508     /**
6509      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
6510      * they are returned as an array.
6511      * @param {Boolean} asString
6512      * @return {Object}
6513      */
6514     getValues : function(asString){
6515         //if (this.childForms) {
6516             // copy values from the child forms
6517         //    Roo.each(this.childForms, function (f) {
6518         //        this.setValues(f.getValues());
6519         //    }, this);
6520         //}
6521         
6522         
6523         
6524         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
6525         if(asString === true){
6526             return fs;
6527         }
6528         return Roo.urlDecode(fs);
6529     },
6530     
6531     /**
6532      * Returns the fields in this form as an object with key/value pairs. 
6533      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
6534      * @return {Object}
6535      */
6536     getFieldValues : function(with_hidden)
6537     {
6538         var items = this.getItems();
6539         var ret = {};
6540         items.each(function(f){
6541             if (!f.getName()) {
6542                 return;
6543             }
6544             var v = f.getValue();
6545             if (f.inputType =='radio') {
6546                 if (typeof(ret[f.getName()]) == 'undefined') {
6547                     ret[f.getName()] = ''; // empty..
6548                 }
6549                 
6550                 if (!f.el.dom.checked) {
6551                     return;
6552                     
6553                 }
6554                 v = f.el.dom.value;
6555                 
6556             }
6557             
6558             // not sure if this supported any more..
6559             if ((typeof(v) == 'object') && f.getRawValue) {
6560                 v = f.getRawValue() ; // dates..
6561             }
6562             // combo boxes where name != hiddenName...
6563             if (f.name != f.getName()) {
6564                 ret[f.name] = f.getRawValue();
6565             }
6566             ret[f.getName()] = v;
6567         });
6568         
6569         return ret;
6570     },
6571
6572     /**
6573      * Clears all invalid messages in this form.
6574      * @return {BasicForm} this
6575      */
6576     clearInvalid : function(){
6577         var items = this.getItems();
6578         
6579         items.each(function(f){
6580            f.clearInvalid();
6581         });
6582         
6583         
6584         
6585         return this;
6586     },
6587
6588     /**
6589      * Resets this form.
6590      * @return {BasicForm} this
6591      */
6592     reset : function(){
6593         var items = this.getItems();
6594         items.each(function(f){
6595             f.reset();
6596         });
6597         
6598         Roo.each(this.childForms || [], function (f) {
6599             f.reset();
6600         });
6601        
6602         
6603         return this;
6604     },
6605     getItems : function()
6606     {
6607         var r=new Roo.util.MixedCollection(false, function(o){
6608             return o.id || (o.id = Roo.id());
6609         });
6610         var iter = function(el) {
6611             if (el.inputEl) {
6612                 r.add(el);
6613             }
6614             if (!el.items) {
6615                 return;
6616             }
6617             Roo.each(el.items,function(e) {
6618                 iter(e);
6619             });
6620             
6621             
6622         };
6623         iter(this);
6624         return r;
6625         
6626         
6627         
6628         
6629     }
6630     
6631 });
6632
6633  
6634 /*
6635  * Based on:
6636  * Ext JS Library 1.1.1
6637  * Copyright(c) 2006-2007, Ext JS, LLC.
6638  *
6639  * Originally Released Under LGPL - original licence link has changed is not relivant.
6640  *
6641  * Fork - LGPL
6642  * <script type="text/javascript">
6643  */
6644 /**
6645  * @class Roo.form.VTypes
6646  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
6647  * @singleton
6648  */
6649 Roo.form.VTypes = function(){
6650     // closure these in so they are only created once.
6651     var alpha = /^[a-zA-Z_]+$/;
6652     var alphanum = /^[a-zA-Z0-9_]+$/;
6653     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
6654     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
6655
6656     // All these messages and functions are configurable
6657     return {
6658         /**
6659          * The function used to validate email addresses
6660          * @param {String} value The email address
6661          */
6662         'email' : function(v){
6663             return email.test(v);
6664         },
6665         /**
6666          * The error text to display when the email validation function returns false
6667          * @type String
6668          */
6669         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
6670         /**
6671          * The keystroke filter mask to be applied on email input
6672          * @type RegExp
6673          */
6674         'emailMask' : /[a-z0-9_\.\-@]/i,
6675
6676         /**
6677          * The function used to validate URLs
6678          * @param {String} value The URL
6679          */
6680         'url' : function(v){
6681             return url.test(v);
6682         },
6683         /**
6684          * The error text to display when the url validation function returns false
6685          * @type String
6686          */
6687         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
6688         
6689         /**
6690          * The function used to validate alpha values
6691          * @param {String} value The value
6692          */
6693         'alpha' : function(v){
6694             return alpha.test(v);
6695         },
6696         /**
6697          * The error text to display when the alpha validation function returns false
6698          * @type String
6699          */
6700         'alphaText' : 'This field should only contain letters and _',
6701         /**
6702          * The keystroke filter mask to be applied on alpha input
6703          * @type RegExp
6704          */
6705         'alphaMask' : /[a-z_]/i,
6706
6707         /**
6708          * The function used to validate alphanumeric values
6709          * @param {String} value The value
6710          */
6711         'alphanum' : function(v){
6712             return alphanum.test(v);
6713         },
6714         /**
6715          * The error text to display when the alphanumeric validation function returns false
6716          * @type String
6717          */
6718         'alphanumText' : 'This field should only contain letters, numbers and _',
6719         /**
6720          * The keystroke filter mask to be applied on alphanumeric input
6721          * @type RegExp
6722          */
6723         'alphanumMask' : /[a-z0-9_]/i
6724     };
6725 }();/*
6726  * - LGPL
6727  *
6728  * Input
6729  * 
6730  */
6731
6732 /**
6733  * @class Roo.bootstrap.Input
6734  * @extends Roo.bootstrap.Component
6735  * Bootstrap Input class
6736  * @cfg {Boolean} disabled is it disabled
6737  * @cfg {String} fieldLabel - the label associated
6738  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
6739  * @cfg {String} name name of the input
6740  * @cfg {string} fieldLabel - the label associated
6741  * @cfg {string}  inputType - input / file submit ...
6742  * @cfg {string} placeholder - placeholder to put in text.
6743  * @cfg {string}  before - input group add on before
6744  * @cfg {string} after - input group add on after
6745  * @cfg {string} size - (lg|sm) or leave empty..
6746  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
6747  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
6748  * @cfg {Number} md colspan out of 12 for computer-sized screens
6749  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
6750  * @cfg {string} value default value of the input
6751  * @cfg {Number} labelWidth set the width of label (0-12)
6752  * @cfg {String} labelAlign (top|left)
6753  * @cfg {Boolean} readOnly Specifies that the field should be read-only
6754  * @cfg {String} align (left|center|right) Default left
6755  * 
6756  * 
6757  * @constructor
6758  * Create a new Input
6759  * @param {Object} config The config object
6760  */
6761
6762 Roo.bootstrap.Input = function(config){
6763     Roo.bootstrap.Input.superclass.constructor.call(this, config);
6764    
6765         this.addEvents({
6766             /**
6767              * @event focus
6768              * Fires when this field receives input focus.
6769              * @param {Roo.form.Field} this
6770              */
6771             focus : true,
6772             /**
6773              * @event blur
6774              * Fires when this field loses input focus.
6775              * @param {Roo.form.Field} this
6776              */
6777             blur : true,
6778             /**
6779              * @event specialkey
6780              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
6781              * {@link Roo.EventObject#getKey} to determine which key was pressed.
6782              * @param {Roo.form.Field} this
6783              * @param {Roo.EventObject} e The event object
6784              */
6785             specialkey : true,
6786             /**
6787              * @event change
6788              * Fires just before the field blurs if the field value has changed.
6789              * @param {Roo.form.Field} this
6790              * @param {Mixed} newValue The new value
6791              * @param {Mixed} oldValue The original value
6792              */
6793             change : true,
6794             /**
6795              * @event invalid
6796              * Fires after the field has been marked as invalid.
6797              * @param {Roo.form.Field} this
6798              * @param {String} msg The validation message
6799              */
6800             invalid : true,
6801             /**
6802              * @event valid
6803              * Fires after the field has been validated with no errors.
6804              * @param {Roo.form.Field} this
6805              */
6806             valid : true,
6807              /**
6808              * @event keyup
6809              * Fires after the key up
6810              * @param {Roo.form.Field} this
6811              * @param {Roo.EventObject}  e The event Object
6812              */
6813             keyup : true
6814         });
6815 };
6816
6817 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
6818      /**
6819      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
6820       automatic validation (defaults to "keyup").
6821      */
6822     validationEvent : "keyup",
6823      /**
6824      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
6825      */
6826     validateOnBlur : true,
6827     /**
6828      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
6829      */
6830     validationDelay : 250,
6831      /**
6832      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
6833      */
6834     focusClass : "x-form-focus",  // not needed???
6835     
6836        
6837     /**
6838      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
6839      */
6840     invalidClass : "has-error",
6841     
6842     /**
6843      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
6844      */
6845     selectOnFocus : false,
6846     
6847      /**
6848      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
6849      */
6850     maskRe : null,
6851        /**
6852      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
6853      */
6854     vtype : null,
6855     
6856       /**
6857      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
6858      */
6859     disableKeyFilter : false,
6860     
6861        /**
6862      * @cfg {Boolean} disabled True to disable the field (defaults to false).
6863      */
6864     disabled : false,
6865      /**
6866      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
6867      */
6868     allowBlank : true,
6869     /**
6870      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
6871      */
6872     blankText : "This field is required",
6873     
6874      /**
6875      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
6876      */
6877     minLength : 0,
6878     /**
6879      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
6880      */
6881     maxLength : Number.MAX_VALUE,
6882     /**
6883      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
6884      */
6885     minLengthText : "The minimum length for this field is {0}",
6886     /**
6887      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
6888      */
6889     maxLengthText : "The maximum length for this field is {0}",
6890   
6891     
6892     /**
6893      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
6894      * If available, this function will be called only after the basic validators all return true, and will be passed the
6895      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
6896      */
6897     validator : null,
6898     /**
6899      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
6900      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
6901      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
6902      */
6903     regex : null,
6904     /**
6905      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
6906      */
6907     regexText : "",
6908     
6909     
6910     
6911     fieldLabel : '',
6912     inputType : 'text',
6913     
6914     name : false,
6915     placeholder: false,
6916     before : false,
6917     after : false,
6918     size : false,
6919     // private
6920     hasFocus : false,
6921     preventMark: false,
6922     isFormField : true,
6923     value : '',
6924     labelWidth : 2,
6925     labelAlign : false,
6926     readOnly : false,
6927     align : false,
6928     formatedValue : false,
6929     
6930     parentLabelAlign : function()
6931     {
6932         var parent = this;
6933         while (parent.parent()) {
6934             parent = parent.parent();
6935             if (typeof(parent.labelAlign) !='undefined') {
6936                 return parent.labelAlign;
6937             }
6938         }
6939         return 'left';
6940         
6941     },
6942     
6943     getAutoCreate : function(){
6944         
6945         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
6946         
6947         var id = Roo.id();
6948         
6949         var cfg = {};
6950         
6951         if(this.inputType != 'hidden'){
6952             cfg.cls = 'form-group' //input-group
6953         }
6954         
6955         var input =  {
6956             tag: 'input',
6957             id : id,
6958             type : this.inputType,
6959             value : this.value,
6960             cls : 'form-control',
6961             placeholder : this.placeholder || ''
6962             
6963         };
6964         
6965         if(this.align){
6966             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
6967         }
6968         
6969         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
6970             input.maxLength = this.maxLength;
6971         }
6972         
6973         if (this.disabled) {
6974             input.disabled=true;
6975         }
6976         
6977         if (this.readOnly) {
6978             input.readonly=true;
6979         }
6980         
6981         if (this.name) {
6982             input.name = this.name;
6983         }
6984         if (this.size) {
6985             input.cls += ' input-' + this.size;
6986         }
6987         var settings=this;
6988         ['xs','sm','md','lg'].map(function(size){
6989             if (settings[size]) {
6990                 cfg.cls += ' col-' + size + '-' + settings[size];
6991             }
6992         });
6993         
6994         var inputblock = input;
6995         
6996         if (this.before || this.after) {
6997             
6998             inputblock = {
6999                 cls : 'input-group',
7000                 cn :  [] 
7001             };
7002             if (this.before && typeof(this.before) == 'string') {
7003                 
7004                 inputblock.cn.push({
7005                     tag :'span',
7006                     cls : 'roo-input-before input-group-addon',
7007                     html : this.before
7008                 });
7009             }
7010             if (this.before && typeof(this.before) == 'object') {
7011                 this.before = Roo.factory(this.before);
7012                 Roo.log(this.before);
7013                 inputblock.cn.push({
7014                     tag :'span',
7015                     cls : 'roo-input-before input-group-' +
7016                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7017                 });
7018             }
7019             
7020             inputblock.cn.push(input);
7021             
7022             if (this.after && typeof(this.after) == 'string') {
7023                 inputblock.cn.push({
7024                     tag :'span',
7025                     cls : 'roo-input-after input-group-addon',
7026                     html : this.after
7027                 });
7028             }
7029             if (this.after && typeof(this.after) == 'object') {
7030                 this.after = Roo.factory(this.after);
7031                 Roo.log(this.after);
7032                 inputblock.cn.push({
7033                     tag :'span',
7034                     cls : 'roo-input-after input-group-' +
7035                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7036                 });
7037             }
7038         };
7039         
7040         if (align ==='left' && this.fieldLabel.length) {
7041                 Roo.log("left and has label");
7042                 cfg.cn = [
7043                     
7044                     {
7045                         tag: 'label',
7046                         'for' :  id,
7047                         cls : 'control-label col-sm-' + this.labelWidth,
7048                         html : this.fieldLabel
7049                         
7050                     },
7051                     {
7052                         cls : "col-sm-" + (12 - this.labelWidth), 
7053                         cn: [
7054                             inputblock
7055                         ]
7056                     }
7057                     
7058                 ];
7059         } else if ( this.fieldLabel.length) {
7060                 Roo.log(" label");
7061                  cfg.cn = [
7062                    
7063                     {
7064                         tag: 'label',
7065                         //cls : 'input-group-addon',
7066                         html : this.fieldLabel
7067                         
7068                     },
7069                     
7070                     inputblock
7071                     
7072                 ];
7073
7074         } else {
7075             
7076                 Roo.log(" no label && no align");
7077                 cfg.cn = [
7078                     
7079                         inputblock
7080                     
7081                 ];
7082                 
7083                 
7084         };
7085         Roo.log('input-parentType: ' + this.parentType);
7086         
7087         if (this.parentType === 'Navbar' &&  this.parent().bar) {
7088            cfg.cls += ' navbar-form';
7089            Roo.log(cfg);
7090         }
7091         
7092         return cfg;
7093         
7094     },
7095     /**
7096      * return the real input element.
7097      */
7098     inputEl: function ()
7099     {
7100         return this.el.select('input.form-control',true).first();
7101     },
7102     setDisabled : function(v)
7103     {
7104         var i  = this.inputEl().dom;
7105         if (!v) {
7106             i.removeAttribute('disabled');
7107             return;
7108             
7109         }
7110         i.setAttribute('disabled','true');
7111     },
7112     initEvents : function()
7113     {
7114         
7115         this.inputEl().on("keydown" , this.fireKey,  this);
7116         this.inputEl().on("focus", this.onFocus,  this);
7117         this.inputEl().on("blur", this.onBlur,  this);
7118         
7119         this.inputEl().relayEvent('keyup', this);
7120
7121         // reference to original value for reset
7122         this.originalValue = this.getValue();
7123         //Roo.form.TextField.superclass.initEvents.call(this);
7124         if(this.validationEvent == 'keyup'){
7125             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
7126             this.inputEl().on('keyup', this.filterValidation, this);
7127         }
7128         else if(this.validationEvent !== false){
7129             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
7130         }
7131         
7132         if(this.selectOnFocus){
7133             this.on("focus", this.preFocus, this);
7134             
7135         }
7136         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
7137             this.inputEl().on("keypress", this.filterKeys, this);
7138         }
7139        /* if(this.grow){
7140             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
7141             this.el.on("click", this.autoSize,  this);
7142         }
7143         */
7144         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
7145             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
7146         }
7147         
7148         if (typeof(this.before) == 'object') {
7149             this.before.render(this.el.select('.roo-input-before',true).first());
7150         }
7151         if (typeof(this.after) == 'object') {
7152             this.after.render(this.el.select('.roo-input-after',true).first());
7153         }
7154         
7155         
7156     },
7157     filterValidation : function(e){
7158         if(!e.isNavKeyPress()){
7159             this.validationTask.delay(this.validationDelay);
7160         }
7161     },
7162      /**
7163      * Validates the field value
7164      * @return {Boolean} True if the value is valid, else false
7165      */
7166     validate : function(){
7167         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
7168         if(this.disabled || this.validateValue(this.getRawValue())){
7169             this.clearInvalid();
7170             return true;
7171         }
7172         return false;
7173     },
7174     
7175     
7176     /**
7177      * Validates a value according to the field's validation rules and marks the field as invalid
7178      * if the validation fails
7179      * @param {Mixed} value The value to validate
7180      * @return {Boolean} True if the value is valid, else false
7181      */
7182     validateValue : function(value){
7183         if(value.length < 1)  { // if it's blank
7184              if(this.allowBlank){
7185                 this.clearInvalid();
7186                 return true;
7187              }else{
7188                 this.markInvalid(this.blankText);
7189                 return false;
7190              }
7191         }
7192         if(value.length < this.minLength){
7193             this.markInvalid(String.format(this.minLengthText, this.minLength));
7194             return false;
7195         }
7196         if(value.length > this.maxLength){
7197             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
7198             return false;
7199         }
7200         if(this.vtype){
7201             var vt = Roo.form.VTypes;
7202             if(!vt[this.vtype](value, this)){
7203                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
7204                 return false;
7205             }
7206         }
7207         if(typeof this.validator == "function"){
7208             var msg = this.validator(value);
7209             if(msg !== true){
7210                 this.markInvalid(msg);
7211                 return false;
7212             }
7213         }
7214         if(this.regex && !this.regex.test(value)){
7215             this.markInvalid(this.regexText);
7216             return false;
7217         }
7218         return true;
7219     },
7220
7221     
7222     
7223      // private
7224     fireKey : function(e){
7225         //Roo.log('field ' + e.getKey());
7226         if(e.isNavKeyPress()){
7227             this.fireEvent("specialkey", this, e);
7228         }
7229     },
7230     focus : function (selectText){
7231         if(this.rendered){
7232             this.inputEl().focus();
7233             if(selectText === true){
7234                 this.inputEl().dom.select();
7235             }
7236         }
7237         return this;
7238     } ,
7239     
7240     onFocus : function(){
7241         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7242            // this.el.addClass(this.focusClass);
7243         }
7244         if(!this.hasFocus){
7245             this.hasFocus = true;
7246             this.startValue = this.getValue();
7247             this.fireEvent("focus", this);
7248         }
7249     },
7250     
7251     beforeBlur : Roo.emptyFn,
7252
7253     
7254     // private
7255     onBlur : function(){
7256         this.beforeBlur();
7257         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7258             //this.el.removeClass(this.focusClass);
7259         }
7260         this.hasFocus = false;
7261         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
7262             this.validate();
7263         }
7264         var v = this.getValue();
7265         if(String(v) !== String(this.startValue)){
7266             this.fireEvent('change', this, v, this.startValue);
7267         }
7268         this.fireEvent("blur", this);
7269     },
7270     
7271     /**
7272      * Resets the current field value to the originally loaded value and clears any validation messages
7273      */
7274     reset : function(){
7275         this.setValue(this.originalValue);
7276         this.clearInvalid();
7277     },
7278      /**
7279      * Returns the name of the field
7280      * @return {Mixed} name The name field
7281      */
7282     getName: function(){
7283         return this.name;
7284     },
7285      /**
7286      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
7287      * @return {Mixed} value The field value
7288      */
7289     getValue : function(){
7290         
7291         var v = this.inputEl().getValue();
7292         
7293         return v;
7294     },
7295     /**
7296      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
7297      * @return {Mixed} value The field value
7298      */
7299     getRawValue : function(){
7300         var v = this.inputEl().getValue();
7301         
7302         return v;
7303     },
7304     
7305     /**
7306      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
7307      * @param {Mixed} value The value to set
7308      */
7309     setRawValue : function(v){
7310         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7311     },
7312     
7313     selectText : function(start, end){
7314         var v = this.getRawValue();
7315         if(v.length > 0){
7316             start = start === undefined ? 0 : start;
7317             end = end === undefined ? v.length : end;
7318             var d = this.inputEl().dom;
7319             if(d.setSelectionRange){
7320                 d.setSelectionRange(start, end);
7321             }else if(d.createTextRange){
7322                 var range = d.createTextRange();
7323                 range.moveStart("character", start);
7324                 range.moveEnd("character", v.length-end);
7325                 range.select();
7326             }
7327         }
7328     },
7329     
7330     /**
7331      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
7332      * @param {Mixed} value The value to set
7333      */
7334     setValue : function(v){
7335         this.value = v;
7336         if(this.rendered){
7337             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7338             this.validate();
7339         }
7340     },
7341     
7342     /*
7343     processValue : function(value){
7344         if(this.stripCharsRe){
7345             var newValue = value.replace(this.stripCharsRe, '');
7346             if(newValue !== value){
7347                 this.setRawValue(newValue);
7348                 return newValue;
7349             }
7350         }
7351         return value;
7352     },
7353   */
7354     preFocus : function(){
7355         
7356         if(this.selectOnFocus){
7357             this.inputEl().dom.select();
7358         }
7359     },
7360     filterKeys : function(e){
7361         var k = e.getKey();
7362         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
7363             return;
7364         }
7365         var c = e.getCharCode(), cc = String.fromCharCode(c);
7366         if(Roo.isIE && (e.isSpecialKey() || !cc)){
7367             return;
7368         }
7369         if(!this.maskRe.test(cc)){
7370             e.stopEvent();
7371         }
7372     },
7373      /**
7374      * Clear any invalid styles/messages for this field
7375      */
7376     clearInvalid : function(){
7377         
7378         if(!this.el || this.preventMark){ // not rendered
7379             return;
7380         }
7381         this.el.removeClass(this.invalidClass);
7382         /*
7383         switch(this.msgTarget){
7384             case 'qtip':
7385                 this.el.dom.qtip = '';
7386                 break;
7387             case 'title':
7388                 this.el.dom.title = '';
7389                 break;
7390             case 'under':
7391                 if(this.errorEl){
7392                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
7393                 }
7394                 break;
7395             case 'side':
7396                 if(this.errorIcon){
7397                     this.errorIcon.dom.qtip = '';
7398                     this.errorIcon.hide();
7399                     this.un('resize', this.alignErrorIcon, this);
7400                 }
7401                 break;
7402             default:
7403                 var t = Roo.getDom(this.msgTarget);
7404                 t.innerHTML = '';
7405                 t.style.display = 'none';
7406                 break;
7407         }
7408         */
7409         this.fireEvent('valid', this);
7410     },
7411      /**
7412      * Mark this field as invalid
7413      * @param {String} msg The validation message
7414      */
7415     markInvalid : function(msg){
7416         if(!this.el  || this.preventMark){ // not rendered
7417             return;
7418         }
7419         this.el.addClass(this.invalidClass);
7420         /*
7421         msg = msg || this.invalidText;
7422         switch(this.msgTarget){
7423             case 'qtip':
7424                 this.el.dom.qtip = msg;
7425                 this.el.dom.qclass = 'x-form-invalid-tip';
7426                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
7427                     Roo.QuickTips.enable();
7428                 }
7429                 break;
7430             case 'title':
7431                 this.el.dom.title = msg;
7432                 break;
7433             case 'under':
7434                 if(!this.errorEl){
7435                     var elp = this.el.findParent('.x-form-element', 5, true);
7436                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
7437                     this.errorEl.setWidth(elp.getWidth(true)-20);
7438                 }
7439                 this.errorEl.update(msg);
7440                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
7441                 break;
7442             case 'side':
7443                 if(!this.errorIcon){
7444                     var elp = this.el.findParent('.x-form-element', 5, true);
7445                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
7446                 }
7447                 this.alignErrorIcon();
7448                 this.errorIcon.dom.qtip = msg;
7449                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
7450                 this.errorIcon.show();
7451                 this.on('resize', this.alignErrorIcon, this);
7452                 break;
7453             default:
7454                 var t = Roo.getDom(this.msgTarget);
7455                 t.innerHTML = msg;
7456                 t.style.display = this.msgDisplay;
7457                 break;
7458         }
7459         */
7460         this.fireEvent('invalid', this, msg);
7461     },
7462     // private
7463     SafariOnKeyDown : function(event)
7464     {
7465         // this is a workaround for a password hang bug on chrome/ webkit.
7466         
7467         var isSelectAll = false;
7468         
7469         if(this.inputEl().dom.selectionEnd > 0){
7470             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
7471         }
7472         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
7473             event.preventDefault();
7474             this.setValue('');
7475             return;
7476         }
7477         
7478         if(isSelectAll){ // backspace and delete key
7479             
7480             event.preventDefault();
7481             // this is very hacky as keydown always get's upper case.
7482             //
7483             var cc = String.fromCharCode(event.getCharCode());
7484             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
7485             
7486         }
7487     },
7488     adjustWidth : function(tag, w){
7489         tag = tag.toLowerCase();
7490         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
7491             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
7492                 if(tag == 'input'){
7493                     return w + 2;
7494                 }
7495                 if(tag == 'textarea'){
7496                     return w-2;
7497                 }
7498             }else if(Roo.isOpera){
7499                 if(tag == 'input'){
7500                     return w + 2;
7501                 }
7502                 if(tag == 'textarea'){
7503                     return w-2;
7504                 }
7505             }
7506         }
7507         return w;
7508     }
7509     
7510 });
7511
7512  
7513 /*
7514  * - LGPL
7515  *
7516  * Input
7517  * 
7518  */
7519
7520 /**
7521  * @class Roo.bootstrap.TextArea
7522  * @extends Roo.bootstrap.Input
7523  * Bootstrap TextArea class
7524  * @cfg {Number} cols Specifies the visible width of a text area
7525  * @cfg {Number} rows Specifies the visible number of lines in a text area
7526  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
7527  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
7528  * @cfg {string} html text
7529  * 
7530  * @constructor
7531  * Create a new TextArea
7532  * @param {Object} config The config object
7533  */
7534
7535 Roo.bootstrap.TextArea = function(config){
7536     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
7537    
7538 };
7539
7540 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
7541      
7542     cols : false,
7543     rows : 5,
7544     readOnly : false,
7545     warp : 'soft',
7546     resize : false,
7547     value: false,
7548     html: false,
7549     
7550     getAutoCreate : function(){
7551         
7552         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7553         
7554         var id = Roo.id();
7555         
7556         var cfg = {};
7557         
7558         var input =  {
7559             tag: 'textarea',
7560             id : id,
7561             warp : this.warp,
7562             rows : this.rows,
7563             value : this.value || '',
7564             html: this.html || '',
7565             cls : 'form-control',
7566             placeholder : this.placeholder || '' 
7567             
7568         };
7569         
7570         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7571             input.maxLength = this.maxLength;
7572         }
7573         
7574         if(this.resize){
7575             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
7576         }
7577         
7578         if(this.cols){
7579             input.cols = this.cols;
7580         }
7581         
7582         if (this.readOnly) {
7583             input.readonly = true;
7584         }
7585         
7586         if (this.name) {
7587             input.name = this.name;
7588         }
7589         
7590         if (this.size) {
7591             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
7592         }
7593         
7594         var settings=this;
7595         ['xs','sm','md','lg'].map(function(size){
7596             if (settings[size]) {
7597                 cfg.cls += ' col-' + size + '-' + settings[size];
7598             }
7599         });
7600         
7601         var inputblock = input;
7602         
7603         if (this.before || this.after) {
7604             
7605             inputblock = {
7606                 cls : 'input-group',
7607                 cn :  [] 
7608             };
7609             if (this.before) {
7610                 inputblock.cn.push({
7611                     tag :'span',
7612                     cls : 'input-group-addon',
7613                     html : this.before
7614                 });
7615             }
7616             inputblock.cn.push(input);
7617             if (this.after) {
7618                 inputblock.cn.push({
7619                     tag :'span',
7620                     cls : 'input-group-addon',
7621                     html : this.after
7622                 });
7623             }
7624             
7625         }
7626         
7627         if (align ==='left' && this.fieldLabel.length) {
7628                 Roo.log("left and has label");
7629                 cfg.cn = [
7630                     
7631                     {
7632                         tag: 'label',
7633                         'for' :  id,
7634                         cls : 'control-label col-sm-' + this.labelWidth,
7635                         html : this.fieldLabel
7636                         
7637                     },
7638                     {
7639                         cls : "col-sm-" + (12 - this.labelWidth), 
7640                         cn: [
7641                             inputblock
7642                         ]
7643                     }
7644                     
7645                 ];
7646         } else if ( this.fieldLabel.length) {
7647                 Roo.log(" label");
7648                  cfg.cn = [
7649                    
7650                     {
7651                         tag: 'label',
7652                         //cls : 'input-group-addon',
7653                         html : this.fieldLabel
7654                         
7655                     },
7656                     
7657                     inputblock
7658                     
7659                 ];
7660
7661         } else {
7662             
7663                    Roo.log(" no label && no align");
7664                 cfg.cn = [
7665                     
7666                         inputblock
7667                     
7668                 ];
7669                 
7670                 
7671         }
7672         
7673         if (this.disabled) {
7674             input.disabled=true;
7675         }
7676         
7677         return cfg;
7678         
7679     },
7680     /**
7681      * return the real textarea element.
7682      */
7683     inputEl: function ()
7684     {
7685         return this.el.select('textarea.form-control',true).first();
7686     }
7687 });
7688
7689  
7690 /*
7691  * - LGPL
7692  *
7693  * trigger field - base class for combo..
7694  * 
7695  */
7696  
7697 /**
7698  * @class Roo.bootstrap.TriggerField
7699  * @extends Roo.bootstrap.Input
7700  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
7701  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
7702  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
7703  * for which you can provide a custom implementation.  For example:
7704  * <pre><code>
7705 var trigger = new Roo.bootstrap.TriggerField();
7706 trigger.onTriggerClick = myTriggerFn;
7707 trigger.applyTo('my-field');
7708 </code></pre>
7709  *
7710  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
7711  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
7712  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
7713  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
7714  * @constructor
7715  * Create a new TriggerField.
7716  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
7717  * to the base TextField)
7718  */
7719 Roo.bootstrap.TriggerField = function(config){
7720     this.mimicing = false;
7721     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
7722 };
7723
7724 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
7725     /**
7726      * @cfg {String} triggerClass A CSS class to apply to the trigger
7727      */
7728      /**
7729      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
7730      */
7731     hideTrigger:false,
7732
7733     /** @cfg {Boolean} grow @hide */
7734     /** @cfg {Number} growMin @hide */
7735     /** @cfg {Number} growMax @hide */
7736
7737     /**
7738      * @hide 
7739      * @method
7740      */
7741     autoSize: Roo.emptyFn,
7742     // private
7743     monitorTab : true,
7744     // private
7745     deferHeight : true,
7746
7747     
7748     actionMode : 'wrap',
7749     
7750     
7751     
7752     getAutoCreate : function(){
7753        
7754         var align = this.labelAlign || this.parentLabelAlign();
7755         
7756         var id = Roo.id();
7757         
7758         var cfg = {
7759             cls: 'form-group' //input-group
7760         };
7761         
7762         
7763         var input =  {
7764             tag: 'input',
7765             id : id,
7766             type : this.inputType,
7767             cls : 'form-control',
7768             autocomplete: 'off',
7769             placeholder : this.placeholder || '' 
7770             
7771         };
7772         if (this.name) {
7773             input.name = this.name;
7774         }
7775         if (this.size) {
7776             input.cls += ' input-' + this.size;
7777         }
7778         
7779         if (this.disabled) {
7780             input.disabled=true;
7781         }
7782         
7783         var inputblock = input;
7784         
7785         if (this.before || this.after) {
7786             
7787             inputblock = {
7788                 cls : 'input-group',
7789                 cn :  [] 
7790             };
7791             if (this.before) {
7792                 inputblock.cn.push({
7793                     tag :'span',
7794                     cls : 'input-group-addon',
7795                     html : this.before
7796                 });
7797             }
7798             inputblock.cn.push(input);
7799             if (this.after) {
7800                 inputblock.cn.push({
7801                     tag :'span',
7802                     cls : 'input-group-addon',
7803                     html : this.after
7804                 });
7805             }
7806             
7807         };
7808         
7809         var box = {
7810             tag: 'div',
7811             cn: [
7812                 {
7813                     tag: 'input',
7814                     type : 'hidden',
7815                     cls: 'form-hidden-field'
7816                 },
7817                 inputblock
7818             ]
7819             
7820         };
7821         
7822         if(this.multiple){
7823             Roo.log('multiple');
7824             
7825             box = {
7826                 tag: 'div',
7827                 cn: [
7828                     {
7829                         tag: 'input',
7830                         type : 'hidden',
7831                         cls: 'form-hidden-field'
7832                     },
7833                     {
7834                         tag: 'ul',
7835                         cls: 'select2-choices',
7836                         cn:[
7837                             {
7838                                 tag: 'li',
7839                                 cls: 'select2-search-field',
7840                                 cn: [
7841
7842                                     inputblock
7843                                 ]
7844                             }
7845                         ]
7846                     }
7847                 ]
7848             }
7849         };
7850         
7851         var combobox = {
7852             cls: 'select2-container input-group',
7853             cn: [
7854                 box
7855 //                {
7856 //                    tag: 'ul',
7857 //                    cls: 'typeahead typeahead-long dropdown-menu',
7858 //                    style: 'display:none'
7859 //                }
7860             ]
7861         };
7862         
7863         if(!this.multiple && this.showToggleBtn){
7864             combobox.cn.push({
7865                 tag :'span',
7866                 cls : 'input-group-addon btn dropdown-toggle',
7867                 cn : [
7868                     {
7869                         tag: 'span',
7870                         cls: 'caret'
7871                     },
7872                     {
7873                         tag: 'span',
7874                         cls: 'combobox-clear',
7875                         cn  : [
7876                             {
7877                                 tag : 'i',
7878                                 cls: 'icon-remove'
7879                             }
7880                         ]
7881                     }
7882                 ]
7883
7884             })
7885         }
7886         
7887         if(this.multiple){
7888             combobox.cls += ' select2-container-multi';
7889         }
7890         
7891         if (align ==='left' && this.fieldLabel.length) {
7892             
7893                 Roo.log("left and has label");
7894                 cfg.cn = [
7895                     
7896                     {
7897                         tag: 'label',
7898                         'for' :  id,
7899                         cls : 'control-label col-sm-' + this.labelWidth,
7900                         html : this.fieldLabel
7901                         
7902                     },
7903                     {
7904                         cls : "col-sm-" + (12 - this.labelWidth), 
7905                         cn: [
7906                             combobox
7907                         ]
7908                     }
7909                     
7910                 ];
7911         } else if ( this.fieldLabel.length) {
7912                 Roo.log(" label");
7913                  cfg.cn = [
7914                    
7915                     {
7916                         tag: 'label',
7917                         //cls : 'input-group-addon',
7918                         html : this.fieldLabel
7919                         
7920                     },
7921                     
7922                     combobox
7923                     
7924                 ];
7925
7926         } else {
7927             
7928                 Roo.log(" no label && no align");
7929                 cfg = combobox
7930                      
7931                 
7932         }
7933          
7934         var settings=this;
7935         ['xs','sm','md','lg'].map(function(size){
7936             if (settings[size]) {
7937                 cfg.cls += ' col-' + size + '-' + settings[size];
7938             }
7939         });
7940         
7941         return cfg;
7942         
7943     },
7944     
7945     
7946     
7947     // private
7948     onResize : function(w, h){
7949 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
7950 //        if(typeof w == 'number'){
7951 //            var x = w - this.trigger.getWidth();
7952 //            this.inputEl().setWidth(this.adjustWidth('input', x));
7953 //            this.trigger.setStyle('left', x+'px');
7954 //        }
7955     },
7956
7957     // private
7958     adjustSize : Roo.BoxComponent.prototype.adjustSize,
7959
7960     // private
7961     getResizeEl : function(){
7962         return this.inputEl();
7963     },
7964
7965     // private
7966     getPositionEl : function(){
7967         return this.inputEl();
7968     },
7969
7970     // private
7971     alignErrorIcon : function(){
7972         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
7973     },
7974
7975     // private
7976     initEvents : function(){
7977         
7978         this.createList();
7979         
7980         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
7981         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
7982         if(!this.multiple && this.showToggleBtn){
7983             this.trigger = this.el.select('span.dropdown-toggle',true).first();
7984             if(this.hideTrigger){
7985                 this.trigger.setDisplayed(false);
7986             }
7987             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
7988         }
7989         
7990         if(this.multiple){
7991             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
7992         }
7993         
7994         //this.trigger.addClassOnOver('x-form-trigger-over');
7995         //this.trigger.addClassOnClick('x-form-trigger-click');
7996         
7997         //if(!this.width){
7998         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
7999         //}
8000     },
8001     
8002     createList : function()
8003     {
8004         this.list = Roo.get(document.body).createChild({
8005             tag: 'ul',
8006             cls: 'typeahead typeahead-long dropdown-menu',
8007             style: 'display:none'
8008         });
8009         
8010         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
8011         
8012     },
8013
8014     // private
8015     initTrigger : function(){
8016        
8017     },
8018
8019     // private
8020     onDestroy : function(){
8021         if(this.trigger){
8022             this.trigger.removeAllListeners();
8023           //  this.trigger.remove();
8024         }
8025         //if(this.wrap){
8026         //    this.wrap.remove();
8027         //}
8028         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
8029     },
8030
8031     // private
8032     onFocus : function(){
8033         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
8034         /*
8035         if(!this.mimicing){
8036             this.wrap.addClass('x-trigger-wrap-focus');
8037             this.mimicing = true;
8038             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
8039             if(this.monitorTab){
8040                 this.el.on("keydown", this.checkTab, this);
8041             }
8042         }
8043         */
8044     },
8045
8046     // private
8047     checkTab : function(e){
8048         if(e.getKey() == e.TAB){
8049             this.triggerBlur();
8050         }
8051     },
8052
8053     // private
8054     onBlur : function(){
8055         // do nothing
8056     },
8057
8058     // private
8059     mimicBlur : function(e, t){
8060         /*
8061         if(!this.wrap.contains(t) && this.validateBlur()){
8062             this.triggerBlur();
8063         }
8064         */
8065     },
8066
8067     // private
8068     triggerBlur : function(){
8069         this.mimicing = false;
8070         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
8071         if(this.monitorTab){
8072             this.el.un("keydown", this.checkTab, this);
8073         }
8074         //this.wrap.removeClass('x-trigger-wrap-focus');
8075         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
8076     },
8077
8078     // private
8079     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
8080     validateBlur : function(e, t){
8081         return true;
8082     },
8083
8084     // private
8085     onDisable : function(){
8086         this.inputEl().dom.disabled = true;
8087         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
8088         //if(this.wrap){
8089         //    this.wrap.addClass('x-item-disabled');
8090         //}
8091     },
8092
8093     // private
8094     onEnable : function(){
8095         this.inputEl().dom.disabled = false;
8096         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
8097         //if(this.wrap){
8098         //    this.el.removeClass('x-item-disabled');
8099         //}
8100     },
8101
8102     // private
8103     onShow : function(){
8104         var ae = this.getActionEl();
8105         
8106         if(ae){
8107             ae.dom.style.display = '';
8108             ae.dom.style.visibility = 'visible';
8109         }
8110     },
8111
8112     // private
8113     
8114     onHide : function(){
8115         var ae = this.getActionEl();
8116         ae.dom.style.display = 'none';
8117     },
8118
8119     /**
8120      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
8121      * by an implementing function.
8122      * @method
8123      * @param {EventObject} e
8124      */
8125     onTriggerClick : Roo.emptyFn
8126 });
8127  /*
8128  * Based on:
8129  * Ext JS Library 1.1.1
8130  * Copyright(c) 2006-2007, Ext JS, LLC.
8131  *
8132  * Originally Released Under LGPL - original licence link has changed is not relivant.
8133  *
8134  * Fork - LGPL
8135  * <script type="text/javascript">
8136  */
8137
8138
8139 /**
8140  * @class Roo.data.SortTypes
8141  * @singleton
8142  * Defines the default sorting (casting?) comparison functions used when sorting data.
8143  */
8144 Roo.data.SortTypes = {
8145     /**
8146      * Default sort that does nothing
8147      * @param {Mixed} s The value being converted
8148      * @return {Mixed} The comparison value
8149      */
8150     none : function(s){
8151         return s;
8152     },
8153     
8154     /**
8155      * The regular expression used to strip tags
8156      * @type {RegExp}
8157      * @property
8158      */
8159     stripTagsRE : /<\/?[^>]+>/gi,
8160     
8161     /**
8162      * Strips all HTML tags to sort on text only
8163      * @param {Mixed} s The value being converted
8164      * @return {String} The comparison value
8165      */
8166     asText : function(s){
8167         return String(s).replace(this.stripTagsRE, "");
8168     },
8169     
8170     /**
8171      * Strips all HTML tags to sort on text only - Case insensitive
8172      * @param {Mixed} s The value being converted
8173      * @return {String} The comparison value
8174      */
8175     asUCText : function(s){
8176         return String(s).toUpperCase().replace(this.stripTagsRE, "");
8177     },
8178     
8179     /**
8180      * Case insensitive string
8181      * @param {Mixed} s The value being converted
8182      * @return {String} The comparison value
8183      */
8184     asUCString : function(s) {
8185         return String(s).toUpperCase();
8186     },
8187     
8188     /**
8189      * Date sorting
8190      * @param {Mixed} s The value being converted
8191      * @return {Number} The comparison value
8192      */
8193     asDate : function(s) {
8194         if(!s){
8195             return 0;
8196         }
8197         if(s instanceof Date){
8198             return s.getTime();
8199         }
8200         return Date.parse(String(s));
8201     },
8202     
8203     /**
8204      * Float sorting
8205      * @param {Mixed} s The value being converted
8206      * @return {Float} The comparison value
8207      */
8208     asFloat : function(s) {
8209         var val = parseFloat(String(s).replace(/,/g, ""));
8210         if(isNaN(val)) val = 0;
8211         return val;
8212     },
8213     
8214     /**
8215      * Integer sorting
8216      * @param {Mixed} s The value being converted
8217      * @return {Number} The comparison value
8218      */
8219     asInt : function(s) {
8220         var val = parseInt(String(s).replace(/,/g, ""));
8221         if(isNaN(val)) val = 0;
8222         return val;
8223     }
8224 };/*
8225  * Based on:
8226  * Ext JS Library 1.1.1
8227  * Copyright(c) 2006-2007, Ext JS, LLC.
8228  *
8229  * Originally Released Under LGPL - original licence link has changed is not relivant.
8230  *
8231  * Fork - LGPL
8232  * <script type="text/javascript">
8233  */
8234
8235 /**
8236 * @class Roo.data.Record
8237  * Instances of this class encapsulate both record <em>definition</em> information, and record
8238  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
8239  * to access Records cached in an {@link Roo.data.Store} object.<br>
8240  * <p>
8241  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
8242  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
8243  * objects.<br>
8244  * <p>
8245  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
8246  * @constructor
8247  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
8248  * {@link #create}. The parameters are the same.
8249  * @param {Array} data An associative Array of data values keyed by the field name.
8250  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
8251  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
8252  * not specified an integer id is generated.
8253  */
8254 Roo.data.Record = function(data, id){
8255     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
8256     this.data = data;
8257 };
8258
8259 /**
8260  * Generate a constructor for a specific record layout.
8261  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
8262  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
8263  * Each field definition object may contain the following properties: <ul>
8264  * <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,
8265  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
8266  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
8267  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
8268  * is being used, then this is a string containing the javascript expression to reference the data relative to 
8269  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
8270  * to the data item relative to the record element. If the mapping expression is the same as the field name,
8271  * this may be omitted.</p></li>
8272  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
8273  * <ul><li>auto (Default, implies no conversion)</li>
8274  * <li>string</li>
8275  * <li>int</li>
8276  * <li>float</li>
8277  * <li>boolean</li>
8278  * <li>date</li></ul></p></li>
8279  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
8280  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
8281  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
8282  * by the Reader into an object that will be stored in the Record. It is passed the
8283  * following parameters:<ul>
8284  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
8285  * </ul></p></li>
8286  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
8287  * </ul>
8288  * <br>usage:<br><pre><code>
8289 var TopicRecord = Roo.data.Record.create(
8290     {name: 'title', mapping: 'topic_title'},
8291     {name: 'author', mapping: 'username'},
8292     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
8293     {name: 'lastPost', mapping: 'post_time', type: 'date'},
8294     {name: 'lastPoster', mapping: 'user2'},
8295     {name: 'excerpt', mapping: 'post_text'}
8296 );
8297
8298 var myNewRecord = new TopicRecord({
8299     title: 'Do my job please',
8300     author: 'noobie',
8301     totalPosts: 1,
8302     lastPost: new Date(),
8303     lastPoster: 'Animal',
8304     excerpt: 'No way dude!'
8305 });
8306 myStore.add(myNewRecord);
8307 </code></pre>
8308  * @method create
8309  * @static
8310  */
8311 Roo.data.Record.create = function(o){
8312     var f = function(){
8313         f.superclass.constructor.apply(this, arguments);
8314     };
8315     Roo.extend(f, Roo.data.Record);
8316     var p = f.prototype;
8317     p.fields = new Roo.util.MixedCollection(false, function(field){
8318         return field.name;
8319     });
8320     for(var i = 0, len = o.length; i < len; i++){
8321         p.fields.add(new Roo.data.Field(o[i]));
8322     }
8323     f.getField = function(name){
8324         return p.fields.get(name);  
8325     };
8326     return f;
8327 };
8328
8329 Roo.data.Record.AUTO_ID = 1000;
8330 Roo.data.Record.EDIT = 'edit';
8331 Roo.data.Record.REJECT = 'reject';
8332 Roo.data.Record.COMMIT = 'commit';
8333
8334 Roo.data.Record.prototype = {
8335     /**
8336      * Readonly flag - true if this record has been modified.
8337      * @type Boolean
8338      */
8339     dirty : false,
8340     editing : false,
8341     error: null,
8342     modified: null,
8343
8344     // private
8345     join : function(store){
8346         this.store = store;
8347     },
8348
8349     /**
8350      * Set the named field to the specified value.
8351      * @param {String} name The name of the field to set.
8352      * @param {Object} value The value to set the field to.
8353      */
8354     set : function(name, value){
8355         if(this.data[name] == value){
8356             return;
8357         }
8358         this.dirty = true;
8359         if(!this.modified){
8360             this.modified = {};
8361         }
8362         if(typeof this.modified[name] == 'undefined'){
8363             this.modified[name] = this.data[name];
8364         }
8365         this.data[name] = value;
8366         if(!this.editing && this.store){
8367             this.store.afterEdit(this);
8368         }       
8369     },
8370
8371     /**
8372      * Get the value of the named field.
8373      * @param {String} name The name of the field to get the value of.
8374      * @return {Object} The value of the field.
8375      */
8376     get : function(name){
8377         return this.data[name]; 
8378     },
8379
8380     // private
8381     beginEdit : function(){
8382         this.editing = true;
8383         this.modified = {}; 
8384     },
8385
8386     // private
8387     cancelEdit : function(){
8388         this.editing = false;
8389         delete this.modified;
8390     },
8391
8392     // private
8393     endEdit : function(){
8394         this.editing = false;
8395         if(this.dirty && this.store){
8396             this.store.afterEdit(this);
8397         }
8398     },
8399
8400     /**
8401      * Usually called by the {@link Roo.data.Store} which owns the Record.
8402      * Rejects all changes made to the Record since either creation, or the last commit operation.
8403      * Modified fields are reverted to their original values.
8404      * <p>
8405      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8406      * of reject operations.
8407      */
8408     reject : function(){
8409         var m = this.modified;
8410         for(var n in m){
8411             if(typeof m[n] != "function"){
8412                 this.data[n] = m[n];
8413             }
8414         }
8415         this.dirty = false;
8416         delete this.modified;
8417         this.editing = false;
8418         if(this.store){
8419             this.store.afterReject(this);
8420         }
8421     },
8422
8423     /**
8424      * Usually called by the {@link Roo.data.Store} which owns the Record.
8425      * Commits all changes made to the Record since either creation, or the last commit operation.
8426      * <p>
8427      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8428      * of commit operations.
8429      */
8430     commit : function(){
8431         this.dirty = false;
8432         delete this.modified;
8433         this.editing = false;
8434         if(this.store){
8435             this.store.afterCommit(this);
8436         }
8437     },
8438
8439     // private
8440     hasError : function(){
8441         return this.error != null;
8442     },
8443
8444     // private
8445     clearError : function(){
8446         this.error = null;
8447     },
8448
8449     /**
8450      * Creates a copy of this record.
8451      * @param {String} id (optional) A new record id if you don't want to use this record's id
8452      * @return {Record}
8453      */
8454     copy : function(newId) {
8455         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
8456     }
8457 };/*
8458  * Based on:
8459  * Ext JS Library 1.1.1
8460  * Copyright(c) 2006-2007, Ext JS, LLC.
8461  *
8462  * Originally Released Under LGPL - original licence link has changed is not relivant.
8463  *
8464  * Fork - LGPL
8465  * <script type="text/javascript">
8466  */
8467
8468
8469
8470 /**
8471  * @class Roo.data.Store
8472  * @extends Roo.util.Observable
8473  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
8474  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
8475  * <p>
8476  * 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
8477  * has no knowledge of the format of the data returned by the Proxy.<br>
8478  * <p>
8479  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
8480  * instances from the data object. These records are cached and made available through accessor functions.
8481  * @constructor
8482  * Creates a new Store.
8483  * @param {Object} config A config object containing the objects needed for the Store to access data,
8484  * and read the data into Records.
8485  */
8486 Roo.data.Store = function(config){
8487     this.data = new Roo.util.MixedCollection(false);
8488     this.data.getKey = function(o){
8489         return o.id;
8490     };
8491     this.baseParams = {};
8492     // private
8493     this.paramNames = {
8494         "start" : "start",
8495         "limit" : "limit",
8496         "sort" : "sort",
8497         "dir" : "dir",
8498         "multisort" : "_multisort"
8499     };
8500
8501     if(config && config.data){
8502         this.inlineData = config.data;
8503         delete config.data;
8504     }
8505
8506     Roo.apply(this, config);
8507     
8508     if(this.reader){ // reader passed
8509         this.reader = Roo.factory(this.reader, Roo.data);
8510         this.reader.xmodule = this.xmodule || false;
8511         if(!this.recordType){
8512             this.recordType = this.reader.recordType;
8513         }
8514         if(this.reader.onMetaChange){
8515             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
8516         }
8517     }
8518
8519     if(this.recordType){
8520         this.fields = this.recordType.prototype.fields;
8521     }
8522     this.modified = [];
8523
8524     this.addEvents({
8525         /**
8526          * @event datachanged
8527          * Fires when the data cache has changed, and a widget which is using this Store
8528          * as a Record cache should refresh its view.
8529          * @param {Store} this
8530          */
8531         datachanged : true,
8532         /**
8533          * @event metachange
8534          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
8535          * @param {Store} this
8536          * @param {Object} meta The JSON metadata
8537          */
8538         metachange : true,
8539         /**
8540          * @event add
8541          * Fires when Records have been added to the Store
8542          * @param {Store} this
8543          * @param {Roo.data.Record[]} records The array of Records added
8544          * @param {Number} index The index at which the record(s) were added
8545          */
8546         add : true,
8547         /**
8548          * @event remove
8549          * Fires when a Record has been removed from the Store
8550          * @param {Store} this
8551          * @param {Roo.data.Record} record The Record that was removed
8552          * @param {Number} index The index at which the record was removed
8553          */
8554         remove : true,
8555         /**
8556          * @event update
8557          * Fires when a Record has been updated
8558          * @param {Store} this
8559          * @param {Roo.data.Record} record The Record that was updated
8560          * @param {String} operation The update operation being performed.  Value may be one of:
8561          * <pre><code>
8562  Roo.data.Record.EDIT
8563  Roo.data.Record.REJECT
8564  Roo.data.Record.COMMIT
8565          * </code></pre>
8566          */
8567         update : true,
8568         /**
8569          * @event clear
8570          * Fires when the data cache has been cleared.
8571          * @param {Store} this
8572          */
8573         clear : true,
8574         /**
8575          * @event beforeload
8576          * Fires before a request is made for a new data object.  If the beforeload handler returns false
8577          * the load action will be canceled.
8578          * @param {Store} this
8579          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8580          */
8581         beforeload : true,
8582         /**
8583          * @event beforeloadadd
8584          * Fires after a new set of Records has been loaded.
8585          * @param {Store} this
8586          * @param {Roo.data.Record[]} records The Records that were loaded
8587          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8588          */
8589         beforeloadadd : true,
8590         /**
8591          * @event load
8592          * Fires after a new set of Records has been loaded, before they are added to the store.
8593          * @param {Store} this
8594          * @param {Roo.data.Record[]} records The Records that were loaded
8595          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8596          * @params {Object} return from reader
8597          */
8598         load : true,
8599         /**
8600          * @event loadexception
8601          * Fires if an exception occurs in the Proxy during loading.
8602          * Called with the signature of the Proxy's "loadexception" event.
8603          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
8604          * 
8605          * @param {Proxy} 
8606          * @param {Object} return from JsonData.reader() - success, totalRecords, records
8607          * @param {Object} load options 
8608          * @param {Object} jsonData from your request (normally this contains the Exception)
8609          */
8610         loadexception : true
8611     });
8612     
8613     if(this.proxy){
8614         this.proxy = Roo.factory(this.proxy, Roo.data);
8615         this.proxy.xmodule = this.xmodule || false;
8616         this.relayEvents(this.proxy,  ["loadexception"]);
8617     }
8618     this.sortToggle = {};
8619     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
8620
8621     Roo.data.Store.superclass.constructor.call(this);
8622
8623     if(this.inlineData){
8624         this.loadData(this.inlineData);
8625         delete this.inlineData;
8626     }
8627 };
8628
8629 Roo.extend(Roo.data.Store, Roo.util.Observable, {
8630      /**
8631     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
8632     * without a remote query - used by combo/forms at present.
8633     */
8634     
8635     /**
8636     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
8637     */
8638     /**
8639     * @cfg {Array} data Inline data to be loaded when the store is initialized.
8640     */
8641     /**
8642     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
8643     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
8644     */
8645     /**
8646     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
8647     * on any HTTP request
8648     */
8649     /**
8650     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
8651     */
8652     /**
8653     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
8654     */
8655     multiSort: false,
8656     /**
8657     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
8658     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
8659     */
8660     remoteSort : false,
8661
8662     /**
8663     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
8664      * loaded or when a record is removed. (defaults to false).
8665     */
8666     pruneModifiedRecords : false,
8667
8668     // private
8669     lastOptions : null,
8670
8671     /**
8672      * Add Records to the Store and fires the add event.
8673      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8674      */
8675     add : function(records){
8676         records = [].concat(records);
8677         for(var i = 0, len = records.length; i < len; i++){
8678             records[i].join(this);
8679         }
8680         var index = this.data.length;
8681         this.data.addAll(records);
8682         this.fireEvent("add", this, records, index);
8683     },
8684
8685     /**
8686      * Remove a Record from the Store and fires the remove event.
8687      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
8688      */
8689     remove : function(record){
8690         var index = this.data.indexOf(record);
8691         this.data.removeAt(index);
8692         if(this.pruneModifiedRecords){
8693             this.modified.remove(record);
8694         }
8695         this.fireEvent("remove", this, record, index);
8696     },
8697
8698     /**
8699      * Remove all Records from the Store and fires the clear event.
8700      */
8701     removeAll : function(){
8702         this.data.clear();
8703         if(this.pruneModifiedRecords){
8704             this.modified = [];
8705         }
8706         this.fireEvent("clear", this);
8707     },
8708
8709     /**
8710      * Inserts Records to the Store at the given index and fires the add event.
8711      * @param {Number} index The start index at which to insert the passed Records.
8712      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8713      */
8714     insert : function(index, records){
8715         records = [].concat(records);
8716         for(var i = 0, len = records.length; i < len; i++){
8717             this.data.insert(index, records[i]);
8718             records[i].join(this);
8719         }
8720         this.fireEvent("add", this, records, index);
8721     },
8722
8723     /**
8724      * Get the index within the cache of the passed Record.
8725      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
8726      * @return {Number} The index of the passed Record. Returns -1 if not found.
8727      */
8728     indexOf : function(record){
8729         return this.data.indexOf(record);
8730     },
8731
8732     /**
8733      * Get the index within the cache of the Record with the passed id.
8734      * @param {String} id The id of the Record to find.
8735      * @return {Number} The index of the Record. Returns -1 if not found.
8736      */
8737     indexOfId : function(id){
8738         return this.data.indexOfKey(id);
8739     },
8740
8741     /**
8742      * Get the Record with the specified id.
8743      * @param {String} id The id of the Record to find.
8744      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
8745      */
8746     getById : function(id){
8747         return this.data.key(id);
8748     },
8749
8750     /**
8751      * Get the Record at the specified index.
8752      * @param {Number} index The index of the Record to find.
8753      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
8754      */
8755     getAt : function(index){
8756         return this.data.itemAt(index);
8757     },
8758
8759     /**
8760      * Returns a range of Records between specified indices.
8761      * @param {Number} startIndex (optional) The starting index (defaults to 0)
8762      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
8763      * @return {Roo.data.Record[]} An array of Records
8764      */
8765     getRange : function(start, end){
8766         return this.data.getRange(start, end);
8767     },
8768
8769     // private
8770     storeOptions : function(o){
8771         o = Roo.apply({}, o);
8772         delete o.callback;
8773         delete o.scope;
8774         this.lastOptions = o;
8775     },
8776
8777     /**
8778      * Loads the Record cache from the configured Proxy using the configured Reader.
8779      * <p>
8780      * If using remote paging, then the first load call must specify the <em>start</em>
8781      * and <em>limit</em> properties in the options.params property to establish the initial
8782      * position within the dataset, and the number of Records to cache on each read from the Proxy.
8783      * <p>
8784      * <strong>It is important to note that for remote data sources, loading is asynchronous,
8785      * and this call will return before the new data has been loaded. Perform any post-processing
8786      * in a callback function, or in a "load" event handler.</strong>
8787      * <p>
8788      * @param {Object} options An object containing properties which control loading options:<ul>
8789      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
8790      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
8791      * passed the following arguments:<ul>
8792      * <li>r : Roo.data.Record[]</li>
8793      * <li>options: Options object from the load call</li>
8794      * <li>success: Boolean success indicator</li></ul></li>
8795      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
8796      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
8797      * </ul>
8798      */
8799     load : function(options){
8800         options = options || {};
8801         if(this.fireEvent("beforeload", this, options) !== false){
8802             this.storeOptions(options);
8803             var p = Roo.apply(options.params || {}, this.baseParams);
8804             // if meta was not loaded from remote source.. try requesting it.
8805             if (!this.reader.metaFromRemote) {
8806                 p._requestMeta = 1;
8807             }
8808             if(this.sortInfo && this.remoteSort){
8809                 var pn = this.paramNames;
8810                 p[pn["sort"]] = this.sortInfo.field;
8811                 p[pn["dir"]] = this.sortInfo.direction;
8812             }
8813             if (this.multiSort) {
8814                 var pn = this.paramNames;
8815                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
8816             }
8817             
8818             this.proxy.load(p, this.reader, this.loadRecords, this, options);
8819         }
8820     },
8821
8822     /**
8823      * Reloads the Record cache from the configured Proxy using the configured Reader and
8824      * the options from the last load operation performed.
8825      * @param {Object} options (optional) An object containing properties which may override the options
8826      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
8827      * the most recently used options are reused).
8828      */
8829     reload : function(options){
8830         this.load(Roo.applyIf(options||{}, this.lastOptions));
8831     },
8832
8833     // private
8834     // Called as a callback by the Reader during a load operation.
8835     loadRecords : function(o, options, success){
8836         if(!o || success === false){
8837             if(success !== false){
8838                 this.fireEvent("load", this, [], options, o);
8839             }
8840             if(options.callback){
8841                 options.callback.call(options.scope || this, [], options, false);
8842             }
8843             return;
8844         }
8845         // if data returned failure - throw an exception.
8846         if (o.success === false) {
8847             // show a message if no listener is registered.
8848             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
8849                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
8850             }
8851             // loadmask wil be hooked into this..
8852             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
8853             return;
8854         }
8855         var r = o.records, t = o.totalRecords || r.length;
8856         
8857         this.fireEvent("beforeloadadd", this, r, options, o);
8858         
8859         if(!options || options.add !== true){
8860             if(this.pruneModifiedRecords){
8861                 this.modified = [];
8862             }
8863             for(var i = 0, len = r.length; i < len; i++){
8864                 r[i].join(this);
8865             }
8866             if(this.snapshot){
8867                 this.data = this.snapshot;
8868                 delete this.snapshot;
8869             }
8870             this.data.clear();
8871             this.data.addAll(r);
8872             this.totalLength = t;
8873             this.applySort();
8874             this.fireEvent("datachanged", this);
8875         }else{
8876             this.totalLength = Math.max(t, this.data.length+r.length);
8877             this.add(r);
8878         }
8879         this.fireEvent("load", this, r, options, o);
8880         if(options.callback){
8881             options.callback.call(options.scope || this, r, options, true);
8882         }
8883     },
8884
8885
8886     /**
8887      * Loads data from a passed data block. A Reader which understands the format of the data
8888      * must have been configured in the constructor.
8889      * @param {Object} data The data block from which to read the Records.  The format of the data expected
8890      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
8891      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
8892      */
8893     loadData : function(o, append){
8894         var r = this.reader.readRecords(o);
8895         this.loadRecords(r, {add: append}, true);
8896     },
8897
8898     /**
8899      * Gets the number of cached records.
8900      * <p>
8901      * <em>If using paging, this may not be the total size of the dataset. If the data object
8902      * used by the Reader contains the dataset size, then the getTotalCount() function returns
8903      * the data set size</em>
8904      */
8905     getCount : function(){
8906         return this.data.length || 0;
8907     },
8908
8909     /**
8910      * Gets the total number of records in the dataset as returned by the server.
8911      * <p>
8912      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
8913      * the dataset size</em>
8914      */
8915     getTotalCount : function(){
8916         return this.totalLength || 0;
8917     },
8918
8919     /**
8920      * Returns the sort state of the Store as an object with two properties:
8921      * <pre><code>
8922  field {String} The name of the field by which the Records are sorted
8923  direction {String} The sort order, "ASC" or "DESC"
8924      * </code></pre>
8925      */
8926     getSortState : function(){
8927         return this.sortInfo;
8928     },
8929
8930     // private
8931     applySort : function(){
8932         if(this.sortInfo && !this.remoteSort){
8933             var s = this.sortInfo, f = s.field;
8934             var st = this.fields.get(f).sortType;
8935             var fn = function(r1, r2){
8936                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
8937                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
8938             };
8939             this.data.sort(s.direction, fn);
8940             if(this.snapshot && this.snapshot != this.data){
8941                 this.snapshot.sort(s.direction, fn);
8942             }
8943         }
8944     },
8945
8946     /**
8947      * Sets the default sort column and order to be used by the next load operation.
8948      * @param {String} fieldName The name of the field to sort by.
8949      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
8950      */
8951     setDefaultSort : function(field, dir){
8952         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
8953     },
8954
8955     /**
8956      * Sort the Records.
8957      * If remote sorting is used, the sort is performed on the server, and the cache is
8958      * reloaded. If local sorting is used, the cache is sorted internally.
8959      * @param {String} fieldName The name of the field to sort by.
8960      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
8961      */
8962     sort : function(fieldName, dir){
8963         var f = this.fields.get(fieldName);
8964         if(!dir){
8965             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
8966             
8967             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
8968                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
8969             }else{
8970                 dir = f.sortDir;
8971             }
8972         }
8973         this.sortToggle[f.name] = dir;
8974         this.sortInfo = {field: f.name, direction: dir};
8975         if(!this.remoteSort){
8976             this.applySort();
8977             this.fireEvent("datachanged", this);
8978         }else{
8979             this.load(this.lastOptions);
8980         }
8981     },
8982
8983     /**
8984      * Calls the specified function for each of the Records in the cache.
8985      * @param {Function} fn The function to call. The Record is passed as the first parameter.
8986      * Returning <em>false</em> aborts and exits the iteration.
8987      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
8988      */
8989     each : function(fn, scope){
8990         this.data.each(fn, scope);
8991     },
8992
8993     /**
8994      * Gets all records modified since the last commit.  Modified records are persisted across load operations
8995      * (e.g., during paging).
8996      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
8997      */
8998     getModifiedRecords : function(){
8999         return this.modified;
9000     },
9001
9002     // private
9003     createFilterFn : function(property, value, anyMatch){
9004         if(!value.exec){ // not a regex
9005             value = String(value);
9006             if(value.length == 0){
9007                 return false;
9008             }
9009             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
9010         }
9011         return function(r){
9012             return value.test(r.data[property]);
9013         };
9014     },
9015
9016     /**
9017      * Sums the value of <i>property</i> for each record between start and end and returns the result.
9018      * @param {String} property A field on your records
9019      * @param {Number} start The record index to start at (defaults to 0)
9020      * @param {Number} end The last record index to include (defaults to length - 1)
9021      * @return {Number} The sum
9022      */
9023     sum : function(property, start, end){
9024         var rs = this.data.items, v = 0;
9025         start = start || 0;
9026         end = (end || end === 0) ? end : rs.length-1;
9027
9028         for(var i = start; i <= end; i++){
9029             v += (rs[i].data[property] || 0);
9030         }
9031         return v;
9032     },
9033
9034     /**
9035      * Filter the records by a specified property.
9036      * @param {String} field A field on your records
9037      * @param {String/RegExp} value Either a string that the field
9038      * should start with or a RegExp to test against the field
9039      * @param {Boolean} anyMatch True to match any part not just the beginning
9040      */
9041     filter : function(property, value, anyMatch){
9042         var fn = this.createFilterFn(property, value, anyMatch);
9043         return fn ? this.filterBy(fn) : this.clearFilter();
9044     },
9045
9046     /**
9047      * Filter by a function. The specified function will be called with each
9048      * record in this data source. If the function returns true the record is included,
9049      * otherwise it is filtered.
9050      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9051      * @param {Object} scope (optional) The scope of the function (defaults to this)
9052      */
9053     filterBy : function(fn, scope){
9054         this.snapshot = this.snapshot || this.data;
9055         this.data = this.queryBy(fn, scope||this);
9056         this.fireEvent("datachanged", this);
9057     },
9058
9059     /**
9060      * Query the records by a specified property.
9061      * @param {String} field A field on your records
9062      * @param {String/RegExp} value Either a string that the field
9063      * should start with or a RegExp to test against the field
9064      * @param {Boolean} anyMatch True to match any part not just the beginning
9065      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9066      */
9067     query : function(property, value, anyMatch){
9068         var fn = this.createFilterFn(property, value, anyMatch);
9069         return fn ? this.queryBy(fn) : this.data.clone();
9070     },
9071
9072     /**
9073      * Query by a function. The specified function will be called with each
9074      * record in this data source. If the function returns true the record is included
9075      * in the results.
9076      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9077      * @param {Object} scope (optional) The scope of the function (defaults to this)
9078       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9079      **/
9080     queryBy : function(fn, scope){
9081         var data = this.snapshot || this.data;
9082         return data.filterBy(fn, scope||this);
9083     },
9084
9085     /**
9086      * Collects unique values for a particular dataIndex from this store.
9087      * @param {String} dataIndex The property to collect
9088      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
9089      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
9090      * @return {Array} An array of the unique values
9091      **/
9092     collect : function(dataIndex, allowNull, bypassFilter){
9093         var d = (bypassFilter === true && this.snapshot) ?
9094                 this.snapshot.items : this.data.items;
9095         var v, sv, r = [], l = {};
9096         for(var i = 0, len = d.length; i < len; i++){
9097             v = d[i].data[dataIndex];
9098             sv = String(v);
9099             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
9100                 l[sv] = true;
9101                 r[r.length] = v;
9102             }
9103         }
9104         return r;
9105     },
9106
9107     /**
9108      * Revert to a view of the Record cache with no filtering applied.
9109      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
9110      */
9111     clearFilter : function(suppressEvent){
9112         if(this.snapshot && this.snapshot != this.data){
9113             this.data = this.snapshot;
9114             delete this.snapshot;
9115             if(suppressEvent !== true){
9116                 this.fireEvent("datachanged", this);
9117             }
9118         }
9119     },
9120
9121     // private
9122     afterEdit : function(record){
9123         if(this.modified.indexOf(record) == -1){
9124             this.modified.push(record);
9125         }
9126         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
9127     },
9128     
9129     // private
9130     afterReject : function(record){
9131         this.modified.remove(record);
9132         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
9133     },
9134
9135     // private
9136     afterCommit : function(record){
9137         this.modified.remove(record);
9138         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
9139     },
9140
9141     /**
9142      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
9143      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
9144      */
9145     commitChanges : function(){
9146         var m = this.modified.slice(0);
9147         this.modified = [];
9148         for(var i = 0, len = m.length; i < len; i++){
9149             m[i].commit();
9150         }
9151     },
9152
9153     /**
9154      * Cancel outstanding changes on all changed records.
9155      */
9156     rejectChanges : function(){
9157         var m = this.modified.slice(0);
9158         this.modified = [];
9159         for(var i = 0, len = m.length; i < len; i++){
9160             m[i].reject();
9161         }
9162     },
9163
9164     onMetaChange : function(meta, rtype, o){
9165         this.recordType = rtype;
9166         this.fields = rtype.prototype.fields;
9167         delete this.snapshot;
9168         this.sortInfo = meta.sortInfo || this.sortInfo;
9169         this.modified = [];
9170         this.fireEvent('metachange', this, this.reader.meta);
9171     },
9172     
9173     moveIndex : function(data, type)
9174     {
9175         var index = this.indexOf(data);
9176         
9177         var newIndex = index + type;
9178         
9179         this.remove(data);
9180         
9181         this.insert(newIndex, data);
9182         
9183     }
9184 });/*
9185  * Based on:
9186  * Ext JS Library 1.1.1
9187  * Copyright(c) 2006-2007, Ext JS, LLC.
9188  *
9189  * Originally Released Under LGPL - original licence link has changed is not relivant.
9190  *
9191  * Fork - LGPL
9192  * <script type="text/javascript">
9193  */
9194
9195 /**
9196  * @class Roo.data.SimpleStore
9197  * @extends Roo.data.Store
9198  * Small helper class to make creating Stores from Array data easier.
9199  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
9200  * @cfg {Array} fields An array of field definition objects, or field name strings.
9201  * @cfg {Array} data The multi-dimensional array of data
9202  * @constructor
9203  * @param {Object} config
9204  */
9205 Roo.data.SimpleStore = function(config){
9206     Roo.data.SimpleStore.superclass.constructor.call(this, {
9207         isLocal : true,
9208         reader: new Roo.data.ArrayReader({
9209                 id: config.id
9210             },
9211             Roo.data.Record.create(config.fields)
9212         ),
9213         proxy : new Roo.data.MemoryProxy(config.data)
9214     });
9215     this.load();
9216 };
9217 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
9218  * Based on:
9219  * Ext JS Library 1.1.1
9220  * Copyright(c) 2006-2007, Ext JS, LLC.
9221  *
9222  * Originally Released Under LGPL - original licence link has changed is not relivant.
9223  *
9224  * Fork - LGPL
9225  * <script type="text/javascript">
9226  */
9227
9228 /**
9229 /**
9230  * @extends Roo.data.Store
9231  * @class Roo.data.JsonStore
9232  * Small helper class to make creating Stores for JSON data easier. <br/>
9233 <pre><code>
9234 var store = new Roo.data.JsonStore({
9235     url: 'get-images.php',
9236     root: 'images',
9237     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
9238 });
9239 </code></pre>
9240  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
9241  * JsonReader and HttpProxy (unless inline data is provided).</b>
9242  * @cfg {Array} fields An array of field definition objects, or field name strings.
9243  * @constructor
9244  * @param {Object} config
9245  */
9246 Roo.data.JsonStore = function(c){
9247     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
9248         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
9249         reader: new Roo.data.JsonReader(c, c.fields)
9250     }));
9251 };
9252 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
9253  * Based on:
9254  * Ext JS Library 1.1.1
9255  * Copyright(c) 2006-2007, Ext JS, LLC.
9256  *
9257  * Originally Released Under LGPL - original licence link has changed is not relivant.
9258  *
9259  * Fork - LGPL
9260  * <script type="text/javascript">
9261  */
9262
9263  
9264 Roo.data.Field = function(config){
9265     if(typeof config == "string"){
9266         config = {name: config};
9267     }
9268     Roo.apply(this, config);
9269     
9270     if(!this.type){
9271         this.type = "auto";
9272     }
9273     
9274     var st = Roo.data.SortTypes;
9275     // named sortTypes are supported, here we look them up
9276     if(typeof this.sortType == "string"){
9277         this.sortType = st[this.sortType];
9278     }
9279     
9280     // set default sortType for strings and dates
9281     if(!this.sortType){
9282         switch(this.type){
9283             case "string":
9284                 this.sortType = st.asUCString;
9285                 break;
9286             case "date":
9287                 this.sortType = st.asDate;
9288                 break;
9289             default:
9290                 this.sortType = st.none;
9291         }
9292     }
9293
9294     // define once
9295     var stripRe = /[\$,%]/g;
9296
9297     // prebuilt conversion function for this field, instead of
9298     // switching every time we're reading a value
9299     if(!this.convert){
9300         var cv, dateFormat = this.dateFormat;
9301         switch(this.type){
9302             case "":
9303             case "auto":
9304             case undefined:
9305                 cv = function(v){ return v; };
9306                 break;
9307             case "string":
9308                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
9309                 break;
9310             case "int":
9311                 cv = function(v){
9312                     return v !== undefined && v !== null && v !== '' ?
9313                            parseInt(String(v).replace(stripRe, ""), 10) : '';
9314                     };
9315                 break;
9316             case "float":
9317                 cv = function(v){
9318                     return v !== undefined && v !== null && v !== '' ?
9319                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
9320                     };
9321                 break;
9322             case "bool":
9323             case "boolean":
9324                 cv = function(v){ return v === true || v === "true" || v == 1; };
9325                 break;
9326             case "date":
9327                 cv = function(v){
9328                     if(!v){
9329                         return '';
9330                     }
9331                     if(v instanceof Date){
9332                         return v;
9333                     }
9334                     if(dateFormat){
9335                         if(dateFormat == "timestamp"){
9336                             return new Date(v*1000);
9337                         }
9338                         return Date.parseDate(v, dateFormat);
9339                     }
9340                     var parsed = Date.parse(v);
9341                     return parsed ? new Date(parsed) : null;
9342                 };
9343              break;
9344             
9345         }
9346         this.convert = cv;
9347     }
9348 };
9349
9350 Roo.data.Field.prototype = {
9351     dateFormat: null,
9352     defaultValue: "",
9353     mapping: null,
9354     sortType : null,
9355     sortDir : "ASC"
9356 };/*
9357  * Based on:
9358  * Ext JS Library 1.1.1
9359  * Copyright(c) 2006-2007, Ext JS, LLC.
9360  *
9361  * Originally Released Under LGPL - original licence link has changed is not relivant.
9362  *
9363  * Fork - LGPL
9364  * <script type="text/javascript">
9365  */
9366  
9367 // Base class for reading structured data from a data source.  This class is intended to be
9368 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
9369
9370 /**
9371  * @class Roo.data.DataReader
9372  * Base class for reading structured data from a data source.  This class is intended to be
9373  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
9374  */
9375
9376 Roo.data.DataReader = function(meta, recordType){
9377     
9378     this.meta = meta;
9379     
9380     this.recordType = recordType instanceof Array ? 
9381         Roo.data.Record.create(recordType) : recordType;
9382 };
9383
9384 Roo.data.DataReader.prototype = {
9385      /**
9386      * Create an empty record
9387      * @param {Object} data (optional) - overlay some values
9388      * @return {Roo.data.Record} record created.
9389      */
9390     newRow :  function(d) {
9391         var da =  {};
9392         this.recordType.prototype.fields.each(function(c) {
9393             switch( c.type) {
9394                 case 'int' : da[c.name] = 0; break;
9395                 case 'date' : da[c.name] = new Date(); break;
9396                 case 'float' : da[c.name] = 0.0; break;
9397                 case 'boolean' : da[c.name] = false; break;
9398                 default : da[c.name] = ""; break;
9399             }
9400             
9401         });
9402         return new this.recordType(Roo.apply(da, d));
9403     }
9404     
9405 };/*
9406  * Based on:
9407  * Ext JS Library 1.1.1
9408  * Copyright(c) 2006-2007, Ext JS, LLC.
9409  *
9410  * Originally Released Under LGPL - original licence link has changed is not relivant.
9411  *
9412  * Fork - LGPL
9413  * <script type="text/javascript">
9414  */
9415
9416 /**
9417  * @class Roo.data.DataProxy
9418  * @extends Roo.data.Observable
9419  * This class is an abstract base class for implementations which provide retrieval of
9420  * unformatted data objects.<br>
9421  * <p>
9422  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
9423  * (of the appropriate type which knows how to parse the data object) to provide a block of
9424  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
9425  * <p>
9426  * Custom implementations must implement the load method as described in
9427  * {@link Roo.data.HttpProxy#load}.
9428  */
9429 Roo.data.DataProxy = function(){
9430     this.addEvents({
9431         /**
9432          * @event beforeload
9433          * Fires before a network request is made to retrieve a data object.
9434          * @param {Object} This DataProxy object.
9435          * @param {Object} params The params parameter to the load function.
9436          */
9437         beforeload : true,
9438         /**
9439          * @event load
9440          * Fires before the load method's callback is called.
9441          * @param {Object} This DataProxy object.
9442          * @param {Object} o The data object.
9443          * @param {Object} arg The callback argument object passed to the load function.
9444          */
9445         load : true,
9446         /**
9447          * @event loadexception
9448          * Fires if an Exception occurs during data retrieval.
9449          * @param {Object} This DataProxy object.
9450          * @param {Object} o The data object.
9451          * @param {Object} arg The callback argument object passed to the load function.
9452          * @param {Object} e The Exception.
9453          */
9454         loadexception : true
9455     });
9456     Roo.data.DataProxy.superclass.constructor.call(this);
9457 };
9458
9459 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
9460
9461     /**
9462      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
9463      */
9464 /*
9465  * Based on:
9466  * Ext JS Library 1.1.1
9467  * Copyright(c) 2006-2007, Ext JS, LLC.
9468  *
9469  * Originally Released Under LGPL - original licence link has changed is not relivant.
9470  *
9471  * Fork - LGPL
9472  * <script type="text/javascript">
9473  */
9474 /**
9475  * @class Roo.data.MemoryProxy
9476  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
9477  * to the Reader when its load method is called.
9478  * @constructor
9479  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
9480  */
9481 Roo.data.MemoryProxy = function(data){
9482     if (data.data) {
9483         data = data.data;
9484     }
9485     Roo.data.MemoryProxy.superclass.constructor.call(this);
9486     this.data = data;
9487 };
9488
9489 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
9490     /**
9491      * Load data from the requested source (in this case an in-memory
9492      * data object passed to the constructor), read the data object into
9493      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9494      * process that block using the passed callback.
9495      * @param {Object} params This parameter is not used by the MemoryProxy class.
9496      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9497      * object into a block of Roo.data.Records.
9498      * @param {Function} callback The function into which to pass the block of Roo.data.records.
9499      * The function must be passed <ul>
9500      * <li>The Record block object</li>
9501      * <li>The "arg" argument from the load function</li>
9502      * <li>A boolean success indicator</li>
9503      * </ul>
9504      * @param {Object} scope The scope in which to call the callback
9505      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9506      */
9507     load : function(params, reader, callback, scope, arg){
9508         params = params || {};
9509         var result;
9510         try {
9511             result = reader.readRecords(this.data);
9512         }catch(e){
9513             this.fireEvent("loadexception", this, arg, null, e);
9514             callback.call(scope, null, arg, false);
9515             return;
9516         }
9517         callback.call(scope, result, arg, true);
9518     },
9519     
9520     // private
9521     update : function(params, records){
9522         
9523     }
9524 });/*
9525  * Based on:
9526  * Ext JS Library 1.1.1
9527  * Copyright(c) 2006-2007, Ext JS, LLC.
9528  *
9529  * Originally Released Under LGPL - original licence link has changed is not relivant.
9530  *
9531  * Fork - LGPL
9532  * <script type="text/javascript">
9533  */
9534 /**
9535  * @class Roo.data.HttpProxy
9536  * @extends Roo.data.DataProxy
9537  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
9538  * configured to reference a certain URL.<br><br>
9539  * <p>
9540  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
9541  * from which the running page was served.<br><br>
9542  * <p>
9543  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
9544  * <p>
9545  * Be aware that to enable the browser to parse an XML document, the server must set
9546  * the Content-Type header in the HTTP response to "text/xml".
9547  * @constructor
9548  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
9549  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
9550  * will be used to make the request.
9551  */
9552 Roo.data.HttpProxy = function(conn){
9553     Roo.data.HttpProxy.superclass.constructor.call(this);
9554     // is conn a conn config or a real conn?
9555     this.conn = conn;
9556     this.useAjax = !conn || !conn.events;
9557   
9558 };
9559
9560 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
9561     // thse are take from connection...
9562     
9563     /**
9564      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
9565      */
9566     /**
9567      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
9568      * extra parameters to each request made by this object. (defaults to undefined)
9569      */
9570     /**
9571      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
9572      *  to each request made by this object. (defaults to undefined)
9573      */
9574     /**
9575      * @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)
9576      */
9577     /**
9578      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
9579      */
9580      /**
9581      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
9582      * @type Boolean
9583      */
9584   
9585
9586     /**
9587      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
9588      * @type Boolean
9589      */
9590     /**
9591      * Return the {@link Roo.data.Connection} object being used by this Proxy.
9592      * @return {Connection} The Connection object. This object may be used to subscribe to events on
9593      * a finer-grained basis than the DataProxy events.
9594      */
9595     getConnection : function(){
9596         return this.useAjax ? Roo.Ajax : this.conn;
9597     },
9598
9599     /**
9600      * Load data from the configured {@link Roo.data.Connection}, read the data object into
9601      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
9602      * process that block using the passed callback.
9603      * @param {Object} params An object containing properties which are to be used as HTTP parameters
9604      * for the request to the remote server.
9605      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9606      * object into a block of Roo.data.Records.
9607      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9608      * The function must be passed <ul>
9609      * <li>The Record block object</li>
9610      * <li>The "arg" argument from the load function</li>
9611      * <li>A boolean success indicator</li>
9612      * </ul>
9613      * @param {Object} scope The scope in which to call the callback
9614      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9615      */
9616     load : function(params, reader, callback, scope, arg){
9617         if(this.fireEvent("beforeload", this, params) !== false){
9618             var  o = {
9619                 params : params || {},
9620                 request: {
9621                     callback : callback,
9622                     scope : scope,
9623                     arg : arg
9624                 },
9625                 reader: reader,
9626                 callback : this.loadResponse,
9627                 scope: this
9628             };
9629             if(this.useAjax){
9630                 Roo.applyIf(o, this.conn);
9631                 if(this.activeRequest){
9632                     Roo.Ajax.abort(this.activeRequest);
9633                 }
9634                 this.activeRequest = Roo.Ajax.request(o);
9635             }else{
9636                 this.conn.request(o);
9637             }
9638         }else{
9639             callback.call(scope||this, null, arg, false);
9640         }
9641     },
9642
9643     // private
9644     loadResponse : function(o, success, response){
9645         delete this.activeRequest;
9646         if(!success){
9647             this.fireEvent("loadexception", this, o, response);
9648             o.request.callback.call(o.request.scope, null, o.request.arg, false);
9649             return;
9650         }
9651         var result;
9652         try {
9653             result = o.reader.read(response);
9654         }catch(e){
9655             this.fireEvent("loadexception", this, o, response, e);
9656             o.request.callback.call(o.request.scope, null, o.request.arg, false);
9657             return;
9658         }
9659         
9660         this.fireEvent("load", this, o, o.request.arg);
9661         o.request.callback.call(o.request.scope, result, o.request.arg, true);
9662     },
9663
9664     // private
9665     update : function(dataSet){
9666
9667     },
9668
9669     // private
9670     updateResponse : function(dataSet){
9671
9672     }
9673 });/*
9674  * Based on:
9675  * Ext JS Library 1.1.1
9676  * Copyright(c) 2006-2007, Ext JS, LLC.
9677  *
9678  * Originally Released Under LGPL - original licence link has changed is not relivant.
9679  *
9680  * Fork - LGPL
9681  * <script type="text/javascript">
9682  */
9683
9684 /**
9685  * @class Roo.data.ScriptTagProxy
9686  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
9687  * other than the originating domain of the running page.<br><br>
9688  * <p>
9689  * <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
9690  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
9691  * <p>
9692  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
9693  * source code that is used as the source inside a &lt;script> tag.<br><br>
9694  * <p>
9695  * In order for the browser to process the returned data, the server must wrap the data object
9696  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
9697  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
9698  * depending on whether the callback name was passed:
9699  * <p>
9700  * <pre><code>
9701 boolean scriptTag = false;
9702 String cb = request.getParameter("callback");
9703 if (cb != null) {
9704     scriptTag = true;
9705     response.setContentType("text/javascript");
9706 } else {
9707     response.setContentType("application/x-json");
9708 }
9709 Writer out = response.getWriter();
9710 if (scriptTag) {
9711     out.write(cb + "(");
9712 }
9713 out.print(dataBlock.toJsonString());
9714 if (scriptTag) {
9715     out.write(");");
9716 }
9717 </pre></code>
9718  *
9719  * @constructor
9720  * @param {Object} config A configuration object.
9721  */
9722 Roo.data.ScriptTagProxy = function(config){
9723     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
9724     Roo.apply(this, config);
9725     this.head = document.getElementsByTagName("head")[0];
9726 };
9727
9728 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
9729
9730 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
9731     /**
9732      * @cfg {String} url The URL from which to request the data object.
9733      */
9734     /**
9735      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
9736      */
9737     timeout : 30000,
9738     /**
9739      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
9740      * the server the name of the callback function set up by the load call to process the returned data object.
9741      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
9742      * javascript output which calls this named function passing the data object as its only parameter.
9743      */
9744     callbackParam : "callback",
9745     /**
9746      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
9747      * name to the request.
9748      */
9749     nocache : true,
9750
9751     /**
9752      * Load data from the configured URL, read the data object into
9753      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9754      * process that block using the passed callback.
9755      * @param {Object} params An object containing properties which are to be used as HTTP parameters
9756      * for the request to the remote server.
9757      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9758      * object into a block of Roo.data.Records.
9759      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9760      * The function must be passed <ul>
9761      * <li>The Record block object</li>
9762      * <li>The "arg" argument from the load function</li>
9763      * <li>A boolean success indicator</li>
9764      * </ul>
9765      * @param {Object} scope The scope in which to call the callback
9766      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9767      */
9768     load : function(params, reader, callback, scope, arg){
9769         if(this.fireEvent("beforeload", this, params) !== false){
9770
9771             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
9772
9773             var url = this.url;
9774             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
9775             if(this.nocache){
9776                 url += "&_dc=" + (new Date().getTime());
9777             }
9778             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
9779             var trans = {
9780                 id : transId,
9781                 cb : "stcCallback"+transId,
9782                 scriptId : "stcScript"+transId,
9783                 params : params,
9784                 arg : arg,
9785                 url : url,
9786                 callback : callback,
9787                 scope : scope,
9788                 reader : reader
9789             };
9790             var conn = this;
9791
9792             window[trans.cb] = function(o){
9793                 conn.handleResponse(o, trans);
9794             };
9795
9796             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
9797
9798             if(this.autoAbort !== false){
9799                 this.abort();
9800             }
9801
9802             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
9803
9804             var script = document.createElement("script");
9805             script.setAttribute("src", url);
9806             script.setAttribute("type", "text/javascript");
9807             script.setAttribute("id", trans.scriptId);
9808             this.head.appendChild(script);
9809
9810             this.trans = trans;
9811         }else{
9812             callback.call(scope||this, null, arg, false);
9813         }
9814     },
9815
9816     // private
9817     isLoading : function(){
9818         return this.trans ? true : false;
9819     },
9820
9821     /**
9822      * Abort the current server request.
9823      */
9824     abort : function(){
9825         if(this.isLoading()){
9826             this.destroyTrans(this.trans);
9827         }
9828     },
9829
9830     // private
9831     destroyTrans : function(trans, isLoaded){
9832         this.head.removeChild(document.getElementById(trans.scriptId));
9833         clearTimeout(trans.timeoutId);
9834         if(isLoaded){
9835             window[trans.cb] = undefined;
9836             try{
9837                 delete window[trans.cb];
9838             }catch(e){}
9839         }else{
9840             // if hasn't been loaded, wait for load to remove it to prevent script error
9841             window[trans.cb] = function(){
9842                 window[trans.cb] = undefined;
9843                 try{
9844                     delete window[trans.cb];
9845                 }catch(e){}
9846             };
9847         }
9848     },
9849
9850     // private
9851     handleResponse : function(o, trans){
9852         this.trans = false;
9853         this.destroyTrans(trans, true);
9854         var result;
9855         try {
9856             result = trans.reader.readRecords(o);
9857         }catch(e){
9858             this.fireEvent("loadexception", this, o, trans.arg, e);
9859             trans.callback.call(trans.scope||window, null, trans.arg, false);
9860             return;
9861         }
9862         this.fireEvent("load", this, o, trans.arg);
9863         trans.callback.call(trans.scope||window, result, trans.arg, true);
9864     },
9865
9866     // private
9867     handleFailure : function(trans){
9868         this.trans = false;
9869         this.destroyTrans(trans, false);
9870         this.fireEvent("loadexception", this, null, trans.arg);
9871         trans.callback.call(trans.scope||window, null, trans.arg, false);
9872     }
9873 });/*
9874  * Based on:
9875  * Ext JS Library 1.1.1
9876  * Copyright(c) 2006-2007, Ext JS, LLC.
9877  *
9878  * Originally Released Under LGPL - original licence link has changed is not relivant.
9879  *
9880  * Fork - LGPL
9881  * <script type="text/javascript">
9882  */
9883
9884 /**
9885  * @class Roo.data.JsonReader
9886  * @extends Roo.data.DataReader
9887  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
9888  * based on mappings in a provided Roo.data.Record constructor.
9889  * 
9890  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
9891  * in the reply previously. 
9892  * 
9893  * <p>
9894  * Example code:
9895  * <pre><code>
9896 var RecordDef = Roo.data.Record.create([
9897     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
9898     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
9899 ]);
9900 var myReader = new Roo.data.JsonReader({
9901     totalProperty: "results",    // The property which contains the total dataset size (optional)
9902     root: "rows",                // The property which contains an Array of row objects
9903     id: "id"                     // The property within each row object that provides an ID for the record (optional)
9904 }, RecordDef);
9905 </code></pre>
9906  * <p>
9907  * This would consume a JSON file like this:
9908  * <pre><code>
9909 { 'results': 2, 'rows': [
9910     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
9911     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
9912 }
9913 </code></pre>
9914  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
9915  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
9916  * paged from the remote server.
9917  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
9918  * @cfg {String} root name of the property which contains the Array of row objects.
9919  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
9920  * @constructor
9921  * Create a new JsonReader
9922  * @param {Object} meta Metadata configuration options
9923  * @param {Object} recordType Either an Array of field definition objects,
9924  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
9925  */
9926 Roo.data.JsonReader = function(meta, recordType){
9927     
9928     meta = meta || {};
9929     // set some defaults:
9930     Roo.applyIf(meta, {
9931         totalProperty: 'total',
9932         successProperty : 'success',
9933         root : 'data',
9934         id : 'id'
9935     });
9936     
9937     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
9938 };
9939 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
9940     
9941     /**
9942      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
9943      * Used by Store query builder to append _requestMeta to params.
9944      * 
9945      */
9946     metaFromRemote : false,
9947     /**
9948      * This method is only used by a DataProxy which has retrieved data from a remote server.
9949      * @param {Object} response The XHR object which contains the JSON data in its responseText.
9950      * @return {Object} data A data block which is used by an Roo.data.Store object as
9951      * a cache of Roo.data.Records.
9952      */
9953     read : function(response){
9954         var json = response.responseText;
9955        
9956         var o = /* eval:var:o */ eval("("+json+")");
9957         if(!o) {
9958             throw {message: "JsonReader.read: Json object not found"};
9959         }
9960         
9961         if(o.metaData){
9962             
9963             delete this.ef;
9964             this.metaFromRemote = true;
9965             this.meta = o.metaData;
9966             this.recordType = Roo.data.Record.create(o.metaData.fields);
9967             this.onMetaChange(this.meta, this.recordType, o);
9968         }
9969         return this.readRecords(o);
9970     },
9971
9972     // private function a store will implement
9973     onMetaChange : function(meta, recordType, o){
9974
9975     },
9976
9977     /**
9978          * @ignore
9979          */
9980     simpleAccess: function(obj, subsc) {
9981         return obj[subsc];
9982     },
9983
9984         /**
9985          * @ignore
9986          */
9987     getJsonAccessor: function(){
9988         var re = /[\[\.]/;
9989         return function(expr) {
9990             try {
9991                 return(re.test(expr))
9992                     ? new Function("obj", "return obj." + expr)
9993                     : function(obj){
9994                         return obj[expr];
9995                     };
9996             } catch(e){}
9997             return Roo.emptyFn;
9998         };
9999     }(),
10000
10001     /**
10002      * Create a data block containing Roo.data.Records from an XML document.
10003      * @param {Object} o An object which contains an Array of row objects in the property specified
10004      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
10005      * which contains the total size of the dataset.
10006      * @return {Object} data A data block which is used by an Roo.data.Store object as
10007      * a cache of Roo.data.Records.
10008      */
10009     readRecords : function(o){
10010         /**
10011          * After any data loads, the raw JSON data is available for further custom processing.
10012          * @type Object
10013          */
10014         this.o = o;
10015         var s = this.meta, Record = this.recordType,
10016             f = Record.prototype.fields, fi = f.items, fl = f.length;
10017
10018 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
10019         if (!this.ef) {
10020             if(s.totalProperty) {
10021                     this.getTotal = this.getJsonAccessor(s.totalProperty);
10022                 }
10023                 if(s.successProperty) {
10024                     this.getSuccess = this.getJsonAccessor(s.successProperty);
10025                 }
10026                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
10027                 if (s.id) {
10028                         var g = this.getJsonAccessor(s.id);
10029                         this.getId = function(rec) {
10030                                 var r = g(rec);
10031                                 return (r === undefined || r === "") ? null : r;
10032                         };
10033                 } else {
10034                         this.getId = function(){return null;};
10035                 }
10036             this.ef = [];
10037             for(var jj = 0; jj < fl; jj++){
10038                 f = fi[jj];
10039                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
10040                 this.ef[jj] = this.getJsonAccessor(map);
10041             }
10042         }
10043
10044         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
10045         if(s.totalProperty){
10046             var vt = parseInt(this.getTotal(o), 10);
10047             if(!isNaN(vt)){
10048                 totalRecords = vt;
10049             }
10050         }
10051         if(s.successProperty){
10052             var vs = this.getSuccess(o);
10053             if(vs === false || vs === 'false'){
10054                 success = false;
10055             }
10056         }
10057         var records = [];
10058             for(var i = 0; i < c; i++){
10059                     var n = root[i];
10060                 var values = {};
10061                 var id = this.getId(n);
10062                 for(var j = 0; j < fl; j++){
10063                     f = fi[j];
10064                 var v = this.ef[j](n);
10065                 if (!f.convert) {
10066                     Roo.log('missing convert for ' + f.name);
10067                     Roo.log(f);
10068                     continue;
10069                 }
10070                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
10071                 }
10072                 var record = new Record(values, id);
10073                 record.json = n;
10074                 records[i] = record;
10075             }
10076             return {
10077             raw : o,
10078                 success : success,
10079                 records : records,
10080                 totalRecords : totalRecords
10081             };
10082     }
10083 });/*
10084  * Based on:
10085  * Ext JS Library 1.1.1
10086  * Copyright(c) 2006-2007, Ext JS, LLC.
10087  *
10088  * Originally Released Under LGPL - original licence link has changed is not relivant.
10089  *
10090  * Fork - LGPL
10091  * <script type="text/javascript">
10092  */
10093
10094 /**
10095  * @class Roo.data.ArrayReader
10096  * @extends Roo.data.DataReader
10097  * Data reader class to create an Array of Roo.data.Record objects from an Array.
10098  * Each element of that Array represents a row of data fields. The
10099  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
10100  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
10101  * <p>
10102  * Example code:.
10103  * <pre><code>
10104 var RecordDef = Roo.data.Record.create([
10105     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
10106     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
10107 ]);
10108 var myReader = new Roo.data.ArrayReader({
10109     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
10110 }, RecordDef);
10111 </code></pre>
10112  * <p>
10113  * This would consume an Array like this:
10114  * <pre><code>
10115 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
10116   </code></pre>
10117  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
10118  * @constructor
10119  * Create a new JsonReader
10120  * @param {Object} meta Metadata configuration options.
10121  * @param {Object} recordType Either an Array of field definition objects
10122  * as specified to {@link Roo.data.Record#create},
10123  * or an {@link Roo.data.Record} object
10124  * created using {@link Roo.data.Record#create}.
10125  */
10126 Roo.data.ArrayReader = function(meta, recordType){
10127     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
10128 };
10129
10130 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
10131     /**
10132      * Create a data block containing Roo.data.Records from an XML document.
10133      * @param {Object} o An Array of row objects which represents the dataset.
10134      * @return {Object} data A data block which is used by an Roo.data.Store object as
10135      * a cache of Roo.data.Records.
10136      */
10137     readRecords : function(o){
10138         var sid = this.meta ? this.meta.id : null;
10139         var recordType = this.recordType, fields = recordType.prototype.fields;
10140         var records = [];
10141         var root = o;
10142             for(var i = 0; i < root.length; i++){
10143                     var n = root[i];
10144                 var values = {};
10145                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
10146                 for(var j = 0, jlen = fields.length; j < jlen; j++){
10147                 var f = fields.items[j];
10148                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
10149                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
10150                 v = f.convert(v);
10151                 values[f.name] = v;
10152             }
10153                 var record = new recordType(values, id);
10154                 record.json = n;
10155                 records[records.length] = record;
10156             }
10157             return {
10158                 records : records,
10159                 totalRecords : records.length
10160             };
10161     }
10162 });/*
10163  * - LGPL
10164  * * 
10165  */
10166
10167 /**
10168  * @class Roo.bootstrap.ComboBox
10169  * @extends Roo.bootstrap.TriggerField
10170  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
10171  * @cfg {Boolean} append (true|false) default false
10172  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
10173  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
10174  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
10175  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
10176  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
10177  * @constructor
10178  * Create a new ComboBox.
10179  * @param {Object} config Configuration options
10180  */
10181 Roo.bootstrap.ComboBox = function(config){
10182     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
10183     this.addEvents({
10184         /**
10185          * @event expand
10186          * Fires when the dropdown list is expanded
10187              * @param {Roo.bootstrap.ComboBox} combo This combo box
10188              */
10189         'expand' : true,
10190         /**
10191          * @event collapse
10192          * Fires when the dropdown list is collapsed
10193              * @param {Roo.bootstrap.ComboBox} combo This combo box
10194              */
10195         'collapse' : true,
10196         /**
10197          * @event beforeselect
10198          * Fires before a list item is selected. Return false to cancel the selection.
10199              * @param {Roo.bootstrap.ComboBox} combo This combo box
10200              * @param {Roo.data.Record} record The data record returned from the underlying store
10201              * @param {Number} index The index of the selected item in the dropdown list
10202              */
10203         'beforeselect' : true,
10204         /**
10205          * @event select
10206          * Fires when a list item is selected
10207              * @param {Roo.bootstrap.ComboBox} combo This combo box
10208              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
10209              * @param {Number} index The index of the selected item in the dropdown list
10210              */
10211         'select' : true,
10212         /**
10213          * @event beforequery
10214          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
10215          * The event object passed has these properties:
10216              * @param {Roo.bootstrap.ComboBox} combo This combo box
10217              * @param {String} query The query
10218              * @param {Boolean} forceAll true to force "all" query
10219              * @param {Boolean} cancel true to cancel the query
10220              * @param {Object} e The query event object
10221              */
10222         'beforequery': true,
10223          /**
10224          * @event add
10225          * Fires when the 'add' icon is pressed (add a listener to enable add button)
10226              * @param {Roo.bootstrap.ComboBox} combo This combo box
10227              */
10228         'add' : true,
10229         /**
10230          * @event edit
10231          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
10232              * @param {Roo.bootstrap.ComboBox} combo This combo box
10233              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
10234              */
10235         'edit' : true,
10236         /**
10237          * @event remove
10238          * Fires when the remove value from the combobox array
10239              * @param {Roo.bootstrap.ComboBox} combo This combo box
10240              */
10241         'remove' : true
10242         
10243     });
10244     
10245     this.item = [];
10246     this.tickItems = [];
10247     
10248     this.selectedIndex = -1;
10249     if(this.mode == 'local'){
10250         if(config.queryDelay === undefined){
10251             this.queryDelay = 10;
10252         }
10253         if(config.minChars === undefined){
10254             this.minChars = 0;
10255         }
10256     }
10257 };
10258
10259 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
10260      
10261     /**
10262      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
10263      * rendering into an Roo.Editor, defaults to false)
10264      */
10265     /**
10266      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
10267      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
10268      */
10269     /**
10270      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
10271      */
10272     /**
10273      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
10274      * the dropdown list (defaults to undefined, with no header element)
10275      */
10276
10277      /**
10278      * @cfg {String/Roo.Template} tpl The template to use to render the output
10279      */
10280      
10281      /**
10282      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
10283      */
10284     listWidth: undefined,
10285     /**
10286      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
10287      * mode = 'remote' or 'text' if mode = 'local')
10288      */
10289     displayField: undefined,
10290     /**
10291      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
10292      * mode = 'remote' or 'value' if mode = 'local'). 
10293      * Note: use of a valueField requires the user make a selection
10294      * in order for a value to be mapped.
10295      */
10296     valueField: undefined,
10297     
10298     
10299     /**
10300      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
10301      * field's data value (defaults to the underlying DOM element's name)
10302      */
10303     hiddenName: undefined,
10304     /**
10305      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
10306      */
10307     listClass: '',
10308     /**
10309      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
10310      */
10311     selectedClass: 'active',
10312     
10313     /**
10314      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
10315      */
10316     shadow:'sides',
10317     /**
10318      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
10319      * anchor positions (defaults to 'tl-bl')
10320      */
10321     listAlign: 'tl-bl?',
10322     /**
10323      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
10324      */
10325     maxHeight: 300,
10326     /**
10327      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
10328      * query specified by the allQuery config option (defaults to 'query')
10329      */
10330     triggerAction: 'query',
10331     /**
10332      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
10333      * (defaults to 4, does not apply if editable = false)
10334      */
10335     minChars : 4,
10336     /**
10337      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
10338      * delay (typeAheadDelay) if it matches a known value (defaults to false)
10339      */
10340     typeAhead: false,
10341     /**
10342      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
10343      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
10344      */
10345     queryDelay: 500,
10346     /**
10347      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
10348      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
10349      */
10350     pageSize: 0,
10351     /**
10352      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
10353      * when editable = true (defaults to false)
10354      */
10355     selectOnFocus:false,
10356     /**
10357      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
10358      */
10359     queryParam: 'query',
10360     /**
10361      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
10362      * when mode = 'remote' (defaults to 'Loading...')
10363      */
10364     loadingText: 'Loading...',
10365     /**
10366      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
10367      */
10368     resizable: false,
10369     /**
10370      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
10371      */
10372     handleHeight : 8,
10373     /**
10374      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
10375      * traditional select (defaults to true)
10376      */
10377     editable: true,
10378     /**
10379      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
10380      */
10381     allQuery: '',
10382     /**
10383      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
10384      */
10385     mode: 'remote',
10386     /**
10387      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
10388      * listWidth has a higher value)
10389      */
10390     minListWidth : 70,
10391     /**
10392      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
10393      * allow the user to set arbitrary text into the field (defaults to false)
10394      */
10395     forceSelection:false,
10396     /**
10397      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
10398      * if typeAhead = true (defaults to 250)
10399      */
10400     typeAheadDelay : 250,
10401     /**
10402      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
10403      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
10404      */
10405     valueNotFoundText : undefined,
10406     /**
10407      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
10408      */
10409     blockFocus : false,
10410     
10411     /**
10412      * @cfg {Boolean} disableClear Disable showing of clear button.
10413      */
10414     disableClear : false,
10415     /**
10416      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
10417      */
10418     alwaysQuery : false,
10419     
10420     /**
10421      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
10422      */
10423     multiple : false,
10424     
10425     //private
10426     addicon : false,
10427     editicon: false,
10428     
10429     page: 0,
10430     hasQuery: false,
10431     append: false,
10432     loadNext: false,
10433     autoFocus : true,
10434     tickable : false,
10435     btnPosition : 'right',
10436     triggerList : true,
10437     showToggleBtn : true,
10438     // element that contains real text value.. (when hidden is used..)
10439     
10440     getAutoCreate : function()
10441     {
10442         var cfg = false;
10443         
10444         /*
10445          *  Normal ComboBox
10446          */
10447         if(!this.tickable){
10448             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
10449             return cfg;
10450         }
10451         
10452         /*
10453          *  ComboBox with tickable selections
10454          */
10455              
10456         var align = this.labelAlign || this.parentLabelAlign();
10457         
10458         cfg = {
10459             cls : 'form-group roo-combobox-tickable' //input-group
10460         };
10461         
10462         
10463         var buttons = {
10464             tag : 'div',
10465             cls : 'tickable-buttons',
10466             cn : [
10467                 {
10468                     tag : 'button',
10469                     type : 'button',
10470                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
10471                     html : 'Edit'
10472                 },
10473                 {
10474                     tag : 'button',
10475                     type : 'button',
10476                     name : 'ok',
10477                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
10478                     html : 'Done'
10479                 },
10480                 {
10481                     tag : 'button',
10482                     type : 'button',
10483                     name : 'cancel',
10484                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
10485                     html : 'Cancel'
10486                 }
10487             ]
10488         };
10489         
10490         var _this = this;
10491         Roo.each(buttons.cn, function(c){
10492             if (_this.size) {
10493                 c.cls += ' btn-' + _this.size;
10494             }
10495
10496             if (_this.disabled) {
10497                 c.disabled = true;
10498             }
10499         });
10500         
10501         var box = {
10502             tag: 'div',
10503             cn: [
10504                 {
10505                     tag: 'input',
10506                     type : 'hidden',
10507                     cls: 'form-hidden-field'
10508                 },
10509                 {
10510                     tag: 'ul',
10511                     cls: 'select2-choices',
10512                     cn:[
10513                         {
10514                             tag: 'li',
10515                             cls: 'select2-search-field',
10516                             cn: [
10517
10518                                 buttons
10519                             ]
10520                         }
10521                     ]
10522                 }
10523             ]
10524         }
10525         
10526         var combobox = {
10527             cls: 'select2-container input-group select2-container-multi',
10528             cn: [
10529                 box
10530 //                {
10531 //                    tag: 'ul',
10532 //                    cls: 'typeahead typeahead-long dropdown-menu',
10533 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
10534 //                }
10535             ]
10536         };
10537         
10538         if (align ==='left' && this.fieldLabel.length) {
10539             
10540                 Roo.log("left and has label");
10541                 cfg.cn = [
10542                     
10543                     {
10544                         tag: 'label',
10545                         'for' :  id,
10546                         cls : 'control-label col-sm-' + this.labelWidth,
10547                         html : this.fieldLabel
10548                         
10549                     },
10550                     {
10551                         cls : "col-sm-" + (12 - this.labelWidth), 
10552                         cn: [
10553                             combobox
10554                         ]
10555                     }
10556                     
10557                 ];
10558         } else if ( this.fieldLabel.length) {
10559                 Roo.log(" label");
10560                  cfg.cn = [
10561                    
10562                     {
10563                         tag: 'label',
10564                         //cls : 'input-group-addon',
10565                         html : this.fieldLabel
10566                         
10567                     },
10568                     
10569                     combobox
10570                     
10571                 ];
10572
10573         } else {
10574             
10575                 Roo.log(" no label && no align");
10576                 cfg = combobox
10577                      
10578                 
10579         }
10580          
10581         var settings=this;
10582         ['xs','sm','md','lg'].map(function(size){
10583             if (settings[size]) {
10584                 cfg.cls += ' col-' + size + '-' + settings[size];
10585             }
10586         });
10587         
10588         return cfg;
10589         
10590     },
10591     
10592     // private
10593     initEvents: function()
10594     {
10595         
10596         if (!this.store) {
10597             throw "can not find store for combo";
10598         }
10599         this.store = Roo.factory(this.store, Roo.data);
10600         
10601         if(this.tickable){
10602             this.initTickableEvents();
10603             return;
10604         }
10605         
10606         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
10607         
10608         if(this.hiddenName){
10609             
10610             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10611             
10612             this.hiddenField.dom.value =
10613                 this.hiddenValue !== undefined ? this.hiddenValue :
10614                 this.value !== undefined ? this.value : '';
10615
10616             // prevent input submission
10617             this.el.dom.removeAttribute('name');
10618             this.hiddenField.dom.setAttribute('name', this.hiddenName);
10619              
10620              
10621         }
10622         //if(Roo.isGecko){
10623         //    this.el.dom.setAttribute('autocomplete', 'off');
10624         //}
10625         
10626         var cls = 'x-combo-list';
10627         
10628         //this.list = new Roo.Layer({
10629         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
10630         //});
10631         
10632         var _this = this;
10633         
10634         (function(){
10635             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
10636             _this.list.setWidth(lw);
10637         }).defer(100);
10638         
10639         this.list.on('mouseover', this.onViewOver, this);
10640         this.list.on('mousemove', this.onViewMove, this);
10641         
10642         this.list.on('scroll', this.onViewScroll, this);
10643         
10644         /*
10645         this.list.swallowEvent('mousewheel');
10646         this.assetHeight = 0;
10647
10648         if(this.title){
10649             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
10650             this.assetHeight += this.header.getHeight();
10651         }
10652
10653         this.innerList = this.list.createChild({cls:cls+'-inner'});
10654         this.innerList.on('mouseover', this.onViewOver, this);
10655         this.innerList.on('mousemove', this.onViewMove, this);
10656         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
10657         
10658         if(this.allowBlank && !this.pageSize && !this.disableClear){
10659             this.footer = this.list.createChild({cls:cls+'-ft'});
10660             this.pageTb = new Roo.Toolbar(this.footer);
10661            
10662         }
10663         if(this.pageSize){
10664             this.footer = this.list.createChild({cls:cls+'-ft'});
10665             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
10666                     {pageSize: this.pageSize});
10667             
10668         }
10669         
10670         if (this.pageTb && this.allowBlank && !this.disableClear) {
10671             var _this = this;
10672             this.pageTb.add(new Roo.Toolbar.Fill(), {
10673                 cls: 'x-btn-icon x-btn-clear',
10674                 text: '&#160;',
10675                 handler: function()
10676                 {
10677                     _this.collapse();
10678                     _this.clearValue();
10679                     _this.onSelect(false, -1);
10680                 }
10681             });
10682         }
10683         if (this.footer) {
10684             this.assetHeight += this.footer.getHeight();
10685         }
10686         */
10687             
10688         if(!this.tpl){
10689             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
10690         }
10691
10692         this.view = new Roo.View(this.list, this.tpl, {
10693             singleSelect:true, store: this.store, selectedClass: this.selectedClass
10694         });
10695         //this.view.wrapEl.setDisplayed(false);
10696         this.view.on('click', this.onViewClick, this);
10697         
10698         
10699         
10700         this.store.on('beforeload', this.onBeforeLoad, this);
10701         this.store.on('load', this.onLoad, this);
10702         this.store.on('loadexception', this.onLoadException, this);
10703         /*
10704         if(this.resizable){
10705             this.resizer = new Roo.Resizable(this.list,  {
10706                pinned:true, handles:'se'
10707             });
10708             this.resizer.on('resize', function(r, w, h){
10709                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
10710                 this.listWidth = w;
10711                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
10712                 this.restrictHeight();
10713             }, this);
10714             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
10715         }
10716         */
10717         if(!this.editable){
10718             this.editable = true;
10719             this.setEditable(false);
10720         }
10721         
10722         /*
10723         
10724         if (typeof(this.events.add.listeners) != 'undefined') {
10725             
10726             this.addicon = this.wrap.createChild(
10727                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
10728        
10729             this.addicon.on('click', function(e) {
10730                 this.fireEvent('add', this);
10731             }, this);
10732         }
10733         if (typeof(this.events.edit.listeners) != 'undefined') {
10734             
10735             this.editicon = this.wrap.createChild(
10736                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
10737             if (this.addicon) {
10738                 this.editicon.setStyle('margin-left', '40px');
10739             }
10740             this.editicon.on('click', function(e) {
10741                 
10742                 // we fire even  if inothing is selected..
10743                 this.fireEvent('edit', this, this.lastData );
10744                 
10745             }, this);
10746         }
10747         */
10748         
10749         this.keyNav = new Roo.KeyNav(this.inputEl(), {
10750             "up" : function(e){
10751                 this.inKeyMode = true;
10752                 this.selectPrev();
10753             },
10754
10755             "down" : function(e){
10756                 if(!this.isExpanded()){
10757                     this.onTriggerClick();
10758                 }else{
10759                     this.inKeyMode = true;
10760                     this.selectNext();
10761                 }
10762             },
10763
10764             "enter" : function(e){
10765 //                this.onViewClick();
10766                 //return true;
10767                 this.collapse();
10768                 
10769                 if(this.fireEvent("specialkey", this, e)){
10770                     this.onViewClick(false);
10771                 }
10772                 
10773                 return true;
10774             },
10775
10776             "esc" : function(e){
10777                 this.collapse();
10778             },
10779
10780             "tab" : function(e){
10781                 this.collapse();
10782                 
10783                 if(this.fireEvent("specialkey", this, e)){
10784                     this.onViewClick(false);
10785                 }
10786                 
10787                 return true;
10788             },
10789
10790             scope : this,
10791
10792             doRelay : function(foo, bar, hname){
10793                 if(hname == 'down' || this.scope.isExpanded()){
10794                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
10795                 }
10796                 return true;
10797             },
10798
10799             forceKeyDown: true
10800         });
10801         
10802         
10803         this.queryDelay = Math.max(this.queryDelay || 10,
10804                 this.mode == 'local' ? 10 : 250);
10805         
10806         
10807         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
10808         
10809         if(this.typeAhead){
10810             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
10811         }
10812         if(this.editable !== false){
10813             this.inputEl().on("keyup", this.onKeyUp, this);
10814         }
10815         if(this.forceSelection){
10816             this.inputEl().on('blur', this.doForce, this);
10817         }
10818         
10819         if(this.multiple){
10820             this.choices = this.el.select('ul.select2-choices', true).first();
10821             this.searchField = this.el.select('ul li.select2-search-field', true).first();
10822         }
10823     },
10824     
10825     initTickableEvents: function()
10826     {   
10827         this.createList();
10828         
10829         if(this.hiddenName){
10830             
10831             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10832             
10833             this.hiddenField.dom.value =
10834                 this.hiddenValue !== undefined ? this.hiddenValue :
10835                 this.value !== undefined ? this.value : '';
10836
10837             // prevent input submission
10838             this.el.dom.removeAttribute('name');
10839             this.hiddenField.dom.setAttribute('name', this.hiddenName);
10840              
10841              
10842         }
10843         
10844 //        this.list = this.el.select('ul.dropdown-menu',true).first();
10845         
10846         this.choices = this.el.select('ul.select2-choices', true).first();
10847         this.searchField = this.el.select('ul li.select2-search-field', true).first();
10848         if(this.triggerList){
10849             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
10850         }
10851          
10852         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
10853         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
10854         
10855         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
10856         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
10857         
10858         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
10859         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
10860         
10861         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
10862         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
10863         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
10864         
10865         this.okBtn.hide();
10866         this.cancelBtn.hide();
10867         
10868         var _this = this;
10869         
10870         (function(){
10871             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
10872             _this.list.setWidth(lw);
10873         }).defer(100);
10874         
10875         this.list.on('mouseover', this.onViewOver, this);
10876         this.list.on('mousemove', this.onViewMove, this);
10877         
10878         this.list.on('scroll', this.onViewScroll, this);
10879         
10880         if(!this.tpl){
10881             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>';
10882         }
10883
10884         this.view = new Roo.View(this.list, this.tpl, {
10885             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
10886         });
10887         
10888         //this.view.wrapEl.setDisplayed(false);
10889         this.view.on('click', this.onViewClick, this);
10890         
10891         
10892         
10893         this.store.on('beforeload', this.onBeforeLoad, this);
10894         this.store.on('load', this.onLoad, this);
10895         this.store.on('loadexception', this.onLoadException, this);
10896         
10897 //        this.keyNav = new Roo.KeyNav(this.inputEl(), {
10898 //            "up" : function(e){
10899 //                this.inKeyMode = true;
10900 //                this.selectPrev();
10901 //            },
10902 //
10903 //            "down" : function(e){
10904 //                if(!this.isExpanded()){
10905 //                    this.onTriggerClick();
10906 //                }else{
10907 //                    this.inKeyMode = true;
10908 //                    this.selectNext();
10909 //                }
10910 //            },
10911 //
10912 //            "enter" : function(e){
10913 ////                this.onViewClick();
10914 //                //return true;
10915 //                this.collapse();
10916 //                
10917 //                if(this.fireEvent("specialkey", this, e)){
10918 //                    this.onViewClick(false);
10919 //                }
10920 //                
10921 //                return true;
10922 //            },
10923 //
10924 //            "esc" : function(e){
10925 //                this.collapse();
10926 //            },
10927 //
10928 //            "tab" : function(e){
10929 //                this.collapse();
10930 //                
10931 //                if(this.fireEvent("specialkey", this, e)){
10932 //                    this.onViewClick(false);
10933 //                }
10934 //                
10935 //                return true;
10936 //            },
10937 //
10938 //            scope : this,
10939 //
10940 //            doRelay : function(foo, bar, hname){
10941 //                if(hname == 'down' || this.scope.isExpanded()){
10942 //                   return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
10943 //                }
10944 //                return true;
10945 //            },
10946 //
10947 //            forceKeyDown: true
10948 //        });
10949         
10950         
10951         this.queryDelay = Math.max(this.queryDelay || 10,
10952                 this.mode == 'local' ? 10 : 250);
10953         
10954         
10955         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
10956         
10957         if(this.typeAhead){
10958             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
10959         }
10960     },
10961
10962     onDestroy : function(){
10963         if(this.view){
10964             this.view.setStore(null);
10965             this.view.el.removeAllListeners();
10966             this.view.el.remove();
10967             this.view.purgeListeners();
10968         }
10969         if(this.list){
10970             this.list.dom.innerHTML  = '';
10971         }
10972         
10973         if(this.store){
10974             this.store.un('beforeload', this.onBeforeLoad, this);
10975             this.store.un('load', this.onLoad, this);
10976             this.store.un('loadexception', this.onLoadException, this);
10977         }
10978         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
10979     },
10980
10981     // private
10982     fireKey : function(e){
10983         if(e.isNavKeyPress() && !this.list.isVisible()){
10984             this.fireEvent("specialkey", this, e);
10985         }
10986     },
10987
10988     // private
10989     onResize: function(w, h){
10990 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
10991 //        
10992 //        if(typeof w != 'number'){
10993 //            // we do not handle it!?!?
10994 //            return;
10995 //        }
10996 //        var tw = this.trigger.getWidth();
10997 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
10998 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
10999 //        var x = w - tw;
11000 //        this.inputEl().setWidth( this.adjustWidth('input', x));
11001 //            
11002 //        //this.trigger.setStyle('left', x+'px');
11003 //        
11004 //        if(this.list && this.listWidth === undefined){
11005 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
11006 //            this.list.setWidth(lw);
11007 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11008 //        }
11009         
11010     
11011         
11012     },
11013
11014     /**
11015      * Allow or prevent the user from directly editing the field text.  If false is passed,
11016      * the user will only be able to select from the items defined in the dropdown list.  This method
11017      * is the runtime equivalent of setting the 'editable' config option at config time.
11018      * @param {Boolean} value True to allow the user to directly edit the field text
11019      */
11020     setEditable : function(value){
11021         if(value == this.editable){
11022             return;
11023         }
11024         this.editable = value;
11025         if(!value){
11026             this.inputEl().dom.setAttribute('readOnly', true);
11027             this.inputEl().on('mousedown', this.onTriggerClick,  this);
11028             this.inputEl().addClass('x-combo-noedit');
11029         }else{
11030             this.inputEl().dom.setAttribute('readOnly', false);
11031             this.inputEl().un('mousedown', this.onTriggerClick,  this);
11032             this.inputEl().removeClass('x-combo-noedit');
11033         }
11034     },
11035
11036     // private
11037     
11038     onBeforeLoad : function(combo,opts){
11039         if(!this.hasFocus){
11040             return;
11041         }
11042          if (!opts.add) {
11043             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
11044          }
11045 //        this.restrictHeight();
11046         this.selectedIndex = -1;
11047     },
11048
11049     // private
11050     onLoad : function(){
11051         
11052         this.hasQuery = false;
11053         
11054         if(!this.hasFocus){
11055             return;
11056         }
11057         
11058         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11059             this.loading.hide();
11060         }
11061         
11062         if(this.store.getCount() > 0){
11063             this.expand();
11064 //            this.restrictHeight();
11065             if(this.lastQuery == this.allQuery){
11066                 if(this.editable && !this.tickable){
11067                     this.inputEl().dom.select();
11068                 }
11069                 
11070                 if(
11071                     !this.selectByValue(this.value, true) &&
11072                     this.autoFocus && (typeof(this.store.lastOptions.add) == 'undefined' || 
11073                     this.store.lastOptions.add != true)
11074                 ){
11075                     this.select(0, true);
11076                 }
11077             }else{
11078                 if(this.autoFocus){
11079                     this.selectNext();
11080                 }
11081                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
11082                     this.taTask.delay(this.typeAheadDelay);
11083                 }
11084             }
11085         }else{
11086             this.onEmptyResults();
11087         }
11088         
11089         //this.el.focus();
11090     },
11091     // private
11092     onLoadException : function()
11093     {
11094         this.hasQuery = false;
11095         
11096         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11097             this.loading.hide();
11098         }
11099         
11100         this.collapse();
11101         Roo.log(this.store.reader.jsonData);
11102         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
11103             // fixme
11104             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
11105         }
11106         
11107         
11108     },
11109     // private
11110     onTypeAhead : function(){
11111         if(this.store.getCount() > 0){
11112             var r = this.store.getAt(0);
11113             var newValue = r.data[this.displayField];
11114             var len = newValue.length;
11115             var selStart = this.getRawValue().length;
11116             
11117             if(selStart != len){
11118                 this.setRawValue(newValue);
11119                 this.selectText(selStart, newValue.length);
11120             }
11121         }
11122     },
11123
11124     // private
11125     onSelect : function(record, index){
11126         
11127         if(this.fireEvent('beforeselect', this, record, index) !== false){
11128         
11129             this.setFromData(index > -1 ? record.data : false);
11130             
11131             this.collapse();
11132             this.fireEvent('select', this, record, index);
11133         }
11134     },
11135
11136     /**
11137      * Returns the currently selected field value or empty string if no value is set.
11138      * @return {String} value The selected value
11139      */
11140     getValue : function(){
11141         
11142         if(this.multiple){
11143             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
11144         }
11145         
11146         if(this.valueField){
11147             return typeof this.value != 'undefined' ? this.value : '';
11148         }else{
11149             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
11150         }
11151     },
11152
11153     /**
11154      * Clears any text/value currently set in the field
11155      */
11156     clearValue : function(){
11157         if(this.hiddenField){
11158             this.hiddenField.dom.value = '';
11159         }
11160         this.value = '';
11161         this.setRawValue('');
11162         this.lastSelectionText = '';
11163         
11164     },
11165
11166     /**
11167      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
11168      * will be displayed in the field.  If the value does not match the data value of an existing item,
11169      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
11170      * Otherwise the field will be blank (although the value will still be set).
11171      * @param {String} value The value to match
11172      */
11173     setValue : function(v){
11174         if(this.multiple){
11175             this.syncValue();
11176             return;
11177         }
11178         
11179         var text = v;
11180         if(this.valueField){
11181             var r = this.findRecord(this.valueField, v);
11182             if(r){
11183                 text = r.data[this.displayField];
11184             }else if(this.valueNotFoundText !== undefined){
11185                 text = this.valueNotFoundText;
11186             }
11187         }
11188         this.lastSelectionText = text;
11189         if(this.hiddenField){
11190             this.hiddenField.dom.value = v;
11191         }
11192         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
11193         this.value = v;
11194     },
11195     /**
11196      * @property {Object} the last set data for the element
11197      */
11198     
11199     lastData : false,
11200     /**
11201      * Sets the value of the field based on a object which is related to the record format for the store.
11202      * @param {Object} value the value to set as. or false on reset?
11203      */
11204     setFromData : function(o){
11205         
11206         if(this.multiple){
11207             if(typeof o.display_name !== 'string'){
11208                 for(var i=0;i<o.display_name.length;i++){
11209                     this.addItem({'id':o.id[i],'display_name':o.display_name[i]});
11210                 }
11211                 return;
11212             }
11213             this.addItem(o);
11214             return;
11215         }
11216             
11217         var dv = ''; // display value
11218         var vv = ''; // value value..
11219         this.lastData = o;
11220         if (this.displayField) {
11221             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11222         } else {
11223             // this is an error condition!!!
11224             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
11225         }
11226         
11227         if(this.valueField){
11228             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
11229         }
11230         
11231         if(this.hiddenField){
11232             this.hiddenField.dom.value = vv;
11233             
11234             this.lastSelectionText = dv;
11235             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11236             this.value = vv;
11237             return;
11238         }
11239         // no hidden field.. - we store the value in 'value', but still display
11240         // display field!!!!
11241         this.lastSelectionText = dv;
11242         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11243         this.value = vv;
11244         
11245         
11246     },
11247     // private
11248     reset : function(){
11249         // overridden so that last data is reset..
11250         this.setValue(this.originalValue);
11251         this.clearInvalid();
11252         this.lastData = false;
11253         if (this.view) {
11254             this.view.clearSelections();
11255         }
11256     },
11257     // private
11258     findRecord : function(prop, value){
11259         var record;
11260         if(this.store.getCount() > 0){
11261             this.store.each(function(r){
11262                 if(r.data[prop] == value){
11263                     record = r;
11264                     return false;
11265                 }
11266                 return true;
11267             });
11268         }
11269         return record;
11270     },
11271     
11272     getName: function()
11273     {
11274         // returns hidden if it's set..
11275         if (!this.rendered) {return ''};
11276         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
11277         
11278     },
11279     // private
11280     onViewMove : function(e, t){
11281         this.inKeyMode = false;
11282     },
11283
11284     // private
11285     onViewOver : function(e, t){
11286         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
11287             return;
11288         }
11289         var item = this.view.findItemFromChild(t);
11290         
11291         if(item){
11292             var index = this.view.indexOf(item);
11293             this.select(index, false);
11294         }
11295     },
11296
11297     // private
11298     onViewClick : function(view, doFocus, el, e)
11299     {
11300         var index = this.view.getSelectedIndexes()[0];
11301         
11302         var r = this.store.getAt(index);
11303         
11304         if(this.tickable){
11305             
11306             if(e.getTarget().nodeName.toLowerCase() != 'input'){
11307                 return;
11308             }
11309             
11310             var rm = false;
11311             var _this = this;
11312             
11313             Roo.each(this.tickItems, function(v,k){
11314                 
11315                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
11316                     _this.tickItems.splice(k, 1);
11317                     rm = true;
11318                     return;
11319                 }
11320             })
11321             
11322             if(rm){
11323                 return;
11324             }
11325             
11326             this.tickItems.push(r.data);
11327             return;
11328         }
11329         
11330         if(r){
11331             this.onSelect(r, index);
11332         }
11333         if(doFocus !== false && !this.blockFocus){
11334             this.inputEl().focus();
11335         }
11336     },
11337
11338     // private
11339     restrictHeight : function(){
11340         //this.innerList.dom.style.height = '';
11341         //var inner = this.innerList.dom;
11342         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
11343         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
11344         //this.list.beginUpdate();
11345         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
11346         this.list.alignTo(this.inputEl(), this.listAlign);
11347         this.list.alignTo(this.inputEl(), this.listAlign);
11348         //this.list.endUpdate();
11349     },
11350
11351     // private
11352     onEmptyResults : function(){
11353         this.collapse();
11354     },
11355
11356     /**
11357      * Returns true if the dropdown list is expanded, else false.
11358      */
11359     isExpanded : function(){
11360         return this.list.isVisible();
11361     },
11362
11363     /**
11364      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
11365      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11366      * @param {String} value The data value of the item to select
11367      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11368      * selected item if it is not currently in view (defaults to true)
11369      * @return {Boolean} True if the value matched an item in the list, else false
11370      */
11371     selectByValue : function(v, scrollIntoView){
11372         if(v !== undefined && v !== null){
11373             var r = this.findRecord(this.valueField || this.displayField, v);
11374             if(r){
11375                 this.select(this.store.indexOf(r), scrollIntoView);
11376                 return true;
11377             }
11378         }
11379         return false;
11380     },
11381
11382     /**
11383      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
11384      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11385      * @param {Number} index The zero-based index of the list item to select
11386      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11387      * selected item if it is not currently in view (defaults to true)
11388      */
11389     select : function(index, scrollIntoView){
11390         this.selectedIndex = index;
11391         this.view.select(index);
11392         if(scrollIntoView !== false){
11393             var el = this.view.getNode(index);
11394             if(el && !this.multiple && !this.tickable){
11395                 this.list.scrollChildIntoView(el, false);
11396             }
11397         }
11398     },
11399
11400     // private
11401     selectNext : function(){
11402         var ct = this.store.getCount();
11403         if(ct > 0){
11404             if(this.selectedIndex == -1){
11405                 this.select(0);
11406             }else if(this.selectedIndex < ct-1){
11407                 this.select(this.selectedIndex+1);
11408             }
11409         }
11410     },
11411
11412     // private
11413     selectPrev : function(){
11414         var ct = this.store.getCount();
11415         if(ct > 0){
11416             if(this.selectedIndex == -1){
11417                 this.select(0);
11418             }else if(this.selectedIndex != 0){
11419                 this.select(this.selectedIndex-1);
11420             }
11421         }
11422     },
11423
11424     // private
11425     onKeyUp : function(e){
11426         if(this.editable !== false && !e.isSpecialKey()){
11427             this.lastKey = e.getKey();
11428             this.dqTask.delay(this.queryDelay);
11429         }
11430     },
11431
11432     // private
11433     validateBlur : function(){
11434         return !this.list || !this.list.isVisible();   
11435     },
11436
11437     // private
11438     initQuery : function(){
11439         this.doQuery(this.getRawValue());
11440     },
11441
11442     // private
11443     doForce : function(){
11444         if(this.inputEl().dom.value.length > 0){
11445             this.inputEl().dom.value =
11446                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
11447              
11448         }
11449     },
11450
11451     /**
11452      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
11453      * query allowing the query action to be canceled if needed.
11454      * @param {String} query The SQL query to execute
11455      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
11456      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
11457      * saved in the current store (defaults to false)
11458      */
11459     doQuery : function(q, forceAll){
11460         
11461         if(q === undefined || q === null){
11462             q = '';
11463         }
11464         var qe = {
11465             query: q,
11466             forceAll: forceAll,
11467             combo: this,
11468             cancel:false
11469         };
11470         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
11471             return false;
11472         }
11473         q = qe.query;
11474         
11475         forceAll = qe.forceAll;
11476         if(forceAll === true || (q.length >= this.minChars)){
11477             
11478             this.hasQuery = true;
11479             
11480             if(this.lastQuery != q || this.alwaysQuery){
11481                 this.lastQuery = q;
11482                 if(this.mode == 'local'){
11483                     this.selectedIndex = -1;
11484                     if(forceAll){
11485                         this.store.clearFilter();
11486                     }else{
11487                         this.store.filter(this.displayField, q);
11488                     }
11489                     this.onLoad();
11490                 }else{
11491                     this.store.baseParams[this.queryParam] = q;
11492                     
11493                     var options = {params : this.getParams(q)};
11494                     
11495                     if(this.loadNext){
11496                         options.add = true;
11497                         options.params.start = this.page * this.pageSize;
11498                     }
11499                     
11500                     this.store.load(options);
11501                     /*
11502                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
11503                      *  we should expand the list on onLoad
11504                      *  so command out it
11505                      */
11506 //                    this.expand();
11507                 }
11508             }else{
11509                 this.selectedIndex = -1;
11510                 this.onLoad();   
11511             }
11512         }
11513         
11514         this.loadNext = false;
11515     },
11516
11517     // private
11518     getParams : function(q){
11519         var p = {};
11520         //p[this.queryParam] = q;
11521         
11522         if(this.pageSize){
11523             p.start = 0;
11524             p.limit = this.pageSize;
11525         }
11526         return p;
11527     },
11528
11529     /**
11530      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
11531      */
11532     collapse : function(){
11533         if(!this.isExpanded()){
11534             return;
11535         }
11536         
11537         this.list.hide();
11538         
11539         if(this.tickable){
11540             this.okBtn.hide();
11541             this.cancelBtn.hide();
11542             this.trigger.show();
11543         }
11544         
11545         Roo.get(document).un('mousedown', this.collapseIf, this);
11546         Roo.get(document).un('mousewheel', this.collapseIf, this);
11547         if (!this.editable) {
11548             Roo.get(document).un('keydown', this.listKeyPress, this);
11549         }
11550         this.fireEvent('collapse', this);
11551     },
11552
11553     // private
11554     collapseIf : function(e){
11555         var in_combo  = e.within(this.el);
11556         var in_list =  e.within(this.list);
11557         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
11558         
11559         if (in_combo || in_list || is_list) {
11560             //e.stopPropagation();
11561             return;
11562         }
11563         
11564         if(this.tickable){
11565             this.onTickableFooterButtonClick(e, false, false);
11566         }
11567
11568         this.collapse();
11569         
11570     },
11571
11572     /**
11573      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
11574      */
11575     expand : function(){
11576        
11577         if(this.isExpanded() || !this.hasFocus){
11578             return;
11579         }
11580         
11581         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
11582         this.list.setWidth(lw);
11583         
11584         
11585          Roo.log('expand');
11586         
11587         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
11588         this.list.setWidth(lw);
11589             
11590         this.list.show();
11591         
11592         this.restrictHeight();
11593         
11594         if(this.tickable){
11595             
11596             this.tickItems = Roo.apply([], this.item);
11597             
11598             this.okBtn.show();
11599             this.cancelBtn.show();
11600             this.trigger.hide();
11601             
11602         }
11603         
11604         Roo.get(document).on('mousedown', this.collapseIf, this);
11605         Roo.get(document).on('mousewheel', this.collapseIf, this);
11606         if (!this.editable) {
11607             Roo.get(document).on('keydown', this.listKeyPress, this);
11608         }
11609         
11610         this.fireEvent('expand', this);
11611     },
11612
11613     // private
11614     // Implements the default empty TriggerField.onTriggerClick function
11615     onTriggerClick : function(e)
11616     {
11617         Roo.log('trigger click');
11618         
11619         if(this.disabled || !this.triggerList){
11620             return;
11621         }
11622         
11623         this.page = 0;
11624         this.loadNext = false;
11625         
11626         if(this.isExpanded()){
11627             this.collapse();
11628             if (!this.blockFocus) {
11629                 this.inputEl().focus();
11630             }
11631             
11632         }else {
11633             this.hasFocus = true;
11634             if(this.triggerAction == 'all') {
11635                 this.doQuery(this.allQuery, true);
11636             } else {
11637                 this.doQuery(this.getRawValue());
11638             }
11639             if (!this.blockFocus) {
11640                 this.inputEl().focus();
11641             }
11642         }
11643     },
11644     
11645     onTickableTriggerClick : function(e)
11646     {
11647         if(this.disabled){
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     onSearchFieldClick : function(e)
11663     {
11664         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
11665             return;
11666         }
11667         
11668         this.page = 0;
11669         this.loadNext = false;
11670         this.hasFocus = true;
11671         
11672         if(this.triggerAction == 'all') {
11673             this.doQuery(this.allQuery, true);
11674         } else {
11675             this.doQuery(this.getRawValue());
11676         }
11677     },
11678     
11679     listKeyPress : function(e)
11680     {
11681         //Roo.log('listkeypress');
11682         // scroll to first matching element based on key pres..
11683         if (e.isSpecialKey()) {
11684             return false;
11685         }
11686         var k = String.fromCharCode(e.getKey()).toUpperCase();
11687         //Roo.log(k);
11688         var match  = false;
11689         var csel = this.view.getSelectedNodes();
11690         var cselitem = false;
11691         if (csel.length) {
11692             var ix = this.view.indexOf(csel[0]);
11693             cselitem  = this.store.getAt(ix);
11694             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
11695                 cselitem = false;
11696             }
11697             
11698         }
11699         
11700         this.store.each(function(v) { 
11701             if (cselitem) {
11702                 // start at existing selection.
11703                 if (cselitem.id == v.id) {
11704                     cselitem = false;
11705                 }
11706                 return true;
11707             }
11708                 
11709             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
11710                 match = this.store.indexOf(v);
11711                 return false;
11712             }
11713             return true;
11714         }, this);
11715         
11716         if (match === false) {
11717             return true; // no more action?
11718         }
11719         // scroll to?
11720         this.view.select(match);
11721         var sn = Roo.get(this.view.getSelectedNodes()[0])
11722         //sn.scrollIntoView(sn.dom.parentNode, false);
11723     },
11724     
11725     onViewScroll : function(e, t){
11726         
11727         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){
11728             return;
11729         }
11730         
11731         this.hasQuery = true;
11732         
11733         this.loading = this.list.select('.loading', true).first();
11734         
11735         if(this.loading === null){
11736             this.list.createChild({
11737                 tag: 'div',
11738                 cls: 'loading select2-more-results select2-active',
11739                 html: 'Loading more results...'
11740             })
11741             
11742             this.loading = this.list.select('.loading', true).first();
11743             
11744             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
11745             
11746             this.loading.hide();
11747         }
11748         
11749         this.loading.show();
11750         
11751         var _combo = this;
11752         
11753         this.page++;
11754         this.loadNext = true;
11755         
11756         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
11757         
11758         return;
11759     },
11760     
11761     addItem : function(o)
11762     {   
11763         var dv = ''; // display value
11764         
11765         if (this.displayField) {
11766             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11767         } else {
11768             // this is an error condition!!!
11769             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
11770         }
11771         
11772         if(!dv.length){
11773             return;
11774         }
11775         
11776         var choice = this.choices.createChild({
11777             tag: 'li',
11778             cls: 'select2-search-choice',
11779             cn: [
11780                 {
11781                     tag: 'div',
11782                     html: dv
11783                 },
11784                 {
11785                     tag: 'a',
11786                     href: '#',
11787                     cls: 'select2-search-choice-close',
11788                     tabindex: '-1'
11789                 }
11790             ]
11791             
11792         }, this.searchField);
11793         
11794         var close = choice.select('a.select2-search-choice-close', true).first()
11795         
11796         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
11797         
11798         this.item.push(o);
11799         
11800         this.lastData = o;
11801         
11802         this.syncValue();
11803         
11804         this.inputEl().dom.value = '';
11805         
11806     },
11807     
11808     onRemoveItem : function(e, _self, o)
11809     {
11810         e.preventDefault();
11811         var index = this.item.indexOf(o.data) * 1;
11812         
11813         if( index < 0){
11814             Roo.log('not this item?!');
11815             return;
11816         }
11817         
11818         this.item.splice(index, 1);
11819         o.item.remove();
11820         
11821         this.syncValue();
11822         
11823         this.fireEvent('remove', this, e);
11824         
11825     },
11826     
11827     syncValue : function()
11828     {
11829         if(!this.item.length){
11830             this.clearValue();
11831             return;
11832         }
11833             
11834         var value = [];
11835         var _this = this;
11836         Roo.each(this.item, function(i){
11837             if(_this.valueField){
11838                 value.push(i[_this.valueField]);
11839                 return;
11840             }
11841
11842             value.push(i);
11843         });
11844
11845         this.value = value.join(',');
11846
11847         if(this.hiddenField){
11848             this.hiddenField.dom.value = this.value;
11849         }
11850     },
11851     
11852     clearItem : function()
11853     {
11854         if(!this.multiple){
11855             return;
11856         }
11857         
11858         this.item = [];
11859         
11860         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
11861            c.remove();
11862         });
11863         
11864         this.syncValue();
11865     },
11866     
11867     inputEl: function ()
11868     {
11869         if(this.tickable){
11870             return this.searchField;
11871         }
11872         return this.el.select('input.form-control',true).first();
11873     },
11874     
11875     
11876     onTickableFooterButtonClick : function(e, btn, el)
11877     {
11878         e.preventDefault();
11879         
11880         if(btn && btn.name == 'cancel'){
11881             this.tickItems = Roo.apply([], this.item);
11882             this.collapse();
11883             return;
11884         }
11885         
11886         this.clearItem();
11887         
11888         var _this = this;
11889         
11890         Roo.each(this.tickItems, function(o){
11891             _this.addItem(o);
11892         });
11893         
11894         this.collapse();
11895         
11896     }
11897     
11898     
11899
11900     /** 
11901     * @cfg {Boolean} grow 
11902     * @hide 
11903     */
11904     /** 
11905     * @cfg {Number} growMin 
11906     * @hide 
11907     */
11908     /** 
11909     * @cfg {Number} growMax 
11910     * @hide 
11911     */
11912     /**
11913      * @hide
11914      * @method autoSize
11915      */
11916 });
11917 /*
11918  * Based on:
11919  * Ext JS Library 1.1.1
11920  * Copyright(c) 2006-2007, Ext JS, LLC.
11921  *
11922  * Originally Released Under LGPL - original licence link has changed is not relivant.
11923  *
11924  * Fork - LGPL
11925  * <script type="text/javascript">
11926  */
11927
11928 /**
11929  * @class Roo.View
11930  * @extends Roo.util.Observable
11931  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
11932  * This class also supports single and multi selection modes. <br>
11933  * Create a data model bound view:
11934  <pre><code>
11935  var store = new Roo.data.Store(...);
11936
11937  var view = new Roo.View({
11938     el : "my-element",
11939     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
11940  
11941     singleSelect: true,
11942     selectedClass: "ydataview-selected",
11943     store: store
11944  });
11945
11946  // listen for node click?
11947  view.on("click", function(vw, index, node, e){
11948  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
11949  });
11950
11951  // load XML data
11952  dataModel.load("foobar.xml");
11953  </code></pre>
11954  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
11955  * <br><br>
11956  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
11957  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
11958  * 
11959  * Note: old style constructor is still suported (container, template, config)
11960  * 
11961  * @constructor
11962  * Create a new View
11963  * @param {Object} config The config object
11964  * 
11965  */
11966 Roo.View = function(config, depreciated_tpl, depreciated_config){
11967     
11968     this.parent = false;
11969     
11970     if (typeof(depreciated_tpl) == 'undefined') {
11971         // new way.. - universal constructor.
11972         Roo.apply(this, config);
11973         this.el  = Roo.get(this.el);
11974     } else {
11975         // old format..
11976         this.el  = Roo.get(config);
11977         this.tpl = depreciated_tpl;
11978         Roo.apply(this, depreciated_config);
11979     }
11980     this.wrapEl  = this.el.wrap().wrap();
11981     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
11982     
11983     
11984     if(typeof(this.tpl) == "string"){
11985         this.tpl = new Roo.Template(this.tpl);
11986     } else {
11987         // support xtype ctors..
11988         this.tpl = new Roo.factory(this.tpl, Roo);
11989     }
11990     
11991     
11992     this.tpl.compile();
11993     
11994     /** @private */
11995     this.addEvents({
11996         /**
11997          * @event beforeclick
11998          * Fires before a click is processed. Returns false to cancel the default action.
11999          * @param {Roo.View} this
12000          * @param {Number} index The index of the target node
12001          * @param {HTMLElement} node The target node
12002          * @param {Roo.EventObject} e The raw event object
12003          */
12004             "beforeclick" : true,
12005         /**
12006          * @event click
12007          * Fires when a template node is clicked.
12008          * @param {Roo.View} this
12009          * @param {Number} index The index of the target node
12010          * @param {HTMLElement} node The target node
12011          * @param {Roo.EventObject} e The raw event object
12012          */
12013             "click" : true,
12014         /**
12015          * @event dblclick
12016          * Fires when a template node is double clicked.
12017          * @param {Roo.View} this
12018          * @param {Number} index The index of the target node
12019          * @param {HTMLElement} node The target node
12020          * @param {Roo.EventObject} e The raw event object
12021          */
12022             "dblclick" : true,
12023         /**
12024          * @event contextmenu
12025          * Fires when a template node is right clicked.
12026          * @param {Roo.View} this
12027          * @param {Number} index The index of the target node
12028          * @param {HTMLElement} node The target node
12029          * @param {Roo.EventObject} e The raw event object
12030          */
12031             "contextmenu" : true,
12032         /**
12033          * @event selectionchange
12034          * Fires when the selected nodes change.
12035          * @param {Roo.View} this
12036          * @param {Array} selections Array of the selected nodes
12037          */
12038             "selectionchange" : true,
12039     
12040         /**
12041          * @event beforeselect
12042          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
12043          * @param {Roo.View} this
12044          * @param {HTMLElement} node The node to be selected
12045          * @param {Array} selections Array of currently selected nodes
12046          */
12047             "beforeselect" : true,
12048         /**
12049          * @event preparedata
12050          * Fires on every row to render, to allow you to change the data.
12051          * @param {Roo.View} this
12052          * @param {Object} data to be rendered (change this)
12053          */
12054           "preparedata" : true
12055           
12056           
12057         });
12058
12059
12060
12061     this.el.on({
12062         "click": this.onClick,
12063         "dblclick": this.onDblClick,
12064         "contextmenu": this.onContextMenu,
12065         scope:this
12066     });
12067
12068     this.selections = [];
12069     this.nodes = [];
12070     this.cmp = new Roo.CompositeElementLite([]);
12071     if(this.store){
12072         this.store = Roo.factory(this.store, Roo.data);
12073         this.setStore(this.store, true);
12074     }
12075     
12076     if ( this.footer && this.footer.xtype) {
12077            
12078          var fctr = this.wrapEl.appendChild(document.createElement("div"));
12079         
12080         this.footer.dataSource = this.store
12081         this.footer.container = fctr;
12082         this.footer = Roo.factory(this.footer, Roo);
12083         fctr.insertFirst(this.el);
12084         
12085         // this is a bit insane - as the paging toolbar seems to detach the el..
12086 //        dom.parentNode.parentNode.parentNode
12087          // they get detached?
12088     }
12089     
12090     
12091     Roo.View.superclass.constructor.call(this);
12092     
12093     
12094 };
12095
12096 Roo.extend(Roo.View, Roo.util.Observable, {
12097     
12098      /**
12099      * @cfg {Roo.data.Store} store Data store to load data from.
12100      */
12101     store : false,
12102     
12103     /**
12104      * @cfg {String|Roo.Element} el The container element.
12105      */
12106     el : '',
12107     
12108     /**
12109      * @cfg {String|Roo.Template} tpl The template used by this View 
12110      */
12111     tpl : false,
12112     /**
12113      * @cfg {String} dataName the named area of the template to use as the data area
12114      *                          Works with domtemplates roo-name="name"
12115      */
12116     dataName: false,
12117     /**
12118      * @cfg {String} selectedClass The css class to add to selected nodes
12119      */
12120     selectedClass : "x-view-selected",
12121      /**
12122      * @cfg {String} emptyText The empty text to show when nothing is loaded.
12123      */
12124     emptyText : "",
12125     
12126     /**
12127      * @cfg {String} text to display on mask (default Loading)
12128      */
12129     mask : false,
12130     /**
12131      * @cfg {Boolean} multiSelect Allow multiple selection
12132      */
12133     multiSelect : false,
12134     /**
12135      * @cfg {Boolean} singleSelect Allow single selection
12136      */
12137     singleSelect:  false,
12138     
12139     /**
12140      * @cfg {Boolean} toggleSelect - selecting 
12141      */
12142     toggleSelect : false,
12143     
12144     /**
12145      * @cfg {Boolean} tickable - selecting 
12146      */
12147     tickable : false,
12148     
12149     /**
12150      * Returns the element this view is bound to.
12151      * @return {Roo.Element}
12152      */
12153     getEl : function(){
12154         return this.wrapEl;
12155     },
12156     
12157     
12158
12159     /**
12160      * Refreshes the view. - called by datachanged on the store. - do not call directly.
12161      */
12162     refresh : function(){
12163         Roo.log('refresh');
12164         var t = this.tpl;
12165         
12166         // if we are using something like 'domtemplate', then
12167         // the what gets used is:
12168         // t.applySubtemplate(NAME, data, wrapping data..)
12169         // the outer template then get' applied with
12170         //     the store 'extra data'
12171         // and the body get's added to the
12172         //      roo-name="data" node?
12173         //      <span class='roo-tpl-{name}'></span> ?????
12174         
12175         
12176         
12177         this.clearSelections();
12178         this.el.update("");
12179         var html = [];
12180         var records = this.store.getRange();
12181         if(records.length < 1) {
12182             
12183             // is this valid??  = should it render a template??
12184             
12185             this.el.update(this.emptyText);
12186             return;
12187         }
12188         var el = this.el;
12189         if (this.dataName) {
12190             this.el.update(t.apply(this.store.meta)); //????
12191             el = this.el.child('.roo-tpl-' + this.dataName);
12192         }
12193         
12194         for(var i = 0, len = records.length; i < len; i++){
12195             var data = this.prepareData(records[i].data, i, records[i]);
12196             this.fireEvent("preparedata", this, data, i, records[i]);
12197             
12198             var d = Roo.apply({}, data);
12199             
12200             if(this.tickable){
12201                 Roo.apply(d, {'roo-id' : Roo.id()});
12202                 
12203                 var _this = this;
12204             
12205                 Roo.each(this.parent.item, function(item){
12206                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
12207                         return;
12208                     }
12209                     Roo.apply(d, {'roo-data-checked' : 'checked'});
12210                 });
12211             }
12212             
12213             html[html.length] = Roo.util.Format.trim(
12214                 this.dataName ?
12215                     t.applySubtemplate(this.dataName, d, this.store.meta) :
12216                     t.apply(d)
12217             );
12218         }
12219         
12220         
12221         
12222         el.update(html.join(""));
12223         this.nodes = el.dom.childNodes;
12224         this.updateIndexes(0);
12225     },
12226     
12227
12228     /**
12229      * Function to override to reformat the data that is sent to
12230      * the template for each node.
12231      * DEPRICATED - use the preparedata event handler.
12232      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
12233      * a JSON object for an UpdateManager bound view).
12234      */
12235     prepareData : function(data, index, record)
12236     {
12237         this.fireEvent("preparedata", this, data, index, record);
12238         return data;
12239     },
12240
12241     onUpdate : function(ds, record){
12242          Roo.log('on update');   
12243         this.clearSelections();
12244         var index = this.store.indexOf(record);
12245         var n = this.nodes[index];
12246         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
12247         n.parentNode.removeChild(n);
12248         this.updateIndexes(index, index);
12249     },
12250
12251     
12252     
12253 // --------- FIXME     
12254     onAdd : function(ds, records, index)
12255     {
12256         Roo.log(['on Add', ds, records, index] );        
12257         this.clearSelections();
12258         if(this.nodes.length == 0){
12259             this.refresh();
12260             return;
12261         }
12262         var n = this.nodes[index];
12263         for(var i = 0, len = records.length; i < len; i++){
12264             var d = this.prepareData(records[i].data, i, records[i]);
12265             if(n){
12266                 this.tpl.insertBefore(n, d);
12267             }else{
12268                 
12269                 this.tpl.append(this.el, d);
12270             }
12271         }
12272         this.updateIndexes(index);
12273     },
12274
12275     onRemove : function(ds, record, index){
12276         Roo.log('onRemove');
12277         this.clearSelections();
12278         var el = this.dataName  ?
12279             this.el.child('.roo-tpl-' + this.dataName) :
12280             this.el; 
12281         
12282         el.dom.removeChild(this.nodes[index]);
12283         this.updateIndexes(index);
12284     },
12285
12286     /**
12287      * Refresh an individual node.
12288      * @param {Number} index
12289      */
12290     refreshNode : function(index){
12291         this.onUpdate(this.store, this.store.getAt(index));
12292     },
12293
12294     updateIndexes : function(startIndex, endIndex){
12295         var ns = this.nodes;
12296         startIndex = startIndex || 0;
12297         endIndex = endIndex || ns.length - 1;
12298         for(var i = startIndex; i <= endIndex; i++){
12299             ns[i].nodeIndex = i;
12300         }
12301     },
12302
12303     /**
12304      * Changes the data store this view uses and refresh the view.
12305      * @param {Store} store
12306      */
12307     setStore : function(store, initial){
12308         if(!initial && this.store){
12309             this.store.un("datachanged", this.refresh);
12310             this.store.un("add", this.onAdd);
12311             this.store.un("remove", this.onRemove);
12312             this.store.un("update", this.onUpdate);
12313             this.store.un("clear", this.refresh);
12314             this.store.un("beforeload", this.onBeforeLoad);
12315             this.store.un("load", this.onLoad);
12316             this.store.un("loadexception", this.onLoad);
12317         }
12318         if(store){
12319           
12320             store.on("datachanged", this.refresh, this);
12321             store.on("add", this.onAdd, this);
12322             store.on("remove", this.onRemove, this);
12323             store.on("update", this.onUpdate, this);
12324             store.on("clear", this.refresh, this);
12325             store.on("beforeload", this.onBeforeLoad, this);
12326             store.on("load", this.onLoad, this);
12327             store.on("loadexception", this.onLoad, this);
12328         }
12329         
12330         if(store){
12331             this.refresh();
12332         }
12333     },
12334     /**
12335      * onbeforeLoad - masks the loading area.
12336      *
12337      */
12338     onBeforeLoad : function(store,opts)
12339     {
12340          Roo.log('onBeforeLoad');   
12341         if (!opts.add) {
12342             this.el.update("");
12343         }
12344         this.el.mask(this.mask ? this.mask : "Loading" ); 
12345     },
12346     onLoad : function ()
12347     {
12348         this.el.unmask();
12349     },
12350     
12351
12352     /**
12353      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
12354      * @param {HTMLElement} node
12355      * @return {HTMLElement} The template node
12356      */
12357     findItemFromChild : function(node){
12358         var el = this.dataName  ?
12359             this.el.child('.roo-tpl-' + this.dataName,true) :
12360             this.el.dom; 
12361         
12362         if(!node || node.parentNode == el){
12363                     return node;
12364             }
12365             var p = node.parentNode;
12366             while(p && p != el){
12367             if(p.parentNode == el){
12368                 return p;
12369             }
12370             p = p.parentNode;
12371         }
12372             return null;
12373     },
12374
12375     /** @ignore */
12376     onClick : function(e){
12377         var item = this.findItemFromChild(e.getTarget());
12378         if(item){
12379             var index = this.indexOf(item);
12380             if(this.onItemClick(item, index, e) !== false){
12381                 this.fireEvent("click", this, index, item, e);
12382             }
12383         }else{
12384             this.clearSelections();
12385         }
12386     },
12387
12388     /** @ignore */
12389     onContextMenu : function(e){
12390         var item = this.findItemFromChild(e.getTarget());
12391         if(item){
12392             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
12393         }
12394     },
12395
12396     /** @ignore */
12397     onDblClick : function(e){
12398         var item = this.findItemFromChild(e.getTarget());
12399         if(item){
12400             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
12401         }
12402     },
12403
12404     onItemClick : function(item, index, e)
12405     {
12406         if(this.fireEvent("beforeclick", this, index, item, e) === false){
12407             return false;
12408         }
12409         if (this.toggleSelect) {
12410             var m = this.isSelected(item) ? 'unselect' : 'select';
12411             Roo.log(m);
12412             var _t = this;
12413             _t[m](item, true, false);
12414             return true;
12415         }
12416         if(this.multiSelect || this.singleSelect){
12417             if(this.multiSelect && e.shiftKey && this.lastSelection){
12418                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
12419             }else{
12420                 this.select(item, this.multiSelect && e.ctrlKey);
12421                 this.lastSelection = item;
12422             }
12423             
12424             if(!this.tickable){
12425                 e.preventDefault();
12426             }
12427             
12428         }
12429         return true;
12430     },
12431
12432     /**
12433      * Get the number of selected nodes.
12434      * @return {Number}
12435      */
12436     getSelectionCount : function(){
12437         return this.selections.length;
12438     },
12439
12440     /**
12441      * Get the currently selected nodes.
12442      * @return {Array} An array of HTMLElements
12443      */
12444     getSelectedNodes : function(){
12445         return this.selections;
12446     },
12447
12448     /**
12449      * Get the indexes of the selected nodes.
12450      * @return {Array}
12451      */
12452     getSelectedIndexes : function(){
12453         var indexes = [], s = this.selections;
12454         for(var i = 0, len = s.length; i < len; i++){
12455             indexes.push(s[i].nodeIndex);
12456         }
12457         return indexes;
12458     },
12459
12460     /**
12461      * Clear all selections
12462      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
12463      */
12464     clearSelections : function(suppressEvent){
12465         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
12466             this.cmp.elements = this.selections;
12467             this.cmp.removeClass(this.selectedClass);
12468             this.selections = [];
12469             if(!suppressEvent){
12470                 this.fireEvent("selectionchange", this, this.selections);
12471             }
12472         }
12473     },
12474
12475     /**
12476      * Returns true if the passed node is selected
12477      * @param {HTMLElement/Number} node The node or node index
12478      * @return {Boolean}
12479      */
12480     isSelected : function(node){
12481         var s = this.selections;
12482         if(s.length < 1){
12483             return false;
12484         }
12485         node = this.getNode(node);
12486         return s.indexOf(node) !== -1;
12487     },
12488
12489     /**
12490      * Selects nodes.
12491      * @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
12492      * @param {Boolean} keepExisting (optional) true to keep existing selections
12493      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12494      */
12495     select : function(nodeInfo, keepExisting, suppressEvent){
12496         if(nodeInfo instanceof Array){
12497             if(!keepExisting){
12498                 this.clearSelections(true);
12499             }
12500             for(var i = 0, len = nodeInfo.length; i < len; i++){
12501                 this.select(nodeInfo[i], true, true);
12502             }
12503             return;
12504         } 
12505         var node = this.getNode(nodeInfo);
12506         if(!node || this.isSelected(node)){
12507             return; // already selected.
12508         }
12509         if(!keepExisting){
12510             this.clearSelections(true);
12511         }
12512         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
12513             Roo.fly(node).addClass(this.selectedClass);
12514             this.selections.push(node);
12515             if(!suppressEvent){
12516                 this.fireEvent("selectionchange", this, this.selections);
12517             }
12518         }
12519         
12520         
12521     },
12522       /**
12523      * Unselects nodes.
12524      * @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
12525      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
12526      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12527      */
12528     unselect : function(nodeInfo, keepExisting, suppressEvent)
12529     {
12530         if(nodeInfo instanceof Array){
12531             Roo.each(this.selections, function(s) {
12532                 this.unselect(s, nodeInfo);
12533             }, this);
12534             return;
12535         }
12536         var node = this.getNode(nodeInfo);
12537         if(!node || !this.isSelected(node)){
12538             Roo.log("not selected");
12539             return; // not selected.
12540         }
12541         // fireevent???
12542         var ns = [];
12543         Roo.each(this.selections, function(s) {
12544             if (s == node ) {
12545                 Roo.fly(node).removeClass(this.selectedClass);
12546
12547                 return;
12548             }
12549             ns.push(s);
12550         },this);
12551         
12552         this.selections= ns;
12553         this.fireEvent("selectionchange", this, this.selections);
12554     },
12555
12556     /**
12557      * Gets a template node.
12558      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12559      * @return {HTMLElement} The node or null if it wasn't found
12560      */
12561     getNode : function(nodeInfo){
12562         if(typeof nodeInfo == "string"){
12563             return document.getElementById(nodeInfo);
12564         }else if(typeof nodeInfo == "number"){
12565             return this.nodes[nodeInfo];
12566         }
12567         return nodeInfo;
12568     },
12569
12570     /**
12571      * Gets a range template nodes.
12572      * @param {Number} startIndex
12573      * @param {Number} endIndex
12574      * @return {Array} An array of nodes
12575      */
12576     getNodes : function(start, end){
12577         var ns = this.nodes;
12578         start = start || 0;
12579         end = typeof end == "undefined" ? ns.length - 1 : end;
12580         var nodes = [];
12581         if(start <= end){
12582             for(var i = start; i <= end; i++){
12583                 nodes.push(ns[i]);
12584             }
12585         } else{
12586             for(var i = start; i >= end; i--){
12587                 nodes.push(ns[i]);
12588             }
12589         }
12590         return nodes;
12591     },
12592
12593     /**
12594      * Finds the index of the passed node
12595      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12596      * @return {Number} The index of the node or -1
12597      */
12598     indexOf : function(node){
12599         node = this.getNode(node);
12600         if(typeof node.nodeIndex == "number"){
12601             return node.nodeIndex;
12602         }
12603         var ns = this.nodes;
12604         for(var i = 0, len = ns.length; i < len; i++){
12605             if(ns[i] == node){
12606                 return i;
12607             }
12608         }
12609         return -1;
12610     }
12611 });
12612 /*
12613  * - LGPL
12614  *
12615  * based on jquery fullcalendar
12616  * 
12617  */
12618
12619 Roo.bootstrap = Roo.bootstrap || {};
12620 /**
12621  * @class Roo.bootstrap.Calendar
12622  * @extends Roo.bootstrap.Component
12623  * Bootstrap Calendar class
12624  * @cfg {Boolean} loadMask (true|false) default false
12625  * @cfg {Object} header generate the user specific header of the calendar, default false
12626
12627  * @constructor
12628  * Create a new Container
12629  * @param {Object} config The config object
12630  */
12631
12632
12633
12634 Roo.bootstrap.Calendar = function(config){
12635     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
12636      this.addEvents({
12637         /**
12638              * @event select
12639              * Fires when a date is selected
12640              * @param {DatePicker} this
12641              * @param {Date} date The selected date
12642              */
12643         'select': true,
12644         /**
12645              * @event monthchange
12646              * Fires when the displayed month changes 
12647              * @param {DatePicker} this
12648              * @param {Date} date The selected month
12649              */
12650         'monthchange': true,
12651         /**
12652              * @event evententer
12653              * Fires when mouse over an event
12654              * @param {Calendar} this
12655              * @param {event} Event
12656              */
12657         'evententer': true,
12658         /**
12659              * @event eventleave
12660              * Fires when the mouse leaves an
12661              * @param {Calendar} this
12662              * @param {event}
12663              */
12664         'eventleave': true,
12665         /**
12666              * @event eventclick
12667              * Fires when the mouse click an
12668              * @param {Calendar} this
12669              * @param {event}
12670              */
12671         'eventclick': true
12672         
12673     });
12674
12675 };
12676
12677 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
12678     
12679      /**
12680      * @cfg {Number} startDay
12681      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
12682      */
12683     startDay : 0,
12684     
12685     loadMask : false,
12686     
12687     header : false,
12688       
12689     getAutoCreate : function(){
12690         
12691         
12692         var fc_button = function(name, corner, style, content ) {
12693             return Roo.apply({},{
12694                 tag : 'span',
12695                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
12696                          (corner.length ?
12697                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
12698                             ''
12699                         ),
12700                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
12701                 unselectable: 'on'
12702             });
12703         };
12704         
12705         var header = {};
12706         
12707         if(!this.header){
12708             header = {
12709                 tag : 'table',
12710                 cls : 'fc-header',
12711                 style : 'width:100%',
12712                 cn : [
12713                     {
12714                         tag: 'tr',
12715                         cn : [
12716                             {
12717                                 tag : 'td',
12718                                 cls : 'fc-header-left',
12719                                 cn : [
12720                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
12721                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
12722                                     { tag: 'span', cls: 'fc-header-space' },
12723                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
12724
12725
12726                                 ]
12727                             },
12728
12729                             {
12730                                 tag : 'td',
12731                                 cls : 'fc-header-center',
12732                                 cn : [
12733                                     {
12734                                         tag: 'span',
12735                                         cls: 'fc-header-title',
12736                                         cn : {
12737                                             tag: 'H2',
12738                                             html : 'month / year'
12739                                         }
12740                                     }
12741
12742                                 ]
12743                             },
12744                             {
12745                                 tag : 'td',
12746                                 cls : 'fc-header-right',
12747                                 cn : [
12748                               /*      fc_button('month', 'left', '', 'month' ),
12749                                     fc_button('week', '', '', 'week' ),
12750                                     fc_button('day', 'right', '', 'day' )
12751                                 */    
12752
12753                                 ]
12754                             }
12755
12756                         ]
12757                     }
12758                 ]
12759             };
12760         }
12761         
12762         header = this.header;
12763         
12764        
12765         var cal_heads = function() {
12766             var ret = [];
12767             // fixme - handle this.
12768             
12769             for (var i =0; i < Date.dayNames.length; i++) {
12770                 var d = Date.dayNames[i];
12771                 ret.push({
12772                     tag: 'th',
12773                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
12774                     html : d.substring(0,3)
12775                 });
12776                 
12777             }
12778             ret[0].cls += ' fc-first';
12779             ret[6].cls += ' fc-last';
12780             return ret;
12781         };
12782         var cal_cell = function(n) {
12783             return  {
12784                 tag: 'td',
12785                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
12786                 cn : [
12787                     {
12788                         cn : [
12789                             {
12790                                 cls: 'fc-day-number',
12791                                 html: 'D'
12792                             },
12793                             {
12794                                 cls: 'fc-day-content',
12795                              
12796                                 cn : [
12797                                      {
12798                                         style: 'position: relative;' // height: 17px;
12799                                     }
12800                                 ]
12801                             }
12802                             
12803                             
12804                         ]
12805                     }
12806                 ]
12807                 
12808             }
12809         };
12810         var cal_rows = function() {
12811             
12812             var ret = []
12813             for (var r = 0; r < 6; r++) {
12814                 var row= {
12815                     tag : 'tr',
12816                     cls : 'fc-week',
12817                     cn : []
12818                 };
12819                 
12820                 for (var i =0; i < Date.dayNames.length; i++) {
12821                     var d = Date.dayNames[i];
12822                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
12823
12824                 }
12825                 row.cn[0].cls+=' fc-first';
12826                 row.cn[0].cn[0].style = 'min-height:90px';
12827                 row.cn[6].cls+=' fc-last';
12828                 ret.push(row);
12829                 
12830             }
12831             ret[0].cls += ' fc-first';
12832             ret[4].cls += ' fc-prev-last';
12833             ret[5].cls += ' fc-last';
12834             return ret;
12835             
12836         };
12837         
12838         var cal_table = {
12839             tag: 'table',
12840             cls: 'fc-border-separate',
12841             style : 'width:100%',
12842             cellspacing  : 0,
12843             cn : [
12844                 { 
12845                     tag: 'thead',
12846                     cn : [
12847                         { 
12848                             tag: 'tr',
12849                             cls : 'fc-first fc-last',
12850                             cn : cal_heads()
12851                         }
12852                     ]
12853                 },
12854                 { 
12855                     tag: 'tbody',
12856                     cn : cal_rows()
12857                 }
12858                   
12859             ]
12860         };
12861          
12862          var cfg = {
12863             cls : 'fc fc-ltr',
12864             cn : [
12865                 header,
12866                 {
12867                     cls : 'fc-content',
12868                     style : "position: relative;",
12869                     cn : [
12870                         {
12871                             cls : 'fc-view fc-view-month fc-grid',
12872                             style : 'position: relative',
12873                             unselectable : 'on',
12874                             cn : [
12875                                 {
12876                                     cls : 'fc-event-container',
12877                                     style : 'position:absolute;z-index:8;top:0;left:0;'
12878                                 },
12879                                 cal_table
12880                             ]
12881                         }
12882                     ]
12883     
12884                 }
12885            ] 
12886             
12887         };
12888         
12889          
12890         
12891         return cfg;
12892     },
12893     
12894     
12895     initEvents : function()
12896     {
12897         if(!this.store){
12898             throw "can not find store for calendar";
12899         }
12900         
12901         var mark = {
12902             tag: "div",
12903             cls:"x-dlg-mask",
12904             style: "text-align:center",
12905             cn: [
12906                 {
12907                     tag: "div",
12908                     style: "background-color:white;width:50%;margin:250 auto",
12909                     cn: [
12910                         {
12911                             tag: "img",
12912                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
12913                         },
12914                         {
12915                             tag: "span",
12916                             html: "Loading"
12917                         }
12918                         
12919                     ]
12920                 }
12921             ]
12922         }
12923         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
12924         
12925         var size = this.el.select('.fc-content', true).first().getSize();
12926         this.maskEl.setSize(size.width, size.height);
12927         this.maskEl.enableDisplayMode("block");
12928         if(!this.loadMask){
12929             this.maskEl.hide();
12930         }
12931         
12932         this.store = Roo.factory(this.store, Roo.data);
12933         this.store.on('load', this.onLoad, this);
12934         this.store.on('beforeload', this.onBeforeLoad, this);
12935         
12936         this.resize();
12937         
12938         this.cells = this.el.select('.fc-day',true);
12939         //Roo.log(this.cells);
12940         this.textNodes = this.el.query('.fc-day-number');
12941         this.cells.addClassOnOver('fc-state-hover');
12942         
12943         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
12944         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
12945         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
12946         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
12947         
12948         this.on('monthchange', this.onMonthChange, this);
12949         
12950         this.update(new Date().clearTime());
12951     },
12952     
12953     resize : function() {
12954         var sz  = this.el.getSize();
12955         
12956         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
12957         this.el.select('.fc-day-content div',true).setHeight(34);
12958     },
12959     
12960     
12961     // private
12962     showPrevMonth : function(e){
12963         this.update(this.activeDate.add("mo", -1));
12964     },
12965     showToday : function(e){
12966         this.update(new Date().clearTime());
12967     },
12968     // private
12969     showNextMonth : function(e){
12970         this.update(this.activeDate.add("mo", 1));
12971     },
12972
12973     // private
12974     showPrevYear : function(){
12975         this.update(this.activeDate.add("y", -1));
12976     },
12977
12978     // private
12979     showNextYear : function(){
12980         this.update(this.activeDate.add("y", 1));
12981     },
12982
12983     
12984    // private
12985     update : function(date)
12986     {
12987         var vd = this.activeDate;
12988         this.activeDate = date;
12989 //        if(vd && this.el){
12990 //            var t = date.getTime();
12991 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
12992 //                Roo.log('using add remove');
12993 //                
12994 //                this.fireEvent('monthchange', this, date);
12995 //                
12996 //                this.cells.removeClass("fc-state-highlight");
12997 //                this.cells.each(function(c){
12998 //                   if(c.dateValue == t){
12999 //                       c.addClass("fc-state-highlight");
13000 //                       setTimeout(function(){
13001 //                            try{c.dom.firstChild.focus();}catch(e){}
13002 //                       }, 50);
13003 //                       return false;
13004 //                   }
13005 //                   return true;
13006 //                });
13007 //                return;
13008 //            }
13009 //        }
13010         
13011         var days = date.getDaysInMonth();
13012         
13013         var firstOfMonth = date.getFirstDateOfMonth();
13014         var startingPos = firstOfMonth.getDay()-this.startDay;
13015         
13016         if(startingPos < this.startDay){
13017             startingPos += 7;
13018         }
13019         
13020         var pm = date.add(Date.MONTH, -1);
13021         var prevStart = pm.getDaysInMonth()-startingPos;
13022 //        
13023         this.cells = this.el.select('.fc-day',true);
13024         this.textNodes = this.el.query('.fc-day-number');
13025         this.cells.addClassOnOver('fc-state-hover');
13026         
13027         var cells = this.cells.elements;
13028         var textEls = this.textNodes;
13029         
13030         Roo.each(cells, function(cell){
13031             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
13032         });
13033         
13034         days += startingPos;
13035
13036         // convert everything to numbers so it's fast
13037         var day = 86400000;
13038         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
13039         //Roo.log(d);
13040         //Roo.log(pm);
13041         //Roo.log(prevStart);
13042         
13043         var today = new Date().clearTime().getTime();
13044         var sel = date.clearTime().getTime();
13045         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
13046         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
13047         var ddMatch = this.disabledDatesRE;
13048         var ddText = this.disabledDatesText;
13049         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
13050         var ddaysText = this.disabledDaysText;
13051         var format = this.format;
13052         
13053         var setCellClass = function(cal, cell){
13054             cell.row = 0;
13055             cell.events = [];
13056             cell.more = [];
13057             //Roo.log('set Cell Class');
13058             cell.title = "";
13059             var t = d.getTime();
13060             
13061             //Roo.log(d);
13062             
13063             cell.dateValue = t;
13064             if(t == today){
13065                 cell.className += " fc-today";
13066                 cell.className += " fc-state-highlight";
13067                 cell.title = cal.todayText;
13068             }
13069             if(t == sel){
13070                 // disable highlight in other month..
13071                 //cell.className += " fc-state-highlight";
13072                 
13073             }
13074             // disabling
13075             if(t < min) {
13076                 cell.className = " fc-state-disabled";
13077                 cell.title = cal.minText;
13078                 return;
13079             }
13080             if(t > max) {
13081                 cell.className = " fc-state-disabled";
13082                 cell.title = cal.maxText;
13083                 return;
13084             }
13085             if(ddays){
13086                 if(ddays.indexOf(d.getDay()) != -1){
13087                     cell.title = ddaysText;
13088                     cell.className = " fc-state-disabled";
13089                 }
13090             }
13091             if(ddMatch && format){
13092                 var fvalue = d.dateFormat(format);
13093                 if(ddMatch.test(fvalue)){
13094                     cell.title = ddText.replace("%0", fvalue);
13095                     cell.className = " fc-state-disabled";
13096                 }
13097             }
13098             
13099             if (!cell.initialClassName) {
13100                 cell.initialClassName = cell.dom.className;
13101             }
13102             
13103             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
13104         };
13105
13106         var i = 0;
13107         
13108         for(; i < startingPos; i++) {
13109             textEls[i].innerHTML = (++prevStart);
13110             d.setDate(d.getDate()+1);
13111             
13112             cells[i].className = "fc-past fc-other-month";
13113             setCellClass(this, cells[i]);
13114         }
13115         
13116         var intDay = 0;
13117         
13118         for(; i < days; i++){
13119             intDay = i - startingPos + 1;
13120             textEls[i].innerHTML = (intDay);
13121             d.setDate(d.getDate()+1);
13122             
13123             cells[i].className = ''; // "x-date-active";
13124             setCellClass(this, cells[i]);
13125         }
13126         var extraDays = 0;
13127         
13128         for(; i < 42; i++) {
13129             textEls[i].innerHTML = (++extraDays);
13130             d.setDate(d.getDate()+1);
13131             
13132             cells[i].className = "fc-future fc-other-month";
13133             setCellClass(this, cells[i]);
13134         }
13135         
13136         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
13137         
13138         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
13139         
13140         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
13141         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
13142         
13143         if(totalRows != 6){
13144             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
13145             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
13146         }
13147         
13148         this.fireEvent('monthchange', this, date);
13149         
13150         
13151         /*
13152         if(!this.internalRender){
13153             var main = this.el.dom.firstChild;
13154             var w = main.offsetWidth;
13155             this.el.setWidth(w + this.el.getBorderWidth("lr"));
13156             Roo.fly(main).setWidth(w);
13157             this.internalRender = true;
13158             // opera does not respect the auto grow header center column
13159             // then, after it gets a width opera refuses to recalculate
13160             // without a second pass
13161             if(Roo.isOpera && !this.secondPass){
13162                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
13163                 this.secondPass = true;
13164                 this.update.defer(10, this, [date]);
13165             }
13166         }
13167         */
13168         
13169     },
13170     
13171     findCell : function(dt) {
13172         dt = dt.clearTime().getTime();
13173         var ret = false;
13174         this.cells.each(function(c){
13175             //Roo.log("check " +c.dateValue + '?=' + dt);
13176             if(c.dateValue == dt){
13177                 ret = c;
13178                 return false;
13179             }
13180             return true;
13181         });
13182         
13183         return ret;
13184     },
13185     
13186     findCells : function(ev) {
13187         var s = ev.start.clone().clearTime().getTime();
13188        // Roo.log(s);
13189         var e= ev.end.clone().clearTime().getTime();
13190        // Roo.log(e);
13191         var ret = [];
13192         this.cells.each(function(c){
13193              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
13194             
13195             if(c.dateValue > e){
13196                 return ;
13197             }
13198             if(c.dateValue < s){
13199                 return ;
13200             }
13201             ret.push(c);
13202         });
13203         
13204         return ret;    
13205     },
13206     
13207 //    findBestRow: function(cells)
13208 //    {
13209 //        var ret = 0;
13210 //        
13211 //        for (var i =0 ; i < cells.length;i++) {
13212 //            ret  = Math.max(cells[i].rows || 0,ret);
13213 //        }
13214 //        return ret;
13215 //        
13216 //    },
13217     
13218     
13219     addItem : function(ev)
13220     {
13221         // look for vertical location slot in
13222         var cells = this.findCells(ev);
13223         
13224 //        ev.row = this.findBestRow(cells);
13225         
13226         // work out the location.
13227         
13228         var crow = false;
13229         var rows = [];
13230         for(var i =0; i < cells.length; i++) {
13231             
13232             cells[i].row = cells[0].row;
13233             
13234             if(i == 0){
13235                 cells[i].row = cells[i].row + 1;
13236             }
13237             
13238             if (!crow) {
13239                 crow = {
13240                     start : cells[i],
13241                     end :  cells[i]
13242                 };
13243                 continue;
13244             }
13245             if (crow.start.getY() == cells[i].getY()) {
13246                 // on same row.
13247                 crow.end = cells[i];
13248                 continue;
13249             }
13250             // different row.
13251             rows.push(crow);
13252             crow = {
13253                 start: cells[i],
13254                 end : cells[i]
13255             };
13256             
13257         }
13258         
13259         rows.push(crow);
13260         ev.els = [];
13261         ev.rows = rows;
13262         ev.cells = cells;
13263         
13264         cells[0].events.push(ev);
13265         
13266         this.calevents.push(ev);
13267     },
13268     
13269     clearEvents: function() {
13270         
13271         if(!this.calevents){
13272             return;
13273         }
13274         
13275         Roo.each(this.cells.elements, function(c){
13276             c.row = 0;
13277             c.events = [];
13278             c.more = [];
13279         });
13280         
13281         Roo.each(this.calevents, function(e) {
13282             Roo.each(e.els, function(el) {
13283                 el.un('mouseenter' ,this.onEventEnter, this);
13284                 el.un('mouseleave' ,this.onEventLeave, this);
13285                 el.remove();
13286             },this);
13287         },this);
13288         
13289         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
13290             e.remove();
13291         });
13292         
13293     },
13294     
13295     renderEvents: function()
13296     {   
13297         var _this = this;
13298         
13299         this.cells.each(function(c) {
13300             
13301             if(c.row < 5){
13302                 return;
13303             }
13304             
13305             var ev = c.events;
13306             
13307             var r = 4;
13308             if(c.row != c.events.length){
13309                 r = 4 - (4 - (c.row - c.events.length));
13310             }
13311             
13312             c.events = ev.slice(0, r);
13313             c.more = ev.slice(r);
13314             
13315             if(c.more.length && c.more.length == 1){
13316                 c.events.push(c.more.pop());
13317             }
13318             
13319             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
13320             
13321         });
13322             
13323         this.cells.each(function(c) {
13324             
13325             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
13326             
13327             
13328             for (var e = 0; e < c.events.length; e++){
13329                 var ev = c.events[e];
13330                 var rows = ev.rows;
13331                 
13332                 for(var i = 0; i < rows.length; i++) {
13333                 
13334                     // how many rows should it span..
13335
13336                     var  cfg = {
13337                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
13338                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
13339
13340                         unselectable : "on",
13341                         cn : [
13342                             {
13343                                 cls: 'fc-event-inner',
13344                                 cn : [
13345     //                                {
13346     //                                  tag:'span',
13347     //                                  cls: 'fc-event-time',
13348     //                                  html : cells.length > 1 ? '' : ev.time
13349     //                                },
13350                                     {
13351                                       tag:'span',
13352                                       cls: 'fc-event-title',
13353                                       html : String.format('{0}', ev.title)
13354                                     }
13355
13356
13357                                 ]
13358                             },
13359                             {
13360                                 cls: 'ui-resizable-handle ui-resizable-e',
13361                                 html : '&nbsp;&nbsp;&nbsp'
13362                             }
13363
13364                         ]
13365                     };
13366
13367                     if (i == 0) {
13368                         cfg.cls += ' fc-event-start';
13369                     }
13370                     if ((i+1) == rows.length) {
13371                         cfg.cls += ' fc-event-end';
13372                     }
13373
13374                     var ctr = _this.el.select('.fc-event-container',true).first();
13375                     var cg = ctr.createChild(cfg);
13376
13377                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
13378                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
13379
13380                     var r = (c.more.length) ? 1 : 0;
13381                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
13382                     cg.setWidth(ebox.right - sbox.x -2);
13383
13384                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
13385                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
13386                     cg.on('click', _this.onEventClick, _this, ev);
13387
13388                     ev.els.push(cg);
13389                     
13390                 }
13391                 
13392             }
13393             
13394             
13395             if(c.more.length){
13396                 var  cfg = {
13397                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
13398                     style : 'position: absolute',
13399                     unselectable : "on",
13400                     cn : [
13401                         {
13402                             cls: 'fc-event-inner',
13403                             cn : [
13404                                 {
13405                                   tag:'span',
13406                                   cls: 'fc-event-title',
13407                                   html : 'More'
13408                                 }
13409
13410
13411                             ]
13412                         },
13413                         {
13414                             cls: 'ui-resizable-handle ui-resizable-e',
13415                             html : '&nbsp;&nbsp;&nbsp'
13416                         }
13417
13418                     ]
13419                 };
13420
13421                 var ctr = _this.el.select('.fc-event-container',true).first();
13422                 var cg = ctr.createChild(cfg);
13423
13424                 var sbox = c.select('.fc-day-content',true).first().getBox();
13425                 var ebox = c.select('.fc-day-content',true).first().getBox();
13426                 //Roo.log(cg);
13427                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
13428                 cg.setWidth(ebox.right - sbox.x -2);
13429
13430                 cg.on('click', _this.onMoreEventClick, _this, c.more);
13431                 
13432             }
13433             
13434         });
13435         
13436         
13437         
13438     },
13439     
13440     onEventEnter: function (e, el,event,d) {
13441         this.fireEvent('evententer', this, el, event);
13442     },
13443     
13444     onEventLeave: function (e, el,event,d) {
13445         this.fireEvent('eventleave', this, el, event);
13446     },
13447     
13448     onEventClick: function (e, el,event,d) {
13449         this.fireEvent('eventclick', this, el, event);
13450     },
13451     
13452     onMonthChange: function () {
13453         this.store.load();
13454     },
13455     
13456     onMoreEventClick: function(e, el, more)
13457     {
13458         var _this = this;
13459         
13460         this.calpopover.placement = 'right';
13461         this.calpopover.setTitle('More');
13462         
13463         this.calpopover.setContent('');
13464         
13465         var ctr = this.calpopover.el.select('.popover-content', true).first();
13466         
13467         Roo.each(more, function(m){
13468             var cfg = {
13469                 cls : 'fc-event-hori fc-event-draggable',
13470                 html : m.title
13471             }
13472             var cg = ctr.createChild(cfg);
13473             
13474             cg.on('click', _this.onEventClick, _this, m);
13475         });
13476         
13477         this.calpopover.show(el);
13478         
13479         
13480     },
13481     
13482     onLoad: function () 
13483     {   
13484         this.calevents = [];
13485         var cal = this;
13486         
13487         if(this.store.getCount() > 0){
13488             this.store.data.each(function(d){
13489                cal.addItem({
13490                     id : d.data.id,
13491                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
13492                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
13493                     time : d.data.start_time,
13494                     title : d.data.title,
13495                     description : d.data.description,
13496                     venue : d.data.venue
13497                 });
13498             });
13499         }
13500         
13501         this.renderEvents();
13502         
13503         if(this.calevents.length && this.loadMask){
13504             this.maskEl.hide();
13505         }
13506     },
13507     
13508     onBeforeLoad: function()
13509     {
13510         this.clearEvents();
13511         if(this.loadMask){
13512             this.maskEl.show();
13513         }
13514     }
13515 });
13516
13517  
13518  /*
13519  * - LGPL
13520  *
13521  * element
13522  * 
13523  */
13524
13525 /**
13526  * @class Roo.bootstrap.Popover
13527  * @extends Roo.bootstrap.Component
13528  * Bootstrap Popover class
13529  * @cfg {String} html contents of the popover   (or false to use children..)
13530  * @cfg {String} title of popover (or false to hide)
13531  * @cfg {String} placement how it is placed
13532  * @cfg {String} trigger click || hover (or false to trigger manually)
13533  * @cfg {String} over what (parent or false to trigger manually.)
13534  * 
13535  * @constructor
13536  * Create a new Popover
13537  * @param {Object} config The config object
13538  */
13539
13540 Roo.bootstrap.Popover = function(config){
13541     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
13542 };
13543
13544 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
13545     
13546     title: 'Fill in a title',
13547     html: false,
13548     
13549     placement : 'right',
13550     trigger : 'hover', // hover
13551     
13552     over: 'parent',
13553     
13554     can_build_overlaid : false,
13555     
13556     getChildContainer : function()
13557     {
13558         return this.el.select('.popover-content',true).first();
13559     },
13560     
13561     getAutoCreate : function(){
13562          Roo.log('make popover?');
13563         var cfg = {
13564            cls : 'popover roo-dynamic',
13565            style: 'display:block',
13566            cn : [
13567                 {
13568                     cls : 'arrow'
13569                 },
13570                 {
13571                     cls : 'popover-inner',
13572                     cn : [
13573                         {
13574                             tag: 'h3',
13575                             cls: 'popover-title',
13576                             html : this.title
13577                         },
13578                         {
13579                             cls : 'popover-content',
13580                             html : this.html
13581                         }
13582                     ]
13583                     
13584                 }
13585            ]
13586         };
13587         
13588         return cfg;
13589     },
13590     setTitle: function(str)
13591     {
13592         this.el.select('.popover-title',true).first().dom.innerHTML = str;
13593     },
13594     setContent: function(str)
13595     {
13596         this.el.select('.popover-content',true).first().dom.innerHTML = str;
13597     },
13598     // as it get's added to the bottom of the page.
13599     onRender : function(ct, position)
13600     {
13601         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
13602         if(!this.el){
13603             var cfg = Roo.apply({},  this.getAutoCreate());
13604             cfg.id = Roo.id();
13605             
13606             if (this.cls) {
13607                 cfg.cls += ' ' + this.cls;
13608             }
13609             if (this.style) {
13610                 cfg.style = this.style;
13611             }
13612             Roo.log("adding to ")
13613             this.el = Roo.get(document.body).createChild(cfg, position);
13614             Roo.log(this.el);
13615         }
13616         this.initEvents();
13617     },
13618     
13619     initEvents : function()
13620     {
13621         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
13622         this.el.enableDisplayMode('block');
13623         this.el.hide();
13624         if (this.over === false) {
13625             return; 
13626         }
13627         if (this.triggers === false) {
13628             return;
13629         }
13630         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13631         var triggers = this.trigger ? this.trigger.split(' ') : [];
13632         Roo.each(triggers, function(trigger) {
13633         
13634             if (trigger == 'click') {
13635                 on_el.on('click', this.toggle, this);
13636             } else if (trigger != 'manual') {
13637                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
13638                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
13639       
13640                 on_el.on(eventIn  ,this.enter, this);
13641                 on_el.on(eventOut, this.leave, this);
13642             }
13643         }, this);
13644         
13645     },
13646     
13647     
13648     // private
13649     timeout : null,
13650     hoverState : null,
13651     
13652     toggle : function () {
13653         this.hoverState == 'in' ? this.leave() : this.enter();
13654     },
13655     
13656     enter : function () {
13657        
13658     
13659         clearTimeout(this.timeout);
13660     
13661         this.hoverState = 'in'
13662     
13663         if (!this.delay || !this.delay.show) {
13664             this.show();
13665             return 
13666         }
13667         var _t = this;
13668         this.timeout = setTimeout(function () {
13669             if (_t.hoverState == 'in') {
13670                 _t.show();
13671             }
13672         }, this.delay.show)
13673     },
13674     leave : function() {
13675         clearTimeout(this.timeout);
13676     
13677         this.hoverState = 'out'
13678     
13679         if (!this.delay || !this.delay.hide) {
13680             this.hide();
13681             return 
13682         }
13683         var _t = this;
13684         this.timeout = setTimeout(function () {
13685             if (_t.hoverState == 'out') {
13686                 _t.hide();
13687             }
13688         }, this.delay.hide)
13689     },
13690     
13691     show : function (on_el)
13692     {
13693         if (!on_el) {
13694             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13695         }
13696         // set content.
13697         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
13698         if (this.html !== false) {
13699             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
13700         }
13701         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
13702         if (!this.title.length) {
13703             this.el.select('.popover-title',true).hide();
13704         }
13705         
13706         var placement = typeof this.placement == 'function' ?
13707             this.placement.call(this, this.el, on_el) :
13708             this.placement;
13709             
13710         var autoToken = /\s?auto?\s?/i;
13711         var autoPlace = autoToken.test(placement);
13712         if (autoPlace) {
13713             placement = placement.replace(autoToken, '') || 'top';
13714         }
13715         
13716         //this.el.detach()
13717         //this.el.setXY([0,0]);
13718         this.el.show();
13719         this.el.dom.style.display='block';
13720         this.el.addClass(placement);
13721         
13722         //this.el.appendTo(on_el);
13723         
13724         var p = this.getPosition();
13725         var box = this.el.getBox();
13726         
13727         if (autoPlace) {
13728             // fixme..
13729         }
13730         var align = Roo.bootstrap.Popover.alignment[placement]
13731         this.el.alignTo(on_el, align[0],align[1]);
13732         //var arrow = this.el.select('.arrow',true).first();
13733         //arrow.set(align[2], 
13734         
13735         this.el.addClass('in');
13736         this.hoverState = null;
13737         
13738         if (this.el.hasClass('fade')) {
13739             // fade it?
13740         }
13741         
13742     },
13743     hide : function()
13744     {
13745         this.el.setXY([0,0]);
13746         this.el.removeClass('in');
13747         this.el.hide();
13748         
13749     }
13750     
13751 });
13752
13753 Roo.bootstrap.Popover.alignment = {
13754     'left' : ['r-l', [-10,0], 'right'],
13755     'right' : ['l-r', [10,0], 'left'],
13756     'bottom' : ['t-b', [0,10], 'top'],
13757     'top' : [ 'b-t', [0,-10], 'bottom']
13758 };
13759
13760  /*
13761  * - LGPL
13762  *
13763  * Progress
13764  * 
13765  */
13766
13767 /**
13768  * @class Roo.bootstrap.Progress
13769  * @extends Roo.bootstrap.Component
13770  * Bootstrap Progress class
13771  * @cfg {Boolean} striped striped of the progress bar
13772  * @cfg {Boolean} active animated of the progress bar
13773  * 
13774  * 
13775  * @constructor
13776  * Create a new Progress
13777  * @param {Object} config The config object
13778  */
13779
13780 Roo.bootstrap.Progress = function(config){
13781     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
13782 };
13783
13784 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
13785     
13786     striped : false,
13787     active: false,
13788     
13789     getAutoCreate : function(){
13790         var cfg = {
13791             tag: 'div',
13792             cls: 'progress'
13793         };
13794         
13795         
13796         if(this.striped){
13797             cfg.cls += ' progress-striped';
13798         }
13799       
13800         if(this.active){
13801             cfg.cls += ' active';
13802         }
13803         
13804         
13805         return cfg;
13806     }
13807    
13808 });
13809
13810  
13811
13812  /*
13813  * - LGPL
13814  *
13815  * ProgressBar
13816  * 
13817  */
13818
13819 /**
13820  * @class Roo.bootstrap.ProgressBar
13821  * @extends Roo.bootstrap.Component
13822  * Bootstrap ProgressBar class
13823  * @cfg {Number} aria_valuenow aria-value now
13824  * @cfg {Number} aria_valuemin aria-value min
13825  * @cfg {Number} aria_valuemax aria-value max
13826  * @cfg {String} label label for the progress bar
13827  * @cfg {String} panel (success | info | warning | danger )
13828  * @cfg {String} role role of the progress bar
13829  * @cfg {String} sr_only text
13830  * 
13831  * 
13832  * @constructor
13833  * Create a new ProgressBar
13834  * @param {Object} config The config object
13835  */
13836
13837 Roo.bootstrap.ProgressBar = function(config){
13838     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
13839 };
13840
13841 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
13842     
13843     aria_valuenow : 0,
13844     aria_valuemin : 0,
13845     aria_valuemax : 100,
13846     label : false,
13847     panel : false,
13848     role : false,
13849     sr_only: false,
13850     
13851     getAutoCreate : function()
13852     {
13853         
13854         var cfg = {
13855             tag: 'div',
13856             cls: 'progress-bar',
13857             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
13858         };
13859         
13860         if(this.sr_only){
13861             cfg.cn = {
13862                 tag: 'span',
13863                 cls: 'sr-only',
13864                 html: this.sr_only
13865             }
13866         }
13867         
13868         if(this.role){
13869             cfg.role = this.role;
13870         }
13871         
13872         if(this.aria_valuenow){
13873             cfg['aria-valuenow'] = this.aria_valuenow;
13874         }
13875         
13876         if(this.aria_valuemin){
13877             cfg['aria-valuemin'] = this.aria_valuemin;
13878         }
13879         
13880         if(this.aria_valuemax){
13881             cfg['aria-valuemax'] = this.aria_valuemax;
13882         }
13883         
13884         if(this.label && !this.sr_only){
13885             cfg.html = this.label;
13886         }
13887         
13888         if(this.panel){
13889             cfg.cls += ' progress-bar-' + this.panel;
13890         }
13891         
13892         return cfg;
13893     },
13894     
13895     update : function(aria_valuenow)
13896     {
13897         this.aria_valuenow = aria_valuenow;
13898         
13899         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
13900     }
13901    
13902 });
13903
13904  
13905
13906  /*
13907  * - LGPL
13908  *
13909  * column
13910  * 
13911  */
13912
13913 /**
13914  * @class Roo.bootstrap.TabGroup
13915  * @extends Roo.bootstrap.Column
13916  * Bootstrap Column class
13917  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
13918  * @cfg {Boolean} carousel true to make the group behave like a carousel
13919  * 
13920  * @constructor
13921  * Create a new TabGroup
13922  * @param {Object} config The config object
13923  */
13924
13925 Roo.bootstrap.TabGroup = function(config){
13926     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
13927     if (!this.navId) {
13928         this.navId = Roo.id();
13929     }
13930     this.tabs = [];
13931     Roo.bootstrap.TabGroup.register(this);
13932     
13933 };
13934
13935 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
13936     
13937     carousel : false,
13938     transition : false,
13939      
13940     getAutoCreate : function()
13941     {
13942         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
13943         
13944         cfg.cls += ' tab-content';
13945         
13946         if (this.carousel) {
13947             cfg.cls += ' carousel slide';
13948             cfg.cn = [{
13949                cls : 'carousel-inner'
13950             }]
13951         }
13952         
13953         
13954         return cfg;
13955     },
13956     getChildContainer : function()
13957     {
13958         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
13959     },
13960     
13961     /**
13962     * register a Navigation item
13963     * @param {Roo.bootstrap.NavItem} the navitem to add
13964     */
13965     register : function(item)
13966     {
13967         this.tabs.push( item);
13968         item.navId = this.navId; // not really needed..
13969     
13970     },
13971     
13972     getActivePanel : function()
13973     {
13974         var r = false;
13975         Roo.each(this.tabs, function(t) {
13976             if (t.active) {
13977                 r = t;
13978                 return false;
13979             }
13980             return null;
13981         });
13982         return r;
13983         
13984     },
13985     getPanelByName : function(n)
13986     {
13987         var r = false;
13988         Roo.each(this.tabs, function(t) {
13989             if (t.tabId == n) {
13990                 r = t;
13991                 return false;
13992             }
13993             return null;
13994         });
13995         return r;
13996     },
13997     indexOfPanel : function(p)
13998     {
13999         var r = false;
14000         Roo.each(this.tabs, function(t,i) {
14001             if (t.tabId == p.tabId) {
14002                 r = i;
14003                 return false;
14004             }
14005             return null;
14006         });
14007         return r;
14008     },
14009     /**
14010      * show a specific panel
14011      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
14012      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
14013      */
14014     showPanel : function (pan)
14015     {
14016         
14017         if (typeof(pan) == 'number') {
14018             pan = this.tabs[pan];
14019         }
14020         if (typeof(pan) == 'string') {
14021             pan = this.getPanelByName(pan);
14022         }
14023         if (pan.tabId == this.getActivePanel().tabId) {
14024             return true;
14025         }
14026         var cur = this.getActivePanel();
14027         
14028         if (false === cur.fireEvent('beforedeactivate')) {
14029             return false;
14030         }
14031         
14032         if (this.carousel) {
14033             this.transition = true;
14034             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
14035             var lr = dir == 'next' ? 'left' : 'right';
14036             pan.el.addClass(dir); // or prev
14037             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
14038             cur.el.addClass(lr); // or right
14039             pan.el.addClass(lr);
14040             
14041             var _this = this;
14042             cur.el.on('transitionend', function() {
14043                 Roo.log("trans end?");
14044                 
14045                 pan.el.removeClass([lr,dir]);
14046                 pan.setActive(true);
14047                 
14048                 cur.el.removeClass([lr]);
14049                 cur.setActive(false);
14050                 
14051                 _this.transition = false;
14052                 
14053             }, this, { single:  true } );
14054             return true;
14055         }
14056         
14057         cur.setActive(false);
14058         pan.setActive(true);
14059         return true;
14060         
14061     },
14062     showPanelNext : function()
14063     {
14064         var i = this.indexOfPanel(this.getActivePanel());
14065         if (i > this.tabs.length) {
14066             return;
14067         }
14068         this.showPanel(this.tabs[i+1]);
14069     },
14070     showPanelPrev : function()
14071     {
14072         var i = this.indexOfPanel(this.getActivePanel());
14073         if (i  < 1) {
14074             return;
14075         }
14076         this.showPanel(this.tabs[i-1]);
14077     }
14078     
14079     
14080   
14081 });
14082
14083  
14084
14085  
14086  
14087 Roo.apply(Roo.bootstrap.TabGroup, {
14088     
14089     groups: {},
14090      /**
14091     * register a Navigation Group
14092     * @param {Roo.bootstrap.NavGroup} the navgroup to add
14093     */
14094     register : function(navgrp)
14095     {
14096         this.groups[navgrp.navId] = navgrp;
14097         
14098     },
14099     /**
14100     * fetch a Navigation Group based on the navigation ID
14101     * if one does not exist , it will get created.
14102     * @param {string} the navgroup to add
14103     * @returns {Roo.bootstrap.NavGroup} the navgroup 
14104     */
14105     get: function(navId) {
14106         if (typeof(this.groups[navId]) == 'undefined') {
14107             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
14108         }
14109         return this.groups[navId] ;
14110     }
14111     
14112     
14113     
14114 });
14115
14116  /*
14117  * - LGPL
14118  *
14119  * TabPanel
14120  * 
14121  */
14122
14123 /**
14124  * @class Roo.bootstrap.TabPanel
14125  * @extends Roo.bootstrap.Component
14126  * Bootstrap TabPanel class
14127  * @cfg {Boolean} active panel active
14128  * @cfg {String} html panel content
14129  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
14130  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
14131  * 
14132  * 
14133  * @constructor
14134  * Create a new TabPanel
14135  * @param {Object} config The config object
14136  */
14137
14138 Roo.bootstrap.TabPanel = function(config){
14139     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
14140     this.addEvents({
14141         /**
14142              * @event changed
14143              * Fires when the active status changes
14144              * @param {Roo.bootstrap.TabPanel} this
14145              * @param {Boolean} state the new state
14146             
14147          */
14148         'changed': true,
14149         /**
14150              * @event beforedeactivate
14151              * Fires before a tab is de-activated - can be used to do validation on a form.
14152              * @param {Roo.bootstrap.TabPanel} this
14153              * @return {Boolean} false if there is an error
14154             
14155          */
14156         'beforedeactivate': true
14157      });
14158     
14159     this.tabId = this.tabId || Roo.id();
14160   
14161 };
14162
14163 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
14164     
14165     active: false,
14166     html: false,
14167     tabId: false,
14168     navId : false,
14169     
14170     getAutoCreate : function(){
14171         var cfg = {
14172             tag: 'div',
14173             // item is needed for carousel - not sure if it has any effect otherwise
14174             cls: 'tab-pane item',
14175             html: this.html || ''
14176         };
14177         
14178         if(this.active){
14179             cfg.cls += ' active';
14180         }
14181         
14182         if(this.tabId){
14183             cfg.tabId = this.tabId;
14184         }
14185         
14186         
14187         return cfg;
14188     },
14189     
14190     initEvents:  function()
14191     {
14192         Roo.log('-------- init events on tab panel ---------');
14193         
14194         var p = this.parent();
14195         this.navId = this.navId || p.navId;
14196         
14197         if (typeof(this.navId) != 'undefined') {
14198             // not really needed.. but just in case.. parent should be a NavGroup.
14199             var tg = Roo.bootstrap.TabGroup.get(this.navId);
14200             Roo.log(['register', tg, this]);
14201             tg.register(this);
14202         }
14203     },
14204     
14205     
14206     onRender : function(ct, position)
14207     {
14208        // Roo.log("Call onRender: " + this.xtype);
14209         
14210         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
14211         
14212         
14213         
14214         
14215         
14216     },
14217     
14218     setActive: function(state)
14219     {
14220         Roo.log("panel - set active " + this.tabId + "=" + state);
14221         
14222         this.active = state;
14223         if (!state) {
14224             this.el.removeClass('active');
14225             
14226         } else  if (!this.el.hasClass('active')) {
14227             this.el.addClass('active');
14228         }
14229         this.fireEvent('changed', this, state);
14230     }
14231     
14232     
14233 });
14234  
14235
14236  
14237
14238  /*
14239  * - LGPL
14240  *
14241  * DateField
14242  * 
14243  */
14244
14245 /**
14246  * @class Roo.bootstrap.DateField
14247  * @extends Roo.bootstrap.Input
14248  * Bootstrap DateField class
14249  * @cfg {Number} weekStart default 0
14250  * @cfg {Number} weekStart default 0
14251  * @cfg {Number} viewMode default empty, (months|years)
14252  * @cfg {Number} minViewMode default empty, (months|years)
14253  * @cfg {Number} startDate default -Infinity
14254  * @cfg {Number} endDate default Infinity
14255  * @cfg {Boolean} todayHighlight default false
14256  * @cfg {Boolean} todayBtn default false
14257  * @cfg {Boolean} calendarWeeks default false
14258  * @cfg {Object} daysOfWeekDisabled default empty
14259  * 
14260  * @cfg {Boolean} keyboardNavigation default true
14261  * @cfg {String} language default en
14262  * 
14263  * @constructor
14264  * Create a new DateField
14265  * @param {Object} config The config object
14266  */
14267
14268 Roo.bootstrap.DateField = function(config){
14269     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
14270      this.addEvents({
14271             /**
14272              * @event show
14273              * Fires when this field show.
14274              * @param {Roo.bootstrap.DateField} this
14275              * @param {Mixed} date The date value
14276              */
14277             show : true,
14278             /**
14279              * @event show
14280              * Fires when this field hide.
14281              * @param {Roo.bootstrap.DateField} this
14282              * @param {Mixed} date The date value
14283              */
14284             hide : true,
14285             /**
14286              * @event select
14287              * Fires when select a date.
14288              * @param {Roo.bootstrap.DateField} this
14289              * @param {Mixed} date The date value
14290              */
14291             select : true
14292         });
14293 };
14294
14295 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
14296     
14297     /**
14298      * @cfg {String} format
14299      * The default date format string which can be overriden for localization support.  The format must be
14300      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
14301      */
14302     format : "m/d/y",
14303     /**
14304      * @cfg {String} altFormats
14305      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
14306      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
14307      */
14308     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
14309     
14310     weekStart : 0,
14311     
14312     viewMode : '',
14313     
14314     minViewMode : '',
14315     
14316     todayHighlight : false,
14317     
14318     todayBtn: false,
14319     
14320     language: 'en',
14321     
14322     keyboardNavigation: true,
14323     
14324     calendarWeeks: false,
14325     
14326     startDate: -Infinity,
14327     
14328     endDate: Infinity,
14329     
14330     daysOfWeekDisabled: [],
14331     
14332     _events: [],
14333     
14334     UTCDate: function()
14335     {
14336         return new Date(Date.UTC.apply(Date, arguments));
14337     },
14338     
14339     UTCToday: function()
14340     {
14341         var today = new Date();
14342         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
14343     },
14344     
14345     getDate: function() {
14346             var d = this.getUTCDate();
14347             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
14348     },
14349     
14350     getUTCDate: function() {
14351             return this.date;
14352     },
14353     
14354     setDate: function(d) {
14355             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
14356     },
14357     
14358     setUTCDate: function(d) {
14359             this.date = d;
14360             this.setValue(this.formatDate(this.date));
14361     },
14362         
14363     onRender: function(ct, position)
14364     {
14365         
14366         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
14367         
14368         this.language = this.language || 'en';
14369         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
14370         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
14371         
14372         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
14373         this.format = this.format || 'm/d/y';
14374         this.isInline = false;
14375         this.isInput = true;
14376         this.component = this.el.select('.add-on', true).first() || false;
14377         this.component = (this.component && this.component.length === 0) ? false : this.component;
14378         this.hasInput = this.component && this.inputEL().length;
14379         
14380         if (typeof(this.minViewMode === 'string')) {
14381             switch (this.minViewMode) {
14382                 case 'months':
14383                     this.minViewMode = 1;
14384                     break;
14385                 case 'years':
14386                     this.minViewMode = 2;
14387                     break;
14388                 default:
14389                     this.minViewMode = 0;
14390                     break;
14391             }
14392         }
14393         
14394         if (typeof(this.viewMode === 'string')) {
14395             switch (this.viewMode) {
14396                 case 'months':
14397                     this.viewMode = 1;
14398                     break;
14399                 case 'years':
14400                     this.viewMode = 2;
14401                     break;
14402                 default:
14403                     this.viewMode = 0;
14404                     break;
14405             }
14406         }
14407                 
14408         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
14409         
14410 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
14411         
14412         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14413         
14414         this.picker().on('mousedown', this.onMousedown, this);
14415         this.picker().on('click', this.onClick, this);
14416         
14417         this.picker().addClass('datepicker-dropdown');
14418         
14419         this.startViewMode = this.viewMode;
14420         
14421         
14422         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
14423             if(!this.calendarWeeks){
14424                 v.remove();
14425                 return;
14426             };
14427             
14428             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
14429             v.attr('colspan', function(i, val){
14430                 return parseInt(val) + 1;
14431             });
14432         })
14433                         
14434         
14435         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
14436         
14437         this.setStartDate(this.startDate);
14438         this.setEndDate(this.endDate);
14439         
14440         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
14441         
14442         this.fillDow();
14443         this.fillMonths();
14444         this.update();
14445         this.showMode();
14446         
14447         if(this.isInline) {
14448             this.show();
14449         }
14450     },
14451     
14452     picker : function()
14453     {
14454         return this.pickerEl;
14455 //        return this.el.select('.datepicker', true).first();
14456     },
14457     
14458     fillDow: function()
14459     {
14460         var dowCnt = this.weekStart;
14461         
14462         var dow = {
14463             tag: 'tr',
14464             cn: [
14465                 
14466             ]
14467         };
14468         
14469         if(this.calendarWeeks){
14470             dow.cn.push({
14471                 tag: 'th',
14472                 cls: 'cw',
14473                 html: '&nbsp;'
14474             })
14475         }
14476         
14477         while (dowCnt < this.weekStart + 7) {
14478             dow.cn.push({
14479                 tag: 'th',
14480                 cls: 'dow',
14481                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
14482             });
14483         }
14484         
14485         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
14486     },
14487     
14488     fillMonths: function()
14489     {    
14490         var i = 0
14491         var months = this.picker().select('>.datepicker-months td', true).first();
14492         
14493         months.dom.innerHTML = '';
14494         
14495         while (i < 12) {
14496             var month = {
14497                 tag: 'span',
14498                 cls: 'month',
14499                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
14500             }
14501             
14502             months.createChild(month);
14503         }
14504         
14505     },
14506     
14507     update: function()
14508     {
14509         
14510         this.date = (typeof(this.date) === 'undefined' || !this.date.length) ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
14511         
14512         if (this.date < this.startDate) {
14513             this.viewDate = new Date(this.startDate);
14514         } else if (this.date > this.endDate) {
14515             this.viewDate = new Date(this.endDate);
14516         } else {
14517             this.viewDate = new Date(this.date);
14518         }
14519         
14520         this.fill();
14521     },
14522     
14523     fill: function() 
14524     {
14525         var d = new Date(this.viewDate),
14526                 year = d.getUTCFullYear(),
14527                 month = d.getUTCMonth(),
14528                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
14529                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
14530                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
14531                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
14532                 currentDate = this.date && this.date.valueOf(),
14533                 today = this.UTCToday();
14534         
14535         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
14536         
14537 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
14538         
14539 //        this.picker.select('>tfoot th.today').
14540 //                                              .text(dates[this.language].today)
14541 //                                              .toggle(this.todayBtn !== false);
14542     
14543         this.updateNavArrows();
14544         this.fillMonths();
14545                                                 
14546         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
14547         
14548         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
14549          
14550         prevMonth.setUTCDate(day);
14551         
14552         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
14553         
14554         var nextMonth = new Date(prevMonth);
14555         
14556         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
14557         
14558         nextMonth = nextMonth.valueOf();
14559         
14560         var fillMonths = false;
14561         
14562         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
14563         
14564         while(prevMonth.valueOf() < nextMonth) {
14565             var clsName = '';
14566             
14567             if (prevMonth.getUTCDay() === this.weekStart) {
14568                 if(fillMonths){
14569                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
14570                 }
14571                     
14572                 fillMonths = {
14573                     tag: 'tr',
14574                     cn: []
14575                 };
14576                 
14577                 if(this.calendarWeeks){
14578                     // ISO 8601: First week contains first thursday.
14579                     // ISO also states week starts on Monday, but we can be more abstract here.
14580                     var
14581                     // Start of current week: based on weekstart/current date
14582                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
14583                     // Thursday of this week
14584                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
14585                     // First Thursday of year, year from thursday
14586                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
14587                     // Calendar week: ms between thursdays, div ms per day, div 7 days
14588                     calWeek =  (th - yth) / 864e5 / 7 + 1;
14589                     
14590                     fillMonths.cn.push({
14591                         tag: 'td',
14592                         cls: 'cw',
14593                         html: calWeek
14594                     });
14595                 }
14596             }
14597             
14598             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
14599                 clsName += ' old';
14600             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
14601                 clsName += ' new';
14602             }
14603             if (this.todayHighlight &&
14604                 prevMonth.getUTCFullYear() == today.getFullYear() &&
14605                 prevMonth.getUTCMonth() == today.getMonth() &&
14606                 prevMonth.getUTCDate() == today.getDate()) {
14607                 clsName += ' today';
14608             }
14609             
14610             if (currentDate && prevMonth.valueOf() === currentDate) {
14611                 clsName += ' active';
14612             }
14613             
14614             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
14615                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
14616                     clsName += ' disabled';
14617             }
14618             
14619             fillMonths.cn.push({
14620                 tag: 'td',
14621                 cls: 'day ' + clsName,
14622                 html: prevMonth.getDate()
14623             })
14624             
14625             prevMonth.setDate(prevMonth.getDate()+1);
14626         }
14627           
14628         var currentYear = this.date && this.date.getUTCFullYear();
14629         var currentMonth = this.date && this.date.getUTCMonth();
14630         
14631         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
14632         
14633         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
14634             v.removeClass('active');
14635             
14636             if(currentYear === year && k === currentMonth){
14637                 v.addClass('active');
14638             }
14639             
14640             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
14641                 v.addClass('disabled');
14642             }
14643             
14644         });
14645         
14646         
14647         year = parseInt(year/10, 10) * 10;
14648         
14649         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
14650         
14651         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
14652         
14653         year -= 1;
14654         for (var i = -1; i < 11; i++) {
14655             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
14656                 tag: 'span',
14657                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
14658                 html: year
14659             })
14660             
14661             year += 1;
14662         }
14663     },
14664     
14665     showMode: function(dir) 
14666     {
14667         if (dir) {
14668             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
14669         }
14670         Roo.each(this.picker().select('>div',true).elements, function(v){
14671             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14672             v.hide();
14673         });
14674         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
14675     },
14676     
14677     place: function()
14678     {
14679         if(this.isInline) return;
14680         
14681         this.picker().removeClass(['bottom', 'top']);
14682         
14683         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
14684             /*
14685              * place to the top of element!
14686              *
14687              */
14688             
14689             this.picker().addClass('top');
14690             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
14691             
14692             return;
14693         }
14694         
14695         this.picker().addClass('bottom');
14696         
14697         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
14698     },
14699     
14700     parseDate : function(value)
14701     {
14702         if(!value || value instanceof Date){
14703             return value;
14704         }
14705         var v = Date.parseDate(value, this.format);
14706         if (!v && this.useIso) {
14707             v = Date.parseDate(value, 'Y-m-d');
14708         }
14709         if(!v && this.altFormats){
14710             if(!this.altFormatsArray){
14711                 this.altFormatsArray = this.altFormats.split("|");
14712             }
14713             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
14714                 v = Date.parseDate(value, this.altFormatsArray[i]);
14715             }
14716         }
14717         return v;
14718     },
14719     
14720     formatDate : function(date, fmt)
14721     {
14722         return (!date || !(date instanceof Date)) ?
14723         date : date.dateFormat(fmt || this.format);
14724     },
14725     
14726     onFocus : function()
14727     {
14728         Roo.bootstrap.DateField.superclass.onFocus.call(this);
14729         this.show();
14730     },
14731     
14732     onBlur : function()
14733     {
14734         Roo.bootstrap.DateField.superclass.onBlur.call(this);
14735         
14736         var d = this.inputEl().getValue();
14737         
14738         this.setValue(d);
14739                 
14740         this.hide();
14741     },
14742     
14743     show : function()
14744     {
14745         this.picker().show();
14746         this.update();
14747         this.place();
14748         
14749         this.fireEvent('show', this, this.date);
14750     },
14751     
14752     hide : function()
14753     {
14754         if(this.isInline) return;
14755         this.picker().hide();
14756         this.viewMode = this.startViewMode;
14757         this.showMode();
14758         
14759         this.fireEvent('hide', this, this.date);
14760         
14761     },
14762     
14763     onMousedown: function(e)
14764     {
14765         e.stopPropagation();
14766         e.preventDefault();
14767     },
14768     
14769     keyup: function(e)
14770     {
14771         Roo.bootstrap.DateField.superclass.keyup.call(this);
14772         this.update();
14773     },
14774
14775     setValue: function(v)
14776     {
14777         var d = new Date(v);
14778         
14779         if(isNaN(d.getTime())){
14780             this.date = '';
14781             return;
14782         }
14783         
14784         v = this.formatDate(d);
14785         
14786         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
14787         
14788         this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
14789
14790         this.update();
14791
14792         this.fireEvent('select', this, this.date);
14793         
14794     },
14795     
14796     getValue: function()
14797     {
14798         return this.formatDate(this.date);
14799     },
14800     
14801     fireKey: function(e)
14802     {
14803         if (!this.picker().isVisible()){
14804             if (e.keyCode == 27) // allow escape to hide and re-show picker
14805                 this.show();
14806             return;
14807         }
14808         
14809         var dateChanged = false,
14810         dir, day, month,
14811         newDate, newViewDate;
14812         
14813         switch(e.keyCode){
14814             case 27: // escape
14815                 this.hide();
14816                 e.preventDefault();
14817                 break;
14818             case 37: // left
14819             case 39: // right
14820                 if (!this.keyboardNavigation) break;
14821                 dir = e.keyCode == 37 ? -1 : 1;
14822                 
14823                 if (e.ctrlKey){
14824                     newDate = this.moveYear(this.date, dir);
14825                     newViewDate = this.moveYear(this.viewDate, dir);
14826                 } else if (e.shiftKey){
14827                     newDate = this.moveMonth(this.date, dir);
14828                     newViewDate = this.moveMonth(this.viewDate, dir);
14829                 } else {
14830                     newDate = new Date(this.date);
14831                     newDate.setUTCDate(this.date.getUTCDate() + dir);
14832                     newViewDate = new Date(this.viewDate);
14833                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
14834                 }
14835                 if (this.dateWithinRange(newDate)){
14836                     this.date = newDate;
14837                     this.viewDate = newViewDate;
14838                     this.setValue(this.formatDate(this.date));
14839 //                    this.update();
14840                     e.preventDefault();
14841                     dateChanged = true;
14842                 }
14843                 break;
14844             case 38: // up
14845             case 40: // down
14846                 if (!this.keyboardNavigation) break;
14847                 dir = e.keyCode == 38 ? -1 : 1;
14848                 if (e.ctrlKey){
14849                     newDate = this.moveYear(this.date, dir);
14850                     newViewDate = this.moveYear(this.viewDate, dir);
14851                 } else if (e.shiftKey){
14852                     newDate = this.moveMonth(this.date, dir);
14853                     newViewDate = this.moveMonth(this.viewDate, dir);
14854                 } else {
14855                     newDate = new Date(this.date);
14856                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
14857                     newViewDate = new Date(this.viewDate);
14858                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
14859                 }
14860                 if (this.dateWithinRange(newDate)){
14861                     this.date = newDate;
14862                     this.viewDate = newViewDate;
14863                     this.setValue(this.formatDate(this.date));
14864 //                    this.update();
14865                     e.preventDefault();
14866                     dateChanged = true;
14867                 }
14868                 break;
14869             case 13: // enter
14870                 this.setValue(this.formatDate(this.date));
14871                 this.hide();
14872                 e.preventDefault();
14873                 break;
14874             case 9: // tab
14875                 this.setValue(this.formatDate(this.date));
14876                 this.hide();
14877                 break;
14878             case 16: // shift
14879             case 17: // ctrl
14880             case 18: // alt
14881                 break;
14882             default :
14883                 this.hide();
14884                 
14885         }
14886     },
14887     
14888     
14889     onClick: function(e) 
14890     {
14891         e.stopPropagation();
14892         e.preventDefault();
14893         
14894         var target = e.getTarget();
14895         
14896         if(target.nodeName.toLowerCase() === 'i'){
14897             target = Roo.get(target).dom.parentNode;
14898         }
14899         
14900         var nodeName = target.nodeName;
14901         var className = target.className;
14902         var html = target.innerHTML;
14903         
14904         switch(nodeName.toLowerCase()) {
14905             case 'th':
14906                 switch(className) {
14907                     case 'switch':
14908                         this.showMode(1);
14909                         break;
14910                     case 'prev':
14911                     case 'next':
14912                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
14913                         switch(this.viewMode){
14914                                 case 0:
14915                                         this.viewDate = this.moveMonth(this.viewDate, dir);
14916                                         break;
14917                                 case 1:
14918                                 case 2:
14919                                         this.viewDate = this.moveYear(this.viewDate, dir);
14920                                         break;
14921                         }
14922                         this.fill();
14923                         break;
14924                     case 'today':
14925                         var date = new Date();
14926                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
14927 //                        this.fill()
14928                         this.setValue(this.formatDate(this.date));
14929                         
14930                         this.hide();
14931                         break;
14932                 }
14933                 break;
14934             case 'span':
14935                 if (className.indexOf('disabled') === -1) {
14936                     this.viewDate.setUTCDate(1);
14937                     if (className.indexOf('month') !== -1) {
14938                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
14939                     } else {
14940                         var year = parseInt(html, 10) || 0;
14941                         this.viewDate.setUTCFullYear(year);
14942                         
14943                     }
14944                     this.showMode(-1);
14945                     this.fill();
14946                 }
14947                 break;
14948                 
14949             case 'td':
14950                 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
14951                     var day = parseInt(html, 10) || 1;
14952                     var year = this.viewDate.getUTCFullYear(),
14953                         month = this.viewDate.getUTCMonth();
14954
14955                     if (className.indexOf('old') !== -1) {
14956                         if(month === 0 ){
14957                             month = 11;
14958                             year -= 1;
14959                         }else{
14960                             month -= 1;
14961                         }
14962                     } else if (className.indexOf('new') !== -1) {
14963                         if (month == 11) {
14964                             month = 0;
14965                             year += 1;
14966                         } else {
14967                             month += 1;
14968                         }
14969                     }
14970                     this.date = this.UTCDate(year, month, day,0,0,0,0);
14971                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
14972 //                    this.fill();
14973                     this.setValue(this.formatDate(this.date));
14974                     this.hide();
14975                 }
14976                 break;
14977         }
14978     },
14979     
14980     setStartDate: function(startDate)
14981     {
14982         this.startDate = startDate || -Infinity;
14983         if (this.startDate !== -Infinity) {
14984             this.startDate = this.parseDate(this.startDate);
14985         }
14986         this.update();
14987         this.updateNavArrows();
14988     },
14989
14990     setEndDate: function(endDate)
14991     {
14992         this.endDate = endDate || Infinity;
14993         if (this.endDate !== Infinity) {
14994             this.endDate = this.parseDate(this.endDate);
14995         }
14996         this.update();
14997         this.updateNavArrows();
14998     },
14999     
15000     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
15001     {
15002         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
15003         if (typeof(this.daysOfWeekDisabled) !== 'object') {
15004             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
15005         }
15006         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
15007             return parseInt(d, 10);
15008         });
15009         this.update();
15010         this.updateNavArrows();
15011     },
15012     
15013     updateNavArrows: function() 
15014     {
15015         var d = new Date(this.viewDate),
15016         year = d.getUTCFullYear(),
15017         month = d.getUTCMonth();
15018         
15019         Roo.each(this.picker().select('.prev', true).elements, function(v){
15020             v.show();
15021             switch (this.viewMode) {
15022                 case 0:
15023
15024                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
15025                         v.hide();
15026                     }
15027                     break;
15028                 case 1:
15029                 case 2:
15030                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
15031                         v.hide();
15032                     }
15033                     break;
15034             }
15035         });
15036         
15037         Roo.each(this.picker().select('.next', true).elements, function(v){
15038             v.show();
15039             switch (this.viewMode) {
15040                 case 0:
15041
15042                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
15043                         v.hide();
15044                     }
15045                     break;
15046                 case 1:
15047                 case 2:
15048                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
15049                         v.hide();
15050                     }
15051                     break;
15052             }
15053         })
15054     },
15055     
15056     moveMonth: function(date, dir)
15057     {
15058         if (!dir) return date;
15059         var new_date = new Date(date.valueOf()),
15060         day = new_date.getUTCDate(),
15061         month = new_date.getUTCMonth(),
15062         mag = Math.abs(dir),
15063         new_month, test;
15064         dir = dir > 0 ? 1 : -1;
15065         if (mag == 1){
15066             test = dir == -1
15067             // If going back one month, make sure month is not current month
15068             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
15069             ? function(){
15070                 return new_date.getUTCMonth() == month;
15071             }
15072             // If going forward one month, make sure month is as expected
15073             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
15074             : function(){
15075                 return new_date.getUTCMonth() != new_month;
15076             };
15077             new_month = month + dir;
15078             new_date.setUTCMonth(new_month);
15079             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
15080             if (new_month < 0 || new_month > 11)
15081                 new_month = (new_month + 12) % 12;
15082         } else {
15083             // For magnitudes >1, move one month at a time...
15084             for (var i=0; i<mag; i++)
15085                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
15086                 new_date = this.moveMonth(new_date, dir);
15087             // ...then reset the day, keeping it in the new month
15088             new_month = new_date.getUTCMonth();
15089             new_date.setUTCDate(day);
15090             test = function(){
15091                 return new_month != new_date.getUTCMonth();
15092             };
15093         }
15094         // Common date-resetting loop -- if date is beyond end of month, make it
15095         // end of month
15096         while (test()){
15097             new_date.setUTCDate(--day);
15098             new_date.setUTCMonth(new_month);
15099         }
15100         return new_date;
15101     },
15102
15103     moveYear: function(date, dir)
15104     {
15105         return this.moveMonth(date, dir*12);
15106     },
15107
15108     dateWithinRange: function(date)
15109     {
15110         return date >= this.startDate && date <= this.endDate;
15111     },
15112
15113     
15114     remove: function() 
15115     {
15116         this.picker().remove();
15117     }
15118    
15119 });
15120
15121 Roo.apply(Roo.bootstrap.DateField,  {
15122     
15123     head : {
15124         tag: 'thead',
15125         cn: [
15126         {
15127             tag: 'tr',
15128             cn: [
15129             {
15130                 tag: 'th',
15131                 cls: 'prev',
15132                 html: '<i class="fa fa-arrow-left"/>'
15133             },
15134             {
15135                 tag: 'th',
15136                 cls: 'switch',
15137                 colspan: '5'
15138             },
15139             {
15140                 tag: 'th',
15141                 cls: 'next',
15142                 html: '<i class="fa fa-arrow-right"/>'
15143             }
15144
15145             ]
15146         }
15147         ]
15148     },
15149     
15150     content : {
15151         tag: 'tbody',
15152         cn: [
15153         {
15154             tag: 'tr',
15155             cn: [
15156             {
15157                 tag: 'td',
15158                 colspan: '7'
15159             }
15160             ]
15161         }
15162         ]
15163     },
15164     
15165     footer : {
15166         tag: 'tfoot',
15167         cn: [
15168         {
15169             tag: 'tr',
15170             cn: [
15171             {
15172                 tag: 'th',
15173                 colspan: '7',
15174                 cls: 'today'
15175             }
15176                     
15177             ]
15178         }
15179         ]
15180     },
15181     
15182     dates:{
15183         en: {
15184             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
15185             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
15186             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
15187             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
15188             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
15189             today: "Today"
15190         }
15191     },
15192     
15193     modes: [
15194     {
15195         clsName: 'days',
15196         navFnc: 'Month',
15197         navStep: 1
15198     },
15199     {
15200         clsName: 'months',
15201         navFnc: 'FullYear',
15202         navStep: 1
15203     },
15204     {
15205         clsName: 'years',
15206         navFnc: 'FullYear',
15207         navStep: 10
15208     }]
15209 });
15210
15211 Roo.apply(Roo.bootstrap.DateField,  {
15212   
15213     template : {
15214         tag: 'div',
15215         cls: 'datepicker dropdown-menu',
15216         cn: [
15217         {
15218             tag: 'div',
15219             cls: 'datepicker-days',
15220             cn: [
15221             {
15222                 tag: 'table',
15223                 cls: 'table-condensed',
15224                 cn:[
15225                 Roo.bootstrap.DateField.head,
15226                 {
15227                     tag: 'tbody'
15228                 },
15229                 Roo.bootstrap.DateField.footer
15230                 ]
15231             }
15232             ]
15233         },
15234         {
15235             tag: 'div',
15236             cls: 'datepicker-months',
15237             cn: [
15238             {
15239                 tag: 'table',
15240                 cls: 'table-condensed',
15241                 cn:[
15242                 Roo.bootstrap.DateField.head,
15243                 Roo.bootstrap.DateField.content,
15244                 Roo.bootstrap.DateField.footer
15245                 ]
15246             }
15247             ]
15248         },
15249         {
15250             tag: 'div',
15251             cls: 'datepicker-years',
15252             cn: [
15253             {
15254                 tag: 'table',
15255                 cls: 'table-condensed',
15256                 cn:[
15257                 Roo.bootstrap.DateField.head,
15258                 Roo.bootstrap.DateField.content,
15259                 Roo.bootstrap.DateField.footer
15260                 ]
15261             }
15262             ]
15263         }
15264         ]
15265     }
15266 });
15267
15268  
15269
15270  /*
15271  * - LGPL
15272  *
15273  * TimeField
15274  * 
15275  */
15276
15277 /**
15278  * @class Roo.bootstrap.TimeField
15279  * @extends Roo.bootstrap.Input
15280  * Bootstrap DateField class
15281  * 
15282  * 
15283  * @constructor
15284  * Create a new TimeField
15285  * @param {Object} config The config object
15286  */
15287
15288 Roo.bootstrap.TimeField = function(config){
15289     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
15290     this.addEvents({
15291             /**
15292              * @event show
15293              * Fires when this field show.
15294              * @param {Roo.bootstrap.DateField} this
15295              * @param {Mixed} date The date value
15296              */
15297             show : true,
15298             /**
15299              * @event show
15300              * Fires when this field hide.
15301              * @param {Roo.bootstrap.DateField} this
15302              * @param {Mixed} date The date value
15303              */
15304             hide : true,
15305             /**
15306              * @event select
15307              * Fires when select a date.
15308              * @param {Roo.bootstrap.DateField} this
15309              * @param {Mixed} date The date value
15310              */
15311             select : true
15312         });
15313 };
15314
15315 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
15316     
15317     /**
15318      * @cfg {String} format
15319      * The default time format string which can be overriden for localization support.  The format must be
15320      * valid according to {@link Date#parseDate} (defaults to 'H:i').
15321      */
15322     format : "H:i",
15323        
15324     onRender: function(ct, position)
15325     {
15326         
15327         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
15328                 
15329         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
15330         
15331         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15332         
15333         this.pop = this.picker().select('>.datepicker-time',true).first();
15334         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block' 
15335         
15336         this.picker().on('mousedown', this.onMousedown, this);
15337         this.picker().on('click', this.onClick, this);
15338         
15339         this.picker().addClass('datepicker-dropdown');
15340     
15341         this.fillTime();
15342         this.update();
15343             
15344         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
15345         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
15346         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
15347         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
15348         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
15349         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
15350
15351     },
15352     
15353     fireKey: function(e){
15354         if (!this.picker().isVisible()){
15355             if (e.keyCode == 27) // allow escape to hide and re-show picker
15356                 this.show();
15357             return;
15358         }
15359
15360         e.preventDefault();
15361         
15362         switch(e.keyCode){
15363             case 27: // escape
15364                 this.hide();
15365                 break;
15366             case 37: // left
15367             case 39: // right
15368                 this.onTogglePeriod();
15369                 break;
15370             case 38: // up
15371                 this.onIncrementMinutes();
15372                 break;
15373             case 40: // down
15374                 this.onDecrementMinutes();
15375                 break;
15376             case 13: // enter
15377             case 9: // tab
15378                 this.setTime();
15379                 break;
15380         }
15381     },
15382     
15383     onClick: function(e) {
15384         e.stopPropagation();
15385         e.preventDefault();
15386     },
15387     
15388     picker : function()
15389     {
15390         return this.el.select('.datepicker', true).first();
15391     },
15392     
15393     fillTime: function()
15394     {    
15395         var time = this.pop.select('tbody', true).first();
15396         
15397         time.dom.innerHTML = '';
15398         
15399         time.createChild({
15400             tag: 'tr',
15401             cn: [
15402                 {
15403                     tag: 'td',
15404                     cn: [
15405                         {
15406                             tag: 'a',
15407                             href: '#',
15408                             cls: 'btn',
15409                             cn: [
15410                                 {
15411                                     tag: 'span',
15412                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
15413                                 }
15414                             ]
15415                         } 
15416                     ]
15417                 },
15418                 {
15419                     tag: 'td',
15420                     cls: 'separator'
15421                 },
15422                 {
15423                     tag: 'td',
15424                     cn: [
15425                         {
15426                             tag: 'a',
15427                             href: '#',
15428                             cls: 'btn',
15429                             cn: [
15430                                 {
15431                                     tag: 'span',
15432                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
15433                                 }
15434                             ]
15435                         }
15436                     ]
15437                 },
15438                 {
15439                     tag: 'td',
15440                     cls: 'separator'
15441                 }
15442             ]
15443         });
15444         
15445         time.createChild({
15446             tag: 'tr',
15447             cn: [
15448                 {
15449                     tag: 'td',
15450                     cn: [
15451                         {
15452                             tag: 'span',
15453                             cls: 'timepicker-hour',
15454                             html: '00'
15455                         }  
15456                     ]
15457                 },
15458                 {
15459                     tag: 'td',
15460                     cls: 'separator',
15461                     html: ':'
15462                 },
15463                 {
15464                     tag: 'td',
15465                     cn: [
15466                         {
15467                             tag: 'span',
15468                             cls: 'timepicker-minute',
15469                             html: '00'
15470                         }  
15471                     ]
15472                 },
15473                 {
15474                     tag: 'td',
15475                     cls: 'separator'
15476                 },
15477                 {
15478                     tag: 'td',
15479                     cn: [
15480                         {
15481                             tag: 'button',
15482                             type: 'button',
15483                             cls: 'btn btn-primary period',
15484                             html: 'AM'
15485                             
15486                         }
15487                     ]
15488                 }
15489             ]
15490         });
15491         
15492         time.createChild({
15493             tag: 'tr',
15494             cn: [
15495                 {
15496                     tag: 'td',
15497                     cn: [
15498                         {
15499                             tag: 'a',
15500                             href: '#',
15501                             cls: 'btn',
15502                             cn: [
15503                                 {
15504                                     tag: 'span',
15505                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
15506                                 }
15507                             ]
15508                         }
15509                     ]
15510                 },
15511                 {
15512                     tag: 'td',
15513                     cls: 'separator'
15514                 },
15515                 {
15516                     tag: 'td',
15517                     cn: [
15518                         {
15519                             tag: 'a',
15520                             href: '#',
15521                             cls: 'btn',
15522                             cn: [
15523                                 {
15524                                     tag: 'span',
15525                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
15526                                 }
15527                             ]
15528                         }
15529                     ]
15530                 },
15531                 {
15532                     tag: 'td',
15533                     cls: 'separator'
15534                 }
15535             ]
15536         });
15537         
15538     },
15539     
15540     update: function()
15541     {
15542         
15543         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
15544         
15545         this.fill();
15546     },
15547     
15548     fill: function() 
15549     {
15550         var hours = this.time.getHours();
15551         var minutes = this.time.getMinutes();
15552         var period = 'AM';
15553         
15554         if(hours > 11){
15555             period = 'PM';
15556         }
15557         
15558         if(hours == 0){
15559             hours = 12;
15560         }
15561         
15562         
15563         if(hours > 12){
15564             hours = hours - 12;
15565         }
15566         
15567         if(hours < 10){
15568             hours = '0' + hours;
15569         }
15570         
15571         if(minutes < 10){
15572             minutes = '0' + minutes;
15573         }
15574         
15575         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
15576         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
15577         this.pop.select('button', true).first().dom.innerHTML = period;
15578         
15579     },
15580     
15581     place: function()
15582     {   
15583         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
15584         
15585         var cls = ['bottom'];
15586         
15587         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
15588             cls.pop();
15589             cls.push('top');
15590         }
15591         
15592         cls.push('right');
15593         
15594         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
15595             cls.pop();
15596             cls.push('left');
15597         }
15598         
15599         this.picker().addClass(cls.join('-'));
15600         
15601         var _this = this;
15602         
15603         Roo.each(cls, function(c){
15604             if(c == 'bottom'){
15605                 _this.picker().setTop(_this.inputEl().getHeight());
15606                 return;
15607             }
15608             if(c == 'top'){
15609                 _this.picker().setTop(0 - _this.picker().getHeight());
15610                 return;
15611             }
15612             
15613             if(c == 'left'){
15614                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
15615                 return;
15616             }
15617             if(c == 'right'){
15618                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
15619                 return;
15620             }
15621         });
15622         
15623     },
15624   
15625     onFocus : function()
15626     {
15627         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
15628         this.show();
15629     },
15630     
15631     onBlur : function()
15632     {
15633         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
15634         this.hide();
15635     },
15636     
15637     show : function()
15638     {
15639         this.picker().show();
15640         this.pop.show();
15641         this.update();
15642         this.place();
15643         
15644         this.fireEvent('show', this, this.date);
15645     },
15646     
15647     hide : function()
15648     {
15649         this.picker().hide();
15650         this.pop.hide();
15651         
15652         this.fireEvent('hide', this, this.date);
15653     },
15654     
15655     setTime : function()
15656     {
15657         this.hide();
15658         this.setValue(this.time.format(this.format));
15659         
15660         this.fireEvent('select', this, this.date);
15661         
15662         
15663     },
15664     
15665     onMousedown: function(e){
15666         e.stopPropagation();
15667         e.preventDefault();
15668     },
15669     
15670     onIncrementHours: function()
15671     {
15672         Roo.log('onIncrementHours');
15673         this.time = this.time.add(Date.HOUR, 1);
15674         this.update();
15675         
15676     },
15677     
15678     onDecrementHours: function()
15679     {
15680         Roo.log('onDecrementHours');
15681         this.time = this.time.add(Date.HOUR, -1);
15682         this.update();
15683     },
15684     
15685     onIncrementMinutes: function()
15686     {
15687         Roo.log('onIncrementMinutes');
15688         this.time = this.time.add(Date.MINUTE, 1);
15689         this.update();
15690     },
15691     
15692     onDecrementMinutes: function()
15693     {
15694         Roo.log('onDecrementMinutes');
15695         this.time = this.time.add(Date.MINUTE, -1);
15696         this.update();
15697     },
15698     
15699     onTogglePeriod: function()
15700     {
15701         Roo.log('onTogglePeriod');
15702         this.time = this.time.add(Date.HOUR, 12);
15703         this.update();
15704     }
15705     
15706    
15707 });
15708
15709 Roo.apply(Roo.bootstrap.TimeField,  {
15710     
15711     content : {
15712         tag: 'tbody',
15713         cn: [
15714             {
15715                 tag: 'tr',
15716                 cn: [
15717                 {
15718                     tag: 'td',
15719                     colspan: '7'
15720                 }
15721                 ]
15722             }
15723         ]
15724     },
15725     
15726     footer : {
15727         tag: 'tfoot',
15728         cn: [
15729             {
15730                 tag: 'tr',
15731                 cn: [
15732                 {
15733                     tag: 'th',
15734                     colspan: '7',
15735                     cls: '',
15736                     cn: [
15737                         {
15738                             tag: 'button',
15739                             cls: 'btn btn-info ok',
15740                             html: 'OK'
15741                         }
15742                     ]
15743                 }
15744
15745                 ]
15746             }
15747         ]
15748     }
15749 });
15750
15751 Roo.apply(Roo.bootstrap.TimeField,  {
15752   
15753     template : {
15754         tag: 'div',
15755         cls: 'datepicker dropdown-menu',
15756         cn: [
15757             {
15758                 tag: 'div',
15759                 cls: 'datepicker-time',
15760                 cn: [
15761                 {
15762                     tag: 'table',
15763                     cls: 'table-condensed',
15764                     cn:[
15765                     Roo.bootstrap.TimeField.content,
15766                     Roo.bootstrap.TimeField.footer
15767                     ]
15768                 }
15769                 ]
15770             }
15771         ]
15772     }
15773 });
15774
15775  
15776
15777  /*
15778  * - LGPL
15779  *
15780  * CheckBox
15781  * 
15782  */
15783
15784 /**
15785  * @class Roo.bootstrap.CheckBox
15786  * @extends Roo.bootstrap.Input
15787  * Bootstrap CheckBox class
15788  * 
15789  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
15790  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
15791  * @cfg {String} boxLabel The text that appears beside the checkbox
15792  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
15793  * @cfg {Boolean} checked initnal the element
15794  * 
15795  * 
15796  * @constructor
15797  * Create a new CheckBox
15798  * @param {Object} config The config object
15799  */
15800
15801 Roo.bootstrap.CheckBox = function(config){
15802     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
15803    
15804         this.addEvents({
15805             /**
15806             * @event check
15807             * Fires when the element is checked or unchecked.
15808             * @param {Roo.bootstrap.CheckBox} this This input
15809             * @param {Boolean} checked The new checked value
15810             */
15811            check : true
15812         });
15813 };
15814
15815 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
15816     
15817     inputType: 'checkbox',
15818     inputValue: 1,
15819     valueOff: 0,
15820     boxLabel: false,
15821     checked: false,
15822     weight : false,
15823     
15824     getAutoCreate : function()
15825     {
15826         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
15827         
15828         var id = Roo.id();
15829         
15830         var cfg = {};
15831         
15832         cfg.cls = 'form-group checkbox' //input-group
15833         
15834         
15835         
15836         
15837         var input =  {
15838             tag: 'input',
15839             id : id,
15840             type : this.inputType,
15841             value : (!this.checked) ? this.valueOff : this.inputValue,
15842             cls : 'roo-checkbox', //'form-box',
15843             placeholder : this.placeholder || ''
15844             
15845         };
15846         
15847         if (this.weight) { // Validity check?
15848             cfg.cls += " checkbox-" + this.weight;
15849         }
15850         
15851         if (this.disabled) {
15852             input.disabled=true;
15853         }
15854         
15855         if(this.checked){
15856             input.checked = this.checked;
15857         }
15858         
15859         if (this.name) {
15860             input.name = this.name;
15861         }
15862         
15863         if (this.size) {
15864             input.cls += ' input-' + this.size;
15865         }
15866         
15867         var settings=this;
15868         ['xs','sm','md','lg'].map(function(size){
15869             if (settings[size]) {
15870                 cfg.cls += ' col-' + size + '-' + settings[size];
15871             }
15872         });
15873         
15874        
15875         
15876         var inputblock = input;
15877         
15878         
15879         
15880         
15881         if (this.before || this.after) {
15882             
15883             inputblock = {
15884                 cls : 'input-group',
15885                 cn :  [] 
15886             };
15887             if (this.before) {
15888                 inputblock.cn.push({
15889                     tag :'span',
15890                     cls : 'input-group-addon',
15891                     html : this.before
15892                 });
15893             }
15894             inputblock.cn.push(input);
15895             if (this.after) {
15896                 inputblock.cn.push({
15897                     tag :'span',
15898                     cls : 'input-group-addon',
15899                     html : this.after
15900                 });
15901             }
15902             
15903         };
15904         
15905         if (align ==='left' && this.fieldLabel.length) {
15906                 Roo.log("left and has label");
15907                 cfg.cn = [
15908                     
15909                     {
15910                         tag: 'label',
15911                         'for' :  id,
15912                         cls : 'control-label col-md-' + this.labelWidth,
15913                         html : this.fieldLabel
15914                         
15915                     },
15916                     {
15917                         cls : "col-md-" + (12 - this.labelWidth), 
15918                         cn: [
15919                             inputblock
15920                         ]
15921                     }
15922                     
15923                 ];
15924         } else if ( this.fieldLabel.length) {
15925                 Roo.log(" label");
15926                 cfg.cn = [
15927                    
15928                     {
15929                         tag: this.boxLabel ? 'span' : 'label',
15930                         'for': id,
15931                         cls: 'control-label box-input-label',
15932                         //cls : 'input-group-addon',
15933                         html : this.fieldLabel
15934                         
15935                     },
15936                     
15937                     inputblock
15938                     
15939                 ];
15940
15941         } else {
15942             
15943                 Roo.log(" no label && no align");
15944                 cfg.cn = [  inputblock ] ;
15945                 
15946                 
15947         };
15948          if(this.boxLabel){
15949             cfg.cn.push( {
15950                 tag: 'label',
15951                 'for': id,
15952                 cls: 'box-label',
15953                 html: this.boxLabel
15954                 
15955             });
15956         }
15957         
15958         
15959        
15960         return cfg;
15961         
15962     },
15963     
15964     /**
15965      * return the real input element.
15966      */
15967     inputEl: function ()
15968     {
15969         return this.el.select('input.roo-checkbox',true).first();
15970     },
15971     
15972     label: function()
15973     {
15974         return this.el.select('label.control-label',true).first();
15975     },
15976     
15977     initEvents : function()
15978     {
15979 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
15980         
15981         this.inputEl().on('click', this.onClick,  this);
15982         
15983     },
15984     
15985     onClick : function()
15986     {   
15987         this.setChecked(!this.checked);
15988     },
15989     
15990     setChecked : function(state,suppressEvent)
15991     {
15992         this.checked = state;
15993         
15994         this.inputEl().dom.checked = state;
15995         
15996         if(suppressEvent !== true){
15997             this.fireEvent('check', this, state);
15998         }
15999         
16000         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
16001         
16002     },
16003     
16004     setValue : function(v,suppressEvent)
16005     {
16006         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
16007     }
16008     
16009 });
16010
16011  
16012 /*
16013  * - LGPL
16014  *
16015  * Radio
16016  * 
16017  */
16018
16019 /**
16020  * @class Roo.bootstrap.Radio
16021  * @extends Roo.bootstrap.CheckBox
16022  * Bootstrap Radio class
16023
16024  * @constructor
16025  * Create a new Radio
16026  * @param {Object} config The config object
16027  */
16028
16029 Roo.bootstrap.Radio = function(config){
16030     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
16031    
16032 };
16033
16034 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
16035     
16036     inputType: 'radio',
16037     inputValue: '',
16038     valueOff: '',
16039     
16040     getAutoCreate : function()
16041     {
16042         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
16043         
16044         var id = Roo.id();
16045         
16046         var cfg = {};
16047         
16048         cfg.cls = 'form-group radio' //input-group
16049         
16050         var input =  {
16051             tag: 'input',
16052             id : id,
16053             type : this.inputType,
16054             value : (!this.checked) ? this.valueOff : this.inputValue,
16055             cls : 'roo-radio',
16056             placeholder : this.placeholder || ''
16057             
16058         };
16059           if (this.weight) { // Validity check?
16060             cfg.cls += " radio-" + this.weight;
16061         }
16062         if (this.disabled) {
16063             input.disabled=true;
16064         }
16065         
16066         if(this.checked){
16067             input.checked = this.checked;
16068         }
16069         
16070         if (this.name) {
16071             input.name = this.name;
16072         }
16073         
16074         if (this.size) {
16075             input.cls += ' input-' + this.size;
16076         }
16077         
16078         var settings=this;
16079         ['xs','sm','md','lg'].map(function(size){
16080             if (settings[size]) {
16081                 cfg.cls += ' col-' + size + '-' + settings[size];
16082             }
16083         });
16084         
16085         var inputblock = input;
16086         
16087         if (this.before || this.after) {
16088             
16089             inputblock = {
16090                 cls : 'input-group',
16091                 cn :  [] 
16092             };
16093             if (this.before) {
16094                 inputblock.cn.push({
16095                     tag :'span',
16096                     cls : 'input-group-addon',
16097                     html : this.before
16098                 });
16099             }
16100             inputblock.cn.push(input);
16101             if (this.after) {
16102                 inputblock.cn.push({
16103                     tag :'span',
16104                     cls : 'input-group-addon',
16105                     html : this.after
16106                 });
16107             }
16108             
16109         };
16110         
16111         if (align ==='left' && this.fieldLabel.length) {
16112                 Roo.log("left and has label");
16113                 cfg.cn = [
16114                     
16115                     {
16116                         tag: 'label',
16117                         'for' :  id,
16118                         cls : 'control-label col-md-' + this.labelWidth,
16119                         html : this.fieldLabel
16120                         
16121                     },
16122                     {
16123                         cls : "col-md-" + (12 - this.labelWidth), 
16124                         cn: [
16125                             inputblock
16126                         ]
16127                     }
16128                     
16129                 ];
16130         } else if ( this.fieldLabel.length) {
16131                 Roo.log(" label");
16132                  cfg.cn = [
16133                    
16134                     {
16135                         tag: 'label',
16136                         'for': id,
16137                         cls: 'control-label box-input-label',
16138                         //cls : 'input-group-addon',
16139                         html : this.fieldLabel
16140                         
16141                     },
16142                     
16143                     inputblock
16144                     
16145                 ];
16146
16147         } else {
16148             
16149                    Roo.log(" no label && no align");
16150                 cfg.cn = [
16151                     
16152                         inputblock
16153                     
16154                 ];
16155                 
16156                 
16157         };
16158         
16159         if(this.boxLabel){
16160             cfg.cn.push({
16161                 tag: 'label',
16162                 'for': id,
16163                 cls: 'box-label',
16164                 html: this.boxLabel
16165             })
16166         }
16167         
16168         return cfg;
16169         
16170     },
16171     inputEl: function ()
16172     {
16173         return this.el.select('input.roo-radio',true).first();
16174     },
16175     onClick : function()
16176     {   
16177         this.setChecked(true);
16178     },
16179     
16180     setChecked : function(state,suppressEvent)
16181     {
16182         if(state){
16183             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
16184                 v.dom.checked = false;
16185             });
16186         }
16187         
16188         this.checked = state;
16189         this.inputEl().dom.checked = state;
16190         
16191         if(suppressEvent !== true){
16192             this.fireEvent('check', this, state);
16193         }
16194         
16195         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
16196         
16197     },
16198     
16199     getGroupValue : function()
16200     {
16201         var value = ''
16202         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
16203             if(v.dom.checked == true){
16204                 value = v.dom.value;
16205             }
16206         });
16207         
16208         return value;
16209     },
16210     
16211     /**
16212      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
16213      * @return {Mixed} value The field value
16214      */
16215     getValue : function(){
16216         return this.getGroupValue();
16217     }
16218     
16219 });
16220
16221  
16222 //<script type="text/javascript">
16223
16224 /*
16225  * Based  Ext JS Library 1.1.1
16226  * Copyright(c) 2006-2007, Ext JS, LLC.
16227  * LGPL
16228  *
16229  */
16230  
16231 /**
16232  * @class Roo.HtmlEditorCore
16233  * @extends Roo.Component
16234  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
16235  *
16236  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
16237  */
16238
16239 Roo.HtmlEditorCore = function(config){
16240     
16241     
16242     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
16243     this.addEvents({
16244         /**
16245          * @event initialize
16246          * Fires when the editor is fully initialized (including the iframe)
16247          * @param {Roo.HtmlEditorCore} this
16248          */
16249         initialize: true,
16250         /**
16251          * @event activate
16252          * Fires when the editor is first receives the focus. Any insertion must wait
16253          * until after this event.
16254          * @param {Roo.HtmlEditorCore} this
16255          */
16256         activate: true,
16257          /**
16258          * @event beforesync
16259          * Fires before the textarea is updated with content from the editor iframe. Return false
16260          * to cancel the sync.
16261          * @param {Roo.HtmlEditorCore} this
16262          * @param {String} html
16263          */
16264         beforesync: true,
16265          /**
16266          * @event beforepush
16267          * Fires before the iframe editor is updated with content from the textarea. Return false
16268          * to cancel the push.
16269          * @param {Roo.HtmlEditorCore} this
16270          * @param {String} html
16271          */
16272         beforepush: true,
16273          /**
16274          * @event sync
16275          * Fires when the textarea is updated with content from the editor iframe.
16276          * @param {Roo.HtmlEditorCore} this
16277          * @param {String} html
16278          */
16279         sync: true,
16280          /**
16281          * @event push
16282          * Fires when the iframe editor is updated with content from the textarea.
16283          * @param {Roo.HtmlEditorCore} this
16284          * @param {String} html
16285          */
16286         push: true,
16287         
16288         /**
16289          * @event editorevent
16290          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
16291          * @param {Roo.HtmlEditorCore} this
16292          */
16293         editorevent: true
16294     });
16295      
16296 };
16297
16298
16299 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
16300
16301
16302      /**
16303      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
16304      */
16305     
16306     owner : false,
16307     
16308      /**
16309      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
16310      *                        Roo.resizable.
16311      */
16312     resizable : false,
16313      /**
16314      * @cfg {Number} height (in pixels)
16315      */   
16316     height: 300,
16317    /**
16318      * @cfg {Number} width (in pixels)
16319      */   
16320     width: 500,
16321     
16322     /**
16323      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
16324      * 
16325      */
16326     stylesheets: false,
16327     
16328     // id of frame..
16329     frameId: false,
16330     
16331     // private properties
16332     validationEvent : false,
16333     deferHeight: true,
16334     initialized : false,
16335     activated : false,
16336     sourceEditMode : false,
16337     onFocus : Roo.emptyFn,
16338     iframePad:3,
16339     hideMode:'offsets',
16340     
16341     clearUp: true,
16342     
16343      
16344     
16345
16346     /**
16347      * Protected method that will not generally be called directly. It
16348      * is called when the editor initializes the iframe with HTML contents. Override this method if you
16349      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
16350      */
16351     getDocMarkup : function(){
16352         // body styles..
16353         var st = '';
16354         Roo.log(this.stylesheets);
16355         
16356         // inherit styels from page...?? 
16357         if (this.stylesheets === false) {
16358             
16359             Roo.get(document.head).select('style').each(function(node) {
16360                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16361             });
16362             
16363             Roo.get(document.head).select('link').each(function(node) { 
16364                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16365             });
16366             
16367         } else if (!this.stylesheets.length) {
16368                 // simple..
16369                 st = '<style type="text/css">' +
16370                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16371                    '</style>';
16372         } else {
16373             Roo.each(this.stylesheets, function(s) {
16374                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
16375             });
16376             
16377         }
16378         
16379         st +=  '<style type="text/css">' +
16380             'IMG { cursor: pointer } ' +
16381         '</style>';
16382
16383         
16384         return '<html><head>' + st  +
16385             //<style type="text/css">' +
16386             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16387             //'</style>' +
16388             ' </head><body class="roo-htmleditor-body"></body></html>';
16389     },
16390
16391     // private
16392     onRender : function(ct, position)
16393     {
16394         var _t = this;
16395         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
16396         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
16397         
16398         
16399         this.el.dom.style.border = '0 none';
16400         this.el.dom.setAttribute('tabIndex', -1);
16401         this.el.addClass('x-hidden hide');
16402         
16403         
16404         
16405         if(Roo.isIE){ // fix IE 1px bogus margin
16406             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
16407         }
16408        
16409         
16410         this.frameId = Roo.id();
16411         
16412          
16413         
16414         var iframe = this.owner.wrap.createChild({
16415             tag: 'iframe',
16416             cls: 'form-control', // bootstrap..
16417             id: this.frameId,
16418             name: this.frameId,
16419             frameBorder : 'no',
16420             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
16421         }, this.el
16422         );
16423         
16424         
16425         this.iframe = iframe.dom;
16426
16427          this.assignDocWin();
16428         
16429         this.doc.designMode = 'on';
16430        
16431         this.doc.open();
16432         this.doc.write(this.getDocMarkup());
16433         this.doc.close();
16434
16435         
16436         var task = { // must defer to wait for browser to be ready
16437             run : function(){
16438                 //console.log("run task?" + this.doc.readyState);
16439                 this.assignDocWin();
16440                 if(this.doc.body || this.doc.readyState == 'complete'){
16441                     try {
16442                         this.doc.designMode="on";
16443                     } catch (e) {
16444                         return;
16445                     }
16446                     Roo.TaskMgr.stop(task);
16447                     this.initEditor.defer(10, this);
16448                 }
16449             },
16450             interval : 10,
16451             duration: 10000,
16452             scope: this
16453         };
16454         Roo.TaskMgr.start(task);
16455
16456         
16457          
16458     },
16459
16460     // private
16461     onResize : function(w, h)
16462     {
16463          Roo.log('resize: ' +w + ',' + h );
16464         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
16465         if(!this.iframe){
16466             return;
16467         }
16468         if(typeof w == 'number'){
16469             
16470             this.iframe.style.width = w + 'px';
16471         }
16472         if(typeof h == 'number'){
16473             
16474             this.iframe.style.height = h + 'px';
16475             if(this.doc){
16476                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
16477             }
16478         }
16479         
16480     },
16481
16482     /**
16483      * Toggles the editor between standard and source edit mode.
16484      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
16485      */
16486     toggleSourceEdit : function(sourceEditMode){
16487         
16488         this.sourceEditMode = sourceEditMode === true;
16489         
16490         if(this.sourceEditMode){
16491  
16492             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
16493             
16494         }else{
16495             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
16496             //this.iframe.className = '';
16497             this.deferFocus();
16498         }
16499         //this.setSize(this.owner.wrap.getSize());
16500         //this.fireEvent('editmodechange', this, this.sourceEditMode);
16501     },
16502
16503     
16504   
16505
16506     /**
16507      * Protected method that will not generally be called directly. If you need/want
16508      * custom HTML cleanup, this is the method you should override.
16509      * @param {String} html The HTML to be cleaned
16510      * return {String} The cleaned HTML
16511      */
16512     cleanHtml : function(html){
16513         html = String(html);
16514         if(html.length > 5){
16515             if(Roo.isSafari){ // strip safari nonsense
16516                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
16517             }
16518         }
16519         if(html == '&nbsp;'){
16520             html = '';
16521         }
16522         return html;
16523     },
16524
16525     /**
16526      * HTML Editor -> Textarea
16527      * Protected method that will not generally be called directly. Syncs the contents
16528      * of the editor iframe with the textarea.
16529      */
16530     syncValue : function(){
16531         if(this.initialized){
16532             var bd = (this.doc.body || this.doc.documentElement);
16533             //this.cleanUpPaste(); -- this is done else where and causes havoc..
16534             var html = bd.innerHTML;
16535             if(Roo.isSafari){
16536                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
16537                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
16538                 if(m && m[1]){
16539                     html = '<div style="'+m[0]+'">' + html + '</div>';
16540                 }
16541             }
16542             html = this.cleanHtml(html);
16543             // fix up the special chars.. normaly like back quotes in word...
16544             // however we do not want to do this with chinese..
16545             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
16546                 var cc = b.charCodeAt();
16547                 if (
16548                     (cc >= 0x4E00 && cc < 0xA000 ) ||
16549                     (cc >= 0x3400 && cc < 0x4E00 ) ||
16550                     (cc >= 0xf900 && cc < 0xfb00 )
16551                 ) {
16552                         return b;
16553                 }
16554                 return "&#"+cc+";" 
16555             });
16556             if(this.owner.fireEvent('beforesync', this, html) !== false){
16557                 this.el.dom.value = html;
16558                 this.owner.fireEvent('sync', this, html);
16559             }
16560         }
16561     },
16562
16563     /**
16564      * Protected method that will not generally be called directly. Pushes the value of the textarea
16565      * into the iframe editor.
16566      */
16567     pushValue : function(){
16568         if(this.initialized){
16569             var v = this.el.dom.value.trim();
16570             
16571 //            if(v.length < 1){
16572 //                v = '&#160;';
16573 //            }
16574             
16575             if(this.owner.fireEvent('beforepush', this, v) !== false){
16576                 var d = (this.doc.body || this.doc.documentElement);
16577                 d.innerHTML = v;
16578                 this.cleanUpPaste();
16579                 this.el.dom.value = d.innerHTML;
16580                 this.owner.fireEvent('push', this, v);
16581             }
16582         }
16583     },
16584
16585     // private
16586     deferFocus : function(){
16587         this.focus.defer(10, this);
16588     },
16589
16590     // doc'ed in Field
16591     focus : function(){
16592         if(this.win && !this.sourceEditMode){
16593             this.win.focus();
16594         }else{
16595             this.el.focus();
16596         }
16597     },
16598     
16599     assignDocWin: function()
16600     {
16601         var iframe = this.iframe;
16602         
16603          if(Roo.isIE){
16604             this.doc = iframe.contentWindow.document;
16605             this.win = iframe.contentWindow;
16606         } else {
16607 //            if (!Roo.get(this.frameId)) {
16608 //                return;
16609 //            }
16610 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
16611 //            this.win = Roo.get(this.frameId).dom.contentWindow;
16612             
16613             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
16614                 return;
16615             }
16616             
16617             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
16618             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
16619         }
16620     },
16621     
16622     // private
16623     initEditor : function(){
16624         //console.log("INIT EDITOR");
16625         this.assignDocWin();
16626         
16627         
16628         
16629         this.doc.designMode="on";
16630         this.doc.open();
16631         this.doc.write(this.getDocMarkup());
16632         this.doc.close();
16633         
16634         var dbody = (this.doc.body || this.doc.documentElement);
16635         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
16636         // this copies styles from the containing element into thsi one..
16637         // not sure why we need all of this..
16638         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
16639         
16640         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
16641         //ss['background-attachment'] = 'fixed'; // w3c
16642         dbody.bgProperties = 'fixed'; // ie
16643         //Roo.DomHelper.applyStyles(dbody, ss);
16644         Roo.EventManager.on(this.doc, {
16645             //'mousedown': this.onEditorEvent,
16646             'mouseup': this.onEditorEvent,
16647             'dblclick': this.onEditorEvent,
16648             'click': this.onEditorEvent,
16649             'keyup': this.onEditorEvent,
16650             buffer:100,
16651             scope: this
16652         });
16653         if(Roo.isGecko){
16654             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
16655         }
16656         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
16657             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
16658         }
16659         this.initialized = true;
16660
16661         this.owner.fireEvent('initialize', this);
16662         this.pushValue();
16663     },
16664
16665     // private
16666     onDestroy : function(){
16667         
16668         
16669         
16670         if(this.rendered){
16671             
16672             //for (var i =0; i < this.toolbars.length;i++) {
16673             //    // fixme - ask toolbars for heights?
16674             //    this.toolbars[i].onDestroy();
16675            // }
16676             
16677             //this.wrap.dom.innerHTML = '';
16678             //this.wrap.remove();
16679         }
16680     },
16681
16682     // private
16683     onFirstFocus : function(){
16684         
16685         this.assignDocWin();
16686         
16687         
16688         this.activated = true;
16689          
16690     
16691         if(Roo.isGecko){ // prevent silly gecko errors
16692             this.win.focus();
16693             var s = this.win.getSelection();
16694             if(!s.focusNode || s.focusNode.nodeType != 3){
16695                 var r = s.getRangeAt(0);
16696                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
16697                 r.collapse(true);
16698                 this.deferFocus();
16699             }
16700             try{
16701                 this.execCmd('useCSS', true);
16702                 this.execCmd('styleWithCSS', false);
16703             }catch(e){}
16704         }
16705         this.owner.fireEvent('activate', this);
16706     },
16707
16708     // private
16709     adjustFont: function(btn){
16710         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
16711         //if(Roo.isSafari){ // safari
16712         //    adjust *= 2;
16713        // }
16714         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
16715         if(Roo.isSafari){ // safari
16716             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
16717             v =  (v < 10) ? 10 : v;
16718             v =  (v > 48) ? 48 : v;
16719             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
16720             
16721         }
16722         
16723         
16724         v = Math.max(1, v+adjust);
16725         
16726         this.execCmd('FontSize', v  );
16727     },
16728
16729     onEditorEvent : function(e){
16730         this.owner.fireEvent('editorevent', this, e);
16731       //  this.updateToolbar();
16732         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
16733     },
16734
16735     insertTag : function(tg)
16736     {
16737         // could be a bit smarter... -> wrap the current selected tRoo..
16738         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
16739             
16740             range = this.createRange(this.getSelection());
16741             var wrappingNode = this.doc.createElement(tg.toLowerCase());
16742             wrappingNode.appendChild(range.extractContents());
16743             range.insertNode(wrappingNode);
16744
16745             return;
16746             
16747             
16748             
16749         }
16750         this.execCmd("formatblock",   tg);
16751         
16752     },
16753     
16754     insertText : function(txt)
16755     {
16756         
16757         
16758         var range = this.createRange();
16759         range.deleteContents();
16760                //alert(Sender.getAttribute('label'));
16761                
16762         range.insertNode(this.doc.createTextNode(txt));
16763     } ,
16764     
16765      
16766
16767     /**
16768      * Executes a Midas editor command on the editor document and performs necessary focus and
16769      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
16770      * @param {String} cmd The Midas command
16771      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16772      */
16773     relayCmd : function(cmd, value){
16774         this.win.focus();
16775         this.execCmd(cmd, value);
16776         this.owner.fireEvent('editorevent', this);
16777         //this.updateToolbar();
16778         this.owner.deferFocus();
16779     },
16780
16781     /**
16782      * Executes a Midas editor command directly on the editor document.
16783      * For visual commands, you should use {@link #relayCmd} instead.
16784      * <b>This should only be called after the editor is initialized.</b>
16785      * @param {String} cmd The Midas command
16786      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16787      */
16788     execCmd : function(cmd, value){
16789         this.doc.execCommand(cmd, false, value === undefined ? null : value);
16790         this.syncValue();
16791     },
16792  
16793  
16794    
16795     /**
16796      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
16797      * to insert tRoo.
16798      * @param {String} text | dom node.. 
16799      */
16800     insertAtCursor : function(text)
16801     {
16802         
16803         
16804         
16805         if(!this.activated){
16806             return;
16807         }
16808         /*
16809         if(Roo.isIE){
16810             this.win.focus();
16811             var r = this.doc.selection.createRange();
16812             if(r){
16813                 r.collapse(true);
16814                 r.pasteHTML(text);
16815                 this.syncValue();
16816                 this.deferFocus();
16817             
16818             }
16819             return;
16820         }
16821         */
16822         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
16823             this.win.focus();
16824             
16825             
16826             // from jquery ui (MIT licenced)
16827             var range, node;
16828             var win = this.win;
16829             
16830             if (win.getSelection && win.getSelection().getRangeAt) {
16831                 range = win.getSelection().getRangeAt(0);
16832                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
16833                 range.insertNode(node);
16834             } else if (win.document.selection && win.document.selection.createRange) {
16835                 // no firefox support
16836                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
16837                 win.document.selection.createRange().pasteHTML(txt);
16838             } else {
16839                 // no firefox support
16840                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
16841                 this.execCmd('InsertHTML', txt);
16842             } 
16843             
16844             this.syncValue();
16845             
16846             this.deferFocus();
16847         }
16848     },
16849  // private
16850     mozKeyPress : function(e){
16851         if(e.ctrlKey){
16852             var c = e.getCharCode(), cmd;
16853           
16854             if(c > 0){
16855                 c = String.fromCharCode(c).toLowerCase();
16856                 switch(c){
16857                     case 'b':
16858                         cmd = 'bold';
16859                         break;
16860                     case 'i':
16861                         cmd = 'italic';
16862                         break;
16863                     
16864                     case 'u':
16865                         cmd = 'underline';
16866                         break;
16867                     
16868                     case 'v':
16869                         this.cleanUpPaste.defer(100, this);
16870                         return;
16871                         
16872                 }
16873                 if(cmd){
16874                     this.win.focus();
16875                     this.execCmd(cmd);
16876                     this.deferFocus();
16877                     e.preventDefault();
16878                 }
16879                 
16880             }
16881         }
16882     },
16883
16884     // private
16885     fixKeys : function(){ // load time branching for fastest keydown performance
16886         if(Roo.isIE){
16887             return function(e){
16888                 var k = e.getKey(), r;
16889                 if(k == e.TAB){
16890                     e.stopEvent();
16891                     r = this.doc.selection.createRange();
16892                     if(r){
16893                         r.collapse(true);
16894                         r.pasteHTML('&#160;&#160;&#160;&#160;');
16895                         this.deferFocus();
16896                     }
16897                     return;
16898                 }
16899                 
16900                 if(k == e.ENTER){
16901                     r = this.doc.selection.createRange();
16902                     if(r){
16903                         var target = r.parentElement();
16904                         if(!target || target.tagName.toLowerCase() != 'li'){
16905                             e.stopEvent();
16906                             r.pasteHTML('<br />');
16907                             r.collapse(false);
16908                             r.select();
16909                         }
16910                     }
16911                 }
16912                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
16913                     this.cleanUpPaste.defer(100, this);
16914                     return;
16915                 }
16916                 
16917                 
16918             };
16919         }else if(Roo.isOpera){
16920             return function(e){
16921                 var k = e.getKey();
16922                 if(k == e.TAB){
16923                     e.stopEvent();
16924                     this.win.focus();
16925                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
16926                     this.deferFocus();
16927                 }
16928                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
16929                     this.cleanUpPaste.defer(100, this);
16930                     return;
16931                 }
16932                 
16933             };
16934         }else if(Roo.isSafari){
16935             return function(e){
16936                 var k = e.getKey();
16937                 
16938                 if(k == e.TAB){
16939                     e.stopEvent();
16940                     this.execCmd('InsertText','\t');
16941                     this.deferFocus();
16942                     return;
16943                 }
16944                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
16945                     this.cleanUpPaste.defer(100, this);
16946                     return;
16947                 }
16948                 
16949              };
16950         }
16951     }(),
16952     
16953     getAllAncestors: function()
16954     {
16955         var p = this.getSelectedNode();
16956         var a = [];
16957         if (!p) {
16958             a.push(p); // push blank onto stack..
16959             p = this.getParentElement();
16960         }
16961         
16962         
16963         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
16964             a.push(p);
16965             p = p.parentNode;
16966         }
16967         a.push(this.doc.body);
16968         return a;
16969     },
16970     lastSel : false,
16971     lastSelNode : false,
16972     
16973     
16974     getSelection : function() 
16975     {
16976         this.assignDocWin();
16977         return Roo.isIE ? this.doc.selection : this.win.getSelection();
16978     },
16979     
16980     getSelectedNode: function() 
16981     {
16982         // this may only work on Gecko!!!
16983         
16984         // should we cache this!!!!
16985         
16986         
16987         
16988          
16989         var range = this.createRange(this.getSelection()).cloneRange();
16990         
16991         if (Roo.isIE) {
16992             var parent = range.parentElement();
16993             while (true) {
16994                 var testRange = range.duplicate();
16995                 testRange.moveToElementText(parent);
16996                 if (testRange.inRange(range)) {
16997                     break;
16998                 }
16999                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
17000                     break;
17001                 }
17002                 parent = parent.parentElement;
17003             }
17004             return parent;
17005         }
17006         
17007         // is ancestor a text element.
17008         var ac =  range.commonAncestorContainer;
17009         if (ac.nodeType == 3) {
17010             ac = ac.parentNode;
17011         }
17012         
17013         var ar = ac.childNodes;
17014          
17015         var nodes = [];
17016         var other_nodes = [];
17017         var has_other_nodes = false;
17018         for (var i=0;i<ar.length;i++) {
17019             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
17020                 continue;
17021             }
17022             // fullly contained node.
17023             
17024             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
17025                 nodes.push(ar[i]);
17026                 continue;
17027             }
17028             
17029             // probably selected..
17030             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
17031                 other_nodes.push(ar[i]);
17032                 continue;
17033             }
17034             // outer..
17035             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
17036                 continue;
17037             }
17038             
17039             
17040             has_other_nodes = true;
17041         }
17042         if (!nodes.length && other_nodes.length) {
17043             nodes= other_nodes;
17044         }
17045         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
17046             return false;
17047         }
17048         
17049         return nodes[0];
17050     },
17051     createRange: function(sel)
17052     {
17053         // this has strange effects when using with 
17054         // top toolbar - not sure if it's a great idea.
17055         //this.editor.contentWindow.focus();
17056         if (typeof sel != "undefined") {
17057             try {
17058                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
17059             } catch(e) {
17060                 return this.doc.createRange();
17061             }
17062         } else {
17063             return this.doc.createRange();
17064         }
17065     },
17066     getParentElement: function()
17067     {
17068         
17069         this.assignDocWin();
17070         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
17071         
17072         var range = this.createRange(sel);
17073          
17074         try {
17075             var p = range.commonAncestorContainer;
17076             while (p.nodeType == 3) { // text node
17077                 p = p.parentNode;
17078             }
17079             return p;
17080         } catch (e) {
17081             return null;
17082         }
17083     
17084     },
17085     /***
17086      *
17087      * Range intersection.. the hard stuff...
17088      *  '-1' = before
17089      *  '0' = hits..
17090      *  '1' = after.
17091      *         [ -- selected range --- ]
17092      *   [fail]                        [fail]
17093      *
17094      *    basically..
17095      *      if end is before start or  hits it. fail.
17096      *      if start is after end or hits it fail.
17097      *
17098      *   if either hits (but other is outside. - then it's not 
17099      *   
17100      *    
17101      **/
17102     
17103     
17104     // @see http://www.thismuchiknow.co.uk/?p=64.
17105     rangeIntersectsNode : function(range, node)
17106     {
17107         var nodeRange = node.ownerDocument.createRange();
17108         try {
17109             nodeRange.selectNode(node);
17110         } catch (e) {
17111             nodeRange.selectNodeContents(node);
17112         }
17113     
17114         var rangeStartRange = range.cloneRange();
17115         rangeStartRange.collapse(true);
17116     
17117         var rangeEndRange = range.cloneRange();
17118         rangeEndRange.collapse(false);
17119     
17120         var nodeStartRange = nodeRange.cloneRange();
17121         nodeStartRange.collapse(true);
17122     
17123         var nodeEndRange = nodeRange.cloneRange();
17124         nodeEndRange.collapse(false);
17125     
17126         return rangeStartRange.compareBoundaryPoints(
17127                  Range.START_TO_START, nodeEndRange) == -1 &&
17128                rangeEndRange.compareBoundaryPoints(
17129                  Range.START_TO_START, nodeStartRange) == 1;
17130         
17131          
17132     },
17133     rangeCompareNode : function(range, node)
17134     {
17135         var nodeRange = node.ownerDocument.createRange();
17136         try {
17137             nodeRange.selectNode(node);
17138         } catch (e) {
17139             nodeRange.selectNodeContents(node);
17140         }
17141         
17142         
17143         range.collapse(true);
17144     
17145         nodeRange.collapse(true);
17146      
17147         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
17148         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
17149          
17150         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
17151         
17152         var nodeIsBefore   =  ss == 1;
17153         var nodeIsAfter    = ee == -1;
17154         
17155         if (nodeIsBefore && nodeIsAfter)
17156             return 0; // outer
17157         if (!nodeIsBefore && nodeIsAfter)
17158             return 1; //right trailed.
17159         
17160         if (nodeIsBefore && !nodeIsAfter)
17161             return 2;  // left trailed.
17162         // fully contined.
17163         return 3;
17164     },
17165
17166     // private? - in a new class?
17167     cleanUpPaste :  function()
17168     {
17169         // cleans up the whole document..
17170         Roo.log('cleanuppaste');
17171         
17172         this.cleanUpChildren(this.doc.body);
17173         var clean = this.cleanWordChars(this.doc.body.innerHTML);
17174         if (clean != this.doc.body.innerHTML) {
17175             this.doc.body.innerHTML = clean;
17176         }
17177         
17178     },
17179     
17180     cleanWordChars : function(input) {// change the chars to hex code
17181         var he = Roo.HtmlEditorCore;
17182         
17183         var output = input;
17184         Roo.each(he.swapCodes, function(sw) { 
17185             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
17186             
17187             output = output.replace(swapper, sw[1]);
17188         });
17189         
17190         return output;
17191     },
17192     
17193     
17194     cleanUpChildren : function (n)
17195     {
17196         if (!n.childNodes.length) {
17197             return;
17198         }
17199         for (var i = n.childNodes.length-1; i > -1 ; i--) {
17200            this.cleanUpChild(n.childNodes[i]);
17201         }
17202     },
17203     
17204     
17205         
17206     
17207     cleanUpChild : function (node)
17208     {
17209         var ed = this;
17210         //console.log(node);
17211         if (node.nodeName == "#text") {
17212             // clean up silly Windows -- stuff?
17213             return; 
17214         }
17215         if (node.nodeName == "#comment") {
17216             node.parentNode.removeChild(node);
17217             // clean up silly Windows -- stuff?
17218             return; 
17219         }
17220         
17221         if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1 && this.clearUp) {
17222             // remove node.
17223             node.parentNode.removeChild(node);
17224             return;
17225             
17226         }
17227         
17228         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
17229         
17230         // remove <a name=....> as rendering on yahoo mailer is borked with this.
17231         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
17232         
17233         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
17234         //    remove_keep_children = true;
17235         //}
17236         
17237         if (remove_keep_children) {
17238             this.cleanUpChildren(node);
17239             // inserts everything just before this node...
17240             while (node.childNodes.length) {
17241                 var cn = node.childNodes[0];
17242                 node.removeChild(cn);
17243                 node.parentNode.insertBefore(cn, node);
17244             }
17245             node.parentNode.removeChild(node);
17246             return;
17247         }
17248         
17249         if (!node.attributes || !node.attributes.length) {
17250             this.cleanUpChildren(node);
17251             return;
17252         }
17253         
17254         function cleanAttr(n,v)
17255         {
17256             
17257             if (v.match(/^\./) || v.match(/^\//)) {
17258                 return;
17259             }
17260             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
17261                 return;
17262             }
17263             if (v.match(/^#/)) {
17264                 return;
17265             }
17266 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
17267             node.removeAttribute(n);
17268             
17269         }
17270         
17271         function cleanStyle(n,v)
17272         {
17273             if (v.match(/expression/)) { //XSS?? should we even bother..
17274                 node.removeAttribute(n);
17275                 return;
17276             }
17277             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
17278             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
17279             
17280             
17281             var parts = v.split(/;/);
17282             var clean = [];
17283             
17284             Roo.each(parts, function(p) {
17285                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
17286                 if (!p.length) {
17287                     return true;
17288                 }
17289                 var l = p.split(':').shift().replace(/\s+/g,'');
17290                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
17291                 
17292                 if ( cblack.indexOf(l) > -1) {
17293 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17294                     //node.removeAttribute(n);
17295                     return true;
17296                 }
17297                 //Roo.log()
17298                 // only allow 'c whitelisted system attributes'
17299                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
17300 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17301                     //node.removeAttribute(n);
17302                     return true;
17303                 }
17304                 
17305                 
17306                  
17307                 
17308                 clean.push(p);
17309                 return true;
17310             });
17311             if (clean.length) { 
17312                 node.setAttribute(n, clean.join(';'));
17313             } else {
17314                 node.removeAttribute(n);
17315             }
17316             
17317         }
17318         
17319         
17320         for (var i = node.attributes.length-1; i > -1 ; i--) {
17321             var a = node.attributes[i];
17322             //console.log(a);
17323             
17324             if (a.name.toLowerCase().substr(0,2)=='on')  {
17325                 node.removeAttribute(a.name);
17326                 continue;
17327             }
17328             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
17329                 node.removeAttribute(a.name);
17330                 continue;
17331             }
17332             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
17333                 cleanAttr(a.name,a.value); // fixme..
17334                 continue;
17335             }
17336             if (a.name == 'style') {
17337                 cleanStyle(a.name,a.value);
17338                 continue;
17339             }
17340             /// clean up MS crap..
17341             // tecnically this should be a list of valid class'es..
17342             
17343             
17344             if (a.name == 'class') {
17345                 if (a.value.match(/^Mso/)) {
17346                     node.className = '';
17347                 }
17348                 
17349                 if (a.value.match(/body/)) {
17350                     node.className = '';
17351                 }
17352                 continue;
17353             }
17354             
17355             // style cleanup!?
17356             // class cleanup?
17357             
17358         }
17359         
17360         
17361         this.cleanUpChildren(node);
17362         
17363         
17364     },
17365     /**
17366      * Clean up MS wordisms...
17367      */
17368     cleanWord : function(node)
17369     {
17370         var _t = this;
17371         var cleanWordChildren = function()
17372         {
17373             if (!node.childNodes.length) {
17374                 return;
17375             }
17376             for (var i = node.childNodes.length-1; i > -1 ; i--) {
17377                _t.cleanWord(node.childNodes[i]);
17378             }
17379         }
17380         
17381         
17382         if (!node) {
17383             this.cleanWord(this.doc.body);
17384             return;
17385         }
17386         if (node.nodeName == "#text") {
17387             // clean up silly Windows -- stuff?
17388             return; 
17389         }
17390         if (node.nodeName == "#comment") {
17391             node.parentNode.removeChild(node);
17392             // clean up silly Windows -- stuff?
17393             return; 
17394         }
17395         
17396         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
17397             node.parentNode.removeChild(node);
17398             return;
17399         }
17400         
17401         // remove - but keep children..
17402         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
17403             while (node.childNodes.length) {
17404                 var cn = node.childNodes[0];
17405                 node.removeChild(cn);
17406                 node.parentNode.insertBefore(cn, node);
17407             }
17408             node.parentNode.removeChild(node);
17409             cleanWordChildren();
17410             return;
17411         }
17412         // clean styles
17413         if (node.className.length) {
17414             
17415             var cn = node.className.split(/\W+/);
17416             var cna = [];
17417             Roo.each(cn, function(cls) {
17418                 if (cls.match(/Mso[a-zA-Z]+/)) {
17419                     return;
17420                 }
17421                 cna.push(cls);
17422             });
17423             node.className = cna.length ? cna.join(' ') : '';
17424             if (!cna.length) {
17425                 node.removeAttribute("class");
17426             }
17427         }
17428         
17429         if (node.hasAttribute("lang")) {
17430             node.removeAttribute("lang");
17431         }
17432         
17433         if (node.hasAttribute("style")) {
17434             
17435             var styles = node.getAttribute("style").split(";");
17436             var nstyle = [];
17437             Roo.each(styles, function(s) {
17438                 if (!s.match(/:/)) {
17439                     return;
17440                 }
17441                 var kv = s.split(":");
17442                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
17443                     return;
17444                 }
17445                 // what ever is left... we allow.
17446                 nstyle.push(s);
17447             });
17448             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
17449             if (!nstyle.length) {
17450                 node.removeAttribute('style');
17451             }
17452         }
17453         
17454         cleanWordChildren();
17455         
17456         
17457     },
17458     domToHTML : function(currentElement, depth, nopadtext) {
17459         
17460             depth = depth || 0;
17461             nopadtext = nopadtext || false;
17462         
17463             if (!currentElement) {
17464                 return this.domToHTML(this.doc.body);
17465             }
17466             
17467             //Roo.log(currentElement);
17468             var j;
17469             var allText = false;
17470             var nodeName = currentElement.nodeName;
17471             var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
17472             
17473             if  (nodeName == '#text') {
17474                 return currentElement.nodeValue;
17475             }
17476             
17477             
17478             var ret = '';
17479             if (nodeName != 'BODY') {
17480                  
17481                 var i = 0;
17482                 // Prints the node tagName, such as <A>, <IMG>, etc
17483                 if (tagName) {
17484                     var attr = [];
17485                     for(i = 0; i < currentElement.attributes.length;i++) {
17486                         // quoting?
17487                         var aname = currentElement.attributes.item(i).name;
17488                         if (!currentElement.attributes.item(i).value.length) {
17489                             continue;
17490                         }
17491                         attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
17492                     }
17493                     
17494                     ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
17495                 } 
17496                 else {
17497                     
17498                     // eack
17499                 }
17500             } else {
17501                 tagName = false;
17502             }
17503             if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
17504                 return ret;
17505             }
17506             if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
17507                 nopadtext = true;
17508             }
17509             
17510             
17511             // Traverse the tree
17512             i = 0;
17513             var currentElementChild = currentElement.childNodes.item(i);
17514             var allText = true;
17515             var innerHTML  = '';
17516             lastnode = '';
17517             while (currentElementChild) {
17518                 // Formatting code (indent the tree so it looks nice on the screen)
17519                 var nopad = nopadtext;
17520                 if (lastnode == 'SPAN') {
17521                     nopad  = true;
17522                 }
17523                 // text
17524                 if  (currentElementChild.nodeName == '#text') {
17525                     var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
17526                     if (!nopad && toadd.length > 80) {
17527                         innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
17528                     }
17529                     innerHTML  += toadd;
17530                     
17531                     i++;
17532                     currentElementChild = currentElement.childNodes.item(i);
17533                     lastNode = '';
17534                     continue;
17535                 }
17536                 allText = false;
17537                 
17538                 innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
17539                     
17540                 // Recursively traverse the tree structure of the child node
17541                 innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
17542                 lastnode = currentElementChild.nodeName;
17543                 i++;
17544                 currentElementChild=currentElement.childNodes.item(i);
17545             }
17546             
17547             ret += innerHTML;
17548             
17549             if (!allText) {
17550                     // The remaining code is mostly for formatting the tree
17551                 ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
17552             }
17553             
17554             
17555             if (tagName) {
17556                 ret+= "</"+tagName+">";
17557             }
17558             return ret;
17559             
17560         }
17561     
17562     // hide stuff that is not compatible
17563     /**
17564      * @event blur
17565      * @hide
17566      */
17567     /**
17568      * @event change
17569      * @hide
17570      */
17571     /**
17572      * @event focus
17573      * @hide
17574      */
17575     /**
17576      * @event specialkey
17577      * @hide
17578      */
17579     /**
17580      * @cfg {String} fieldClass @hide
17581      */
17582     /**
17583      * @cfg {String} focusClass @hide
17584      */
17585     /**
17586      * @cfg {String} autoCreate @hide
17587      */
17588     /**
17589      * @cfg {String} inputType @hide
17590      */
17591     /**
17592      * @cfg {String} invalidClass @hide
17593      */
17594     /**
17595      * @cfg {String} invalidText @hide
17596      */
17597     /**
17598      * @cfg {String} msgFx @hide
17599      */
17600     /**
17601      * @cfg {String} validateOnBlur @hide
17602      */
17603 });
17604
17605 Roo.HtmlEditorCore.white = [
17606         'area', 'br', 'img', 'input', 'hr', 'wbr',
17607         
17608        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
17609        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
17610        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
17611        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
17612        'table',   'ul',         'xmp', 
17613        
17614        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
17615       'thead',   'tr', 
17616      
17617       'dir', 'menu', 'ol', 'ul', 'dl',
17618        
17619       'embed',  'object'
17620 ];
17621
17622
17623 Roo.HtmlEditorCore.black = [
17624     //    'embed',  'object', // enable - backend responsiblity to clean thiese
17625         'applet', // 
17626         'base',   'basefont', 'bgsound', 'blink',  'body', 
17627         'frame',  'frameset', 'head',    'html',   'ilayer', 
17628         'iframe', 'layer',  'link',     'meta',    'object',   
17629         'script', 'style' ,'title',  'xml' // clean later..
17630 ];
17631 Roo.HtmlEditorCore.clean = [
17632     'script', 'style', 'title', 'xml'
17633 ];
17634 Roo.HtmlEditorCore.remove = [
17635     'font'
17636 ];
17637 // attributes..
17638
17639 Roo.HtmlEditorCore.ablack = [
17640     'on'
17641 ];
17642     
17643 Roo.HtmlEditorCore.aclean = [ 
17644     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
17645 ];
17646
17647 // protocols..
17648 Roo.HtmlEditorCore.pwhite= [
17649         'http',  'https',  'mailto'
17650 ];
17651
17652 // white listed style attributes.
17653 Roo.HtmlEditorCore.cwhite= [
17654       //  'text-align', /// default is to allow most things..
17655       
17656          
17657 //        'font-size'//??
17658 ];
17659
17660 // black listed style attributes.
17661 Roo.HtmlEditorCore.cblack= [
17662       //  'font-size' -- this can be set by the project 
17663 ];
17664
17665
17666 Roo.HtmlEditorCore.swapCodes   =[ 
17667     [    8211, "--" ], 
17668     [    8212, "--" ], 
17669     [    8216,  "'" ],  
17670     [    8217, "'" ],  
17671     [    8220, '"' ],  
17672     [    8221, '"' ],  
17673     [    8226, "*" ],  
17674     [    8230, "..." ]
17675 ]; 
17676
17677     /*
17678  * - LGPL
17679  *
17680  * HtmlEditor
17681  * 
17682  */
17683
17684 /**
17685  * @class Roo.bootstrap.HtmlEditor
17686  * @extends Roo.bootstrap.TextArea
17687  * Bootstrap HtmlEditor class
17688
17689  * @constructor
17690  * Create a new HtmlEditor
17691  * @param {Object} config The config object
17692  */
17693
17694 Roo.bootstrap.HtmlEditor = function(config){
17695     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
17696     if (!this.toolbars) {
17697         this.toolbars = [];
17698     }
17699     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
17700     this.addEvents({
17701             /**
17702              * @event initialize
17703              * Fires when the editor is fully initialized (including the iframe)
17704              * @param {HtmlEditor} this
17705              */
17706             initialize: true,
17707             /**
17708              * @event activate
17709              * Fires when the editor is first receives the focus. Any insertion must wait
17710              * until after this event.
17711              * @param {HtmlEditor} this
17712              */
17713             activate: true,
17714              /**
17715              * @event beforesync
17716              * Fires before the textarea is updated with content from the editor iframe. Return false
17717              * to cancel the sync.
17718              * @param {HtmlEditor} this
17719              * @param {String} html
17720              */
17721             beforesync: true,
17722              /**
17723              * @event beforepush
17724              * Fires before the iframe editor is updated with content from the textarea. Return false
17725              * to cancel the push.
17726              * @param {HtmlEditor} this
17727              * @param {String} html
17728              */
17729             beforepush: true,
17730              /**
17731              * @event sync
17732              * Fires when the textarea is updated with content from the editor iframe.
17733              * @param {HtmlEditor} this
17734              * @param {String} html
17735              */
17736             sync: true,
17737              /**
17738              * @event push
17739              * Fires when the iframe editor is updated with content from the textarea.
17740              * @param {HtmlEditor} this
17741              * @param {String} html
17742              */
17743             push: true,
17744              /**
17745              * @event editmodechange
17746              * Fires when the editor switches edit modes
17747              * @param {HtmlEditor} this
17748              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
17749              */
17750             editmodechange: true,
17751             /**
17752              * @event editorevent
17753              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
17754              * @param {HtmlEditor} this
17755              */
17756             editorevent: true,
17757             /**
17758              * @event firstfocus
17759              * Fires when on first focus - needed by toolbars..
17760              * @param {HtmlEditor} this
17761              */
17762             firstfocus: true,
17763             /**
17764              * @event autosave
17765              * Auto save the htmlEditor value as a file into Events
17766              * @param {HtmlEditor} this
17767              */
17768             autosave: true,
17769             /**
17770              * @event savedpreview
17771              * preview the saved version of htmlEditor
17772              * @param {HtmlEditor} this
17773              */
17774             savedpreview: true
17775         });
17776 };
17777
17778
17779 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
17780     
17781     
17782       /**
17783      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
17784      */
17785     toolbars : false,
17786    
17787      /**
17788      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
17789      *                        Roo.resizable.
17790      */
17791     resizable : false,
17792      /**
17793      * @cfg {Number} height (in pixels)
17794      */   
17795     height: 300,
17796    /**
17797      * @cfg {Number} width (in pixels)
17798      */   
17799     width: false,
17800     
17801     /**
17802      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
17803      * 
17804      */
17805     stylesheets: false,
17806     
17807     // id of frame..
17808     frameId: false,
17809     
17810     // private properties
17811     validationEvent : false,
17812     deferHeight: true,
17813     initialized : false,
17814     activated : false,
17815     
17816     onFocus : Roo.emptyFn,
17817     iframePad:3,
17818     hideMode:'offsets',
17819     
17820     
17821     tbContainer : false,
17822     
17823     toolbarContainer :function() {
17824         return this.wrap.select('.x-html-editor-tb',true).first();
17825     },
17826
17827     /**
17828      * Protected method that will not generally be called directly. It
17829      * is called when the editor creates its toolbar. Override this method if you need to
17830      * add custom toolbar buttons.
17831      * @param {HtmlEditor} editor
17832      */
17833     createToolbar : function(){
17834         
17835         Roo.log("create toolbars");
17836         
17837         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
17838         this.toolbars[0].render(this.toolbarContainer());
17839         
17840         return;
17841         
17842 //        if (!editor.toolbars || !editor.toolbars.length) {
17843 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
17844 //        }
17845 //        
17846 //        for (var i =0 ; i < editor.toolbars.length;i++) {
17847 //            editor.toolbars[i] = Roo.factory(
17848 //                    typeof(editor.toolbars[i]) == 'string' ?
17849 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
17850 //                Roo.bootstrap.HtmlEditor);
17851 //            editor.toolbars[i].init(editor);
17852 //        }
17853     },
17854
17855      
17856     // private
17857     onRender : function(ct, position)
17858     {
17859        // Roo.log("Call onRender: " + this.xtype);
17860         var _t = this;
17861         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
17862       
17863         this.wrap = this.inputEl().wrap({
17864             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
17865         });
17866         
17867         this.editorcore.onRender(ct, position);
17868          
17869         if (this.resizable) {
17870             this.resizeEl = new Roo.Resizable(this.wrap, {
17871                 pinned : true,
17872                 wrap: true,
17873                 dynamic : true,
17874                 minHeight : this.height,
17875                 height: this.height,
17876                 handles : this.resizable,
17877                 width: this.width,
17878                 listeners : {
17879                     resize : function(r, w, h) {
17880                         _t.onResize(w,h); // -something
17881                     }
17882                 }
17883             });
17884             
17885         }
17886         this.createToolbar(this);
17887        
17888         
17889         if(!this.width && this.resizable){
17890             this.setSize(this.wrap.getSize());
17891         }
17892         if (this.resizeEl) {
17893             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
17894             // should trigger onReize..
17895         }
17896         
17897     },
17898
17899     // private
17900     onResize : function(w, h)
17901     {
17902         Roo.log('resize: ' +w + ',' + h );
17903         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
17904         var ew = false;
17905         var eh = false;
17906         
17907         if(this.inputEl() ){
17908             if(typeof w == 'number'){
17909                 var aw = w - this.wrap.getFrameWidth('lr');
17910                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
17911                 ew = aw;
17912             }
17913             if(typeof h == 'number'){
17914                  var tbh = -11;  // fixme it needs to tool bar size!
17915                 for (var i =0; i < this.toolbars.length;i++) {
17916                     // fixme - ask toolbars for heights?
17917                     tbh += this.toolbars[i].el.getHeight();
17918                     //if (this.toolbars[i].footer) {
17919                     //    tbh += this.toolbars[i].footer.el.getHeight();
17920                     //}
17921                 }
17922               
17923                 
17924                 
17925                 
17926                 
17927                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
17928                 ah -= 5; // knock a few pixes off for look..
17929                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
17930                 var eh = ah;
17931             }
17932         }
17933         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
17934         this.editorcore.onResize(ew,eh);
17935         
17936     },
17937
17938     /**
17939      * Toggles the editor between standard and source edit mode.
17940      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
17941      */
17942     toggleSourceEdit : function(sourceEditMode)
17943     {
17944         this.editorcore.toggleSourceEdit(sourceEditMode);
17945         
17946         if(this.editorcore.sourceEditMode){
17947             Roo.log('editor - showing textarea');
17948             
17949 //            Roo.log('in');
17950 //            Roo.log(this.syncValue());
17951             this.syncValue();
17952             this.inputEl().removeClass(['hide', 'x-hidden']);
17953             this.inputEl().dom.removeAttribute('tabIndex');
17954             this.inputEl().focus();
17955         }else{
17956             Roo.log('editor - hiding textarea');
17957 //            Roo.log('out')
17958 //            Roo.log(this.pushValue()); 
17959             this.pushValue();
17960             
17961             this.inputEl().addClass(['hide', 'x-hidden']);
17962             this.inputEl().dom.setAttribute('tabIndex', -1);
17963             //this.deferFocus();
17964         }
17965          
17966         if(this.resizable){
17967             this.setSize(this.wrap.getSize());
17968         }
17969         
17970         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
17971     },
17972  
17973     // private (for BoxComponent)
17974     adjustSize : Roo.BoxComponent.prototype.adjustSize,
17975
17976     // private (for BoxComponent)
17977     getResizeEl : function(){
17978         return this.wrap;
17979     },
17980
17981     // private (for BoxComponent)
17982     getPositionEl : function(){
17983         return this.wrap;
17984     },
17985
17986     // private
17987     initEvents : function(){
17988         this.originalValue = this.getValue();
17989     },
17990
17991 //    /**
17992 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
17993 //     * @method
17994 //     */
17995 //    markInvalid : Roo.emptyFn,
17996 //    /**
17997 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
17998 //     * @method
17999 //     */
18000 //    clearInvalid : Roo.emptyFn,
18001
18002     setValue : function(v){
18003         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
18004         this.editorcore.pushValue();
18005     },
18006
18007      
18008     // private
18009     deferFocus : function(){
18010         this.focus.defer(10, this);
18011     },
18012
18013     // doc'ed in Field
18014     focus : function(){
18015         this.editorcore.focus();
18016         
18017     },
18018       
18019
18020     // private
18021     onDestroy : function(){
18022         
18023         
18024         
18025         if(this.rendered){
18026             
18027             for (var i =0; i < this.toolbars.length;i++) {
18028                 // fixme - ask toolbars for heights?
18029                 this.toolbars[i].onDestroy();
18030             }
18031             
18032             this.wrap.dom.innerHTML = '';
18033             this.wrap.remove();
18034         }
18035     },
18036
18037     // private
18038     onFirstFocus : function(){
18039         //Roo.log("onFirstFocus");
18040         this.editorcore.onFirstFocus();
18041          for (var i =0; i < this.toolbars.length;i++) {
18042             this.toolbars[i].onFirstFocus();
18043         }
18044         
18045     },
18046     
18047     // private
18048     syncValue : function()
18049     {   
18050         this.editorcore.syncValue();
18051     },
18052     
18053     pushValue : function()
18054     {   
18055         this.editorcore.pushValue();
18056     }
18057      
18058     
18059     // hide stuff that is not compatible
18060     /**
18061      * @event blur
18062      * @hide
18063      */
18064     /**
18065      * @event change
18066      * @hide
18067      */
18068     /**
18069      * @event focus
18070      * @hide
18071      */
18072     /**
18073      * @event specialkey
18074      * @hide
18075      */
18076     /**
18077      * @cfg {String} fieldClass @hide
18078      */
18079     /**
18080      * @cfg {String} focusClass @hide
18081      */
18082     /**
18083      * @cfg {String} autoCreate @hide
18084      */
18085     /**
18086      * @cfg {String} inputType @hide
18087      */
18088     /**
18089      * @cfg {String} invalidClass @hide
18090      */
18091     /**
18092      * @cfg {String} invalidText @hide
18093      */
18094     /**
18095      * @cfg {String} msgFx @hide
18096      */
18097     /**
18098      * @cfg {String} validateOnBlur @hide
18099      */
18100 });
18101  
18102     
18103    
18104    
18105    
18106       
18107 Roo.namespace('Roo.bootstrap.htmleditor');
18108 /**
18109  * @class Roo.bootstrap.HtmlEditorToolbar1
18110  * Basic Toolbar
18111  * 
18112  * Usage:
18113  *
18114  new Roo.bootstrap.HtmlEditor({
18115     ....
18116     toolbars : [
18117         new Roo.bootstrap.HtmlEditorToolbar1({
18118             disable : { fonts: 1 , format: 1, ..., ... , ...],
18119             btns : [ .... ]
18120         })
18121     }
18122      
18123  * 
18124  * @cfg {Object} disable List of elements to disable..
18125  * @cfg {Array} btns List of additional buttons.
18126  * 
18127  * 
18128  * NEEDS Extra CSS? 
18129  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
18130  */
18131  
18132 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
18133 {
18134     
18135     Roo.apply(this, config);
18136     
18137     // default disabled, based on 'good practice'..
18138     this.disable = this.disable || {};
18139     Roo.applyIf(this.disable, {
18140         fontSize : true,
18141         colors : true,
18142         specialElements : true
18143     });
18144     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
18145     
18146     this.editor = config.editor;
18147     this.editorcore = config.editor.editorcore;
18148     
18149     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
18150     
18151     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
18152     // dont call parent... till later.
18153 }
18154 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
18155      
18156     bar : true,
18157     
18158     editor : false,
18159     editorcore : false,
18160     
18161     
18162     formats : [
18163         "p" ,  
18164         "h1","h2","h3","h4","h5","h6", 
18165         "pre", "code", 
18166         "abbr", "acronym", "address", "cite", "samp", "var",
18167         'div','span'
18168     ],
18169     
18170     onRender : function(ct, position)
18171     {
18172        // Roo.log("Call onRender: " + this.xtype);
18173         
18174        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
18175        Roo.log(this.el);
18176        this.el.dom.style.marginBottom = '0';
18177        var _this = this;
18178        var editorcore = this.editorcore;
18179        var editor= this.editor;
18180        
18181        var children = [];
18182        var btn = function(id,cmd , toggle, handler){
18183        
18184             var  event = toggle ? 'toggle' : 'click';
18185        
18186             var a = {
18187                 size : 'sm',
18188                 xtype: 'Button',
18189                 xns: Roo.bootstrap,
18190                 glyphicon : id,
18191                 cmd : id || cmd,
18192                 enableToggle:toggle !== false,
18193                 //html : 'submit'
18194                 pressed : toggle ? false : null,
18195                 listeners : {}
18196             }
18197             a.listeners[toggle ? 'toggle' : 'click'] = function() {
18198                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
18199             }
18200             children.push(a);
18201             return a;
18202        }
18203         
18204         var style = {
18205                 xtype: 'Button',
18206                 size : 'sm',
18207                 xns: Roo.bootstrap,
18208                 glyphicon : 'font',
18209                 //html : 'submit'
18210                 menu : {
18211                     xtype: 'Menu',
18212                     xns: Roo.bootstrap,
18213                     items:  []
18214                 }
18215         };
18216         Roo.each(this.formats, function(f) {
18217             style.menu.items.push({
18218                 xtype :'MenuItem',
18219                 xns: Roo.bootstrap,
18220                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
18221                 tagname : f,
18222                 listeners : {
18223                     click : function()
18224                     {
18225                         editorcore.insertTag(this.tagname);
18226                         editor.focus();
18227                     }
18228                 }
18229                 
18230             });
18231         });
18232          children.push(style);   
18233             
18234             
18235         btn('bold',false,true);
18236         btn('italic',false,true);
18237         btn('align-left', 'justifyleft',true);
18238         btn('align-center', 'justifycenter',true);
18239         btn('align-right' , 'justifyright',true);
18240         btn('link', false, false, function(btn) {
18241             //Roo.log("create link?");
18242             var url = prompt(this.createLinkText, this.defaultLinkValue);
18243             if(url && url != 'http:/'+'/'){
18244                 this.editorcore.relayCmd('createlink', url);
18245             }
18246         }),
18247         btn('list','insertunorderedlist',true);
18248         btn('pencil', false,true, function(btn){
18249                 Roo.log(this);
18250                 
18251                 this.toggleSourceEdit(btn.pressed);
18252         });
18253         /*
18254         var cog = {
18255                 xtype: 'Button',
18256                 size : 'sm',
18257                 xns: Roo.bootstrap,
18258                 glyphicon : 'cog',
18259                 //html : 'submit'
18260                 menu : {
18261                     xtype: 'Menu',
18262                     xns: Roo.bootstrap,
18263                     items:  []
18264                 }
18265         };
18266         
18267         cog.menu.items.push({
18268             xtype :'MenuItem',
18269             xns: Roo.bootstrap,
18270             html : Clean styles,
18271             tagname : f,
18272             listeners : {
18273                 click : function()
18274                 {
18275                     editorcore.insertTag(this.tagname);
18276                     editor.focus();
18277                 }
18278             }
18279             
18280         });
18281        */
18282         
18283          
18284        this.xtype = 'NavSimplebar';
18285         
18286         for(var i=0;i< children.length;i++) {
18287             
18288             this.buttons.add(this.addxtypeChild(children[i]));
18289             
18290         }
18291         
18292         editor.on('editorevent', this.updateToolbar, this);
18293     },
18294     onBtnClick : function(id)
18295     {
18296        this.editorcore.relayCmd(id);
18297        this.editorcore.focus();
18298     },
18299     
18300     /**
18301      * Protected method that will not generally be called directly. It triggers
18302      * a toolbar update by reading the markup state of the current selection in the editor.
18303      */
18304     updateToolbar: function(){
18305
18306         if(!this.editorcore.activated){
18307             this.editor.onFirstFocus(); // is this neeed?
18308             return;
18309         }
18310
18311         var btns = this.buttons; 
18312         var doc = this.editorcore.doc;
18313         btns.get('bold').setActive(doc.queryCommandState('bold'));
18314         btns.get('italic').setActive(doc.queryCommandState('italic'));
18315         //btns.get('underline').setActive(doc.queryCommandState('underline'));
18316         
18317         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
18318         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
18319         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
18320         
18321         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
18322         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
18323          /*
18324         
18325         var ans = this.editorcore.getAllAncestors();
18326         if (this.formatCombo) {
18327             
18328             
18329             var store = this.formatCombo.store;
18330             this.formatCombo.setValue("");
18331             for (var i =0; i < ans.length;i++) {
18332                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
18333                     // select it..
18334                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
18335                     break;
18336                 }
18337             }
18338         }
18339         
18340         
18341         
18342         // hides menus... - so this cant be on a menu...
18343         Roo.bootstrap.MenuMgr.hideAll();
18344         */
18345         Roo.bootstrap.MenuMgr.hideAll();
18346         //this.editorsyncValue();
18347     },
18348     onFirstFocus: function() {
18349         this.buttons.each(function(item){
18350            item.enable();
18351         });
18352     },
18353     toggleSourceEdit : function(sourceEditMode){
18354         
18355           
18356         if(sourceEditMode){
18357             Roo.log("disabling buttons");
18358            this.buttons.each( function(item){
18359                 if(item.cmd != 'pencil'){
18360                     item.disable();
18361                 }
18362             });
18363           
18364         }else{
18365             Roo.log("enabling buttons");
18366             if(this.editorcore.initialized){
18367                 this.buttons.each( function(item){
18368                     item.enable();
18369                 });
18370             }
18371             
18372         }
18373         Roo.log("calling toggole on editor");
18374         // tell the editor that it's been pressed..
18375         this.editor.toggleSourceEdit(sourceEditMode);
18376        
18377     }
18378 });
18379
18380
18381
18382
18383
18384 /**
18385  * @class Roo.bootstrap.Table.AbstractSelectionModel
18386  * @extends Roo.util.Observable
18387  * Abstract base class for grid SelectionModels.  It provides the interface that should be
18388  * implemented by descendant classes.  This class should not be directly instantiated.
18389  * @constructor
18390  */
18391 Roo.bootstrap.Table.AbstractSelectionModel = function(){
18392     this.locked = false;
18393     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
18394 };
18395
18396
18397 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
18398     /** @ignore Called by the grid automatically. Do not call directly. */
18399     init : function(grid){
18400         this.grid = grid;
18401         this.initEvents();
18402     },
18403
18404     /**
18405      * Locks the selections.
18406      */
18407     lock : function(){
18408         this.locked = true;
18409     },
18410
18411     /**
18412      * Unlocks the selections.
18413      */
18414     unlock : function(){
18415         this.locked = false;
18416     },
18417
18418     /**
18419      * Returns true if the selections are locked.
18420      * @return {Boolean}
18421      */
18422     isLocked : function(){
18423         return this.locked;
18424     }
18425 });
18426 /**
18427  * @extends Roo.bootstrap.Table.AbstractSelectionModel
18428  * @class Roo.bootstrap.Table.RowSelectionModel
18429  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
18430  * It supports multiple selections and keyboard selection/navigation. 
18431  * @constructor
18432  * @param {Object} config
18433  */
18434
18435 Roo.bootstrap.Table.RowSelectionModel = function(config){
18436     Roo.apply(this, config);
18437     this.selections = new Roo.util.MixedCollection(false, function(o){
18438         return o.id;
18439     });
18440
18441     this.last = false;
18442     this.lastActive = false;
18443
18444     this.addEvents({
18445         /**
18446              * @event selectionchange
18447              * Fires when the selection changes
18448              * @param {SelectionModel} this
18449              */
18450             "selectionchange" : true,
18451         /**
18452              * @event afterselectionchange
18453              * Fires after the selection changes (eg. by key press or clicking)
18454              * @param {SelectionModel} this
18455              */
18456             "afterselectionchange" : true,
18457         /**
18458              * @event beforerowselect
18459              * Fires when a row is selected being selected, return false to cancel.
18460              * @param {SelectionModel} this
18461              * @param {Number} rowIndex The selected index
18462              * @param {Boolean} keepExisting False if other selections will be cleared
18463              */
18464             "beforerowselect" : true,
18465         /**
18466              * @event rowselect
18467              * Fires when a row is selected.
18468              * @param {SelectionModel} this
18469              * @param {Number} rowIndex The selected index
18470              * @param {Roo.data.Record} r The record
18471              */
18472             "rowselect" : true,
18473         /**
18474              * @event rowdeselect
18475              * Fires when a row is deselected.
18476              * @param {SelectionModel} this
18477              * @param {Number} rowIndex The selected index
18478              */
18479         "rowdeselect" : true
18480     });
18481     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
18482     this.locked = false;
18483 };
18484
18485 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
18486     /**
18487      * @cfg {Boolean} singleSelect
18488      * True to allow selection of only one row at a time (defaults to false)
18489      */
18490     singleSelect : false,
18491
18492     // private
18493     initEvents : function(){
18494
18495         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
18496             this.grid.on("mousedown", this.handleMouseDown, this);
18497         }else{ // allow click to work like normal
18498             this.grid.on("rowclick", this.handleDragableRowClick, this);
18499         }
18500
18501         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
18502             "up" : function(e){
18503                 if(!e.shiftKey){
18504                     this.selectPrevious(e.shiftKey);
18505                 }else if(this.last !== false && this.lastActive !== false){
18506                     var last = this.last;
18507                     this.selectRange(this.last,  this.lastActive-1);
18508                     this.grid.getView().focusRow(this.lastActive);
18509                     if(last !== false){
18510                         this.last = last;
18511                     }
18512                 }else{
18513                     this.selectFirstRow();
18514                 }
18515                 this.fireEvent("afterselectionchange", this);
18516             },
18517             "down" : function(e){
18518                 if(!e.shiftKey){
18519                     this.selectNext(e.shiftKey);
18520                 }else if(this.last !== false && this.lastActive !== false){
18521                     var last = this.last;
18522                     this.selectRange(this.last,  this.lastActive+1);
18523                     this.grid.getView().focusRow(this.lastActive);
18524                     if(last !== false){
18525                         this.last = last;
18526                     }
18527                 }else{
18528                     this.selectFirstRow();
18529                 }
18530                 this.fireEvent("afterselectionchange", this);
18531             },
18532             scope: this
18533         });
18534
18535         var view = this.grid.view;
18536         view.on("refresh", this.onRefresh, this);
18537         view.on("rowupdated", this.onRowUpdated, this);
18538         view.on("rowremoved", this.onRemove, this);
18539     },
18540
18541     // private
18542     onRefresh : function(){
18543         var ds = this.grid.dataSource, i, v = this.grid.view;
18544         var s = this.selections;
18545         s.each(function(r){
18546             if((i = ds.indexOfId(r.id)) != -1){
18547                 v.onRowSelect(i);
18548             }else{
18549                 s.remove(r);
18550             }
18551         });
18552     },
18553
18554     // private
18555     onRemove : function(v, index, r){
18556         this.selections.remove(r);
18557     },
18558
18559     // private
18560     onRowUpdated : function(v, index, r){
18561         if(this.isSelected(r)){
18562             v.onRowSelect(index);
18563         }
18564     },
18565
18566     /**
18567      * Select records.
18568      * @param {Array} records The records to select
18569      * @param {Boolean} keepExisting (optional) True to keep existing selections
18570      */
18571     selectRecords : function(records, keepExisting){
18572         if(!keepExisting){
18573             this.clearSelections();
18574         }
18575         var ds = this.grid.dataSource;
18576         for(var i = 0, len = records.length; i < len; i++){
18577             this.selectRow(ds.indexOf(records[i]), true);
18578         }
18579     },
18580
18581     /**
18582      * Gets the number of selected rows.
18583      * @return {Number}
18584      */
18585     getCount : function(){
18586         return this.selections.length;
18587     },
18588
18589     /**
18590      * Selects the first row in the grid.
18591      */
18592     selectFirstRow : function(){
18593         this.selectRow(0);
18594     },
18595
18596     /**
18597      * Select the last row.
18598      * @param {Boolean} keepExisting (optional) True to keep existing selections
18599      */
18600     selectLastRow : function(keepExisting){
18601         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
18602     },
18603
18604     /**
18605      * Selects the row immediately following the last selected row.
18606      * @param {Boolean} keepExisting (optional) True to keep existing selections
18607      */
18608     selectNext : function(keepExisting){
18609         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
18610             this.selectRow(this.last+1, keepExisting);
18611             this.grid.getView().focusRow(this.last);
18612         }
18613     },
18614
18615     /**
18616      * Selects the row that precedes the last selected row.
18617      * @param {Boolean} keepExisting (optional) True to keep existing selections
18618      */
18619     selectPrevious : function(keepExisting){
18620         if(this.last){
18621             this.selectRow(this.last-1, keepExisting);
18622             this.grid.getView().focusRow(this.last);
18623         }
18624     },
18625
18626     /**
18627      * Returns the selected records
18628      * @return {Array} Array of selected records
18629      */
18630     getSelections : function(){
18631         return [].concat(this.selections.items);
18632     },
18633
18634     /**
18635      * Returns the first selected record.
18636      * @return {Record}
18637      */
18638     getSelected : function(){
18639         return this.selections.itemAt(0);
18640     },
18641
18642
18643     /**
18644      * Clears all selections.
18645      */
18646     clearSelections : function(fast){
18647         if(this.locked) return;
18648         if(fast !== true){
18649             var ds = this.grid.dataSource;
18650             var s = this.selections;
18651             s.each(function(r){
18652                 this.deselectRow(ds.indexOfId(r.id));
18653             }, this);
18654             s.clear();
18655         }else{
18656             this.selections.clear();
18657         }
18658         this.last = false;
18659     },
18660
18661
18662     /**
18663      * Selects all rows.
18664      */
18665     selectAll : function(){
18666         if(this.locked) return;
18667         this.selections.clear();
18668         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
18669             this.selectRow(i, true);
18670         }
18671     },
18672
18673     /**
18674      * Returns True if there is a selection.
18675      * @return {Boolean}
18676      */
18677     hasSelection : function(){
18678         return this.selections.length > 0;
18679     },
18680
18681     /**
18682      * Returns True if the specified row is selected.
18683      * @param {Number/Record} record The record or index of the record to check
18684      * @return {Boolean}
18685      */
18686     isSelected : function(index){
18687         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
18688         return (r && this.selections.key(r.id) ? true : false);
18689     },
18690
18691     /**
18692      * Returns True if the specified record id is selected.
18693      * @param {String} id The id of record to check
18694      * @return {Boolean}
18695      */
18696     isIdSelected : function(id){
18697         return (this.selections.key(id) ? true : false);
18698     },
18699
18700     // private
18701     handleMouseDown : function(e, t){
18702         var view = this.grid.getView(), rowIndex;
18703         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
18704             return;
18705         };
18706         if(e.shiftKey && this.last !== false){
18707             var last = this.last;
18708             this.selectRange(last, rowIndex, e.ctrlKey);
18709             this.last = last; // reset the last
18710             view.focusRow(rowIndex);
18711         }else{
18712             var isSelected = this.isSelected(rowIndex);
18713             if(e.button !== 0 && isSelected){
18714                 view.focusRow(rowIndex);
18715             }else if(e.ctrlKey && isSelected){
18716                 this.deselectRow(rowIndex);
18717             }else if(!isSelected){
18718                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
18719                 view.focusRow(rowIndex);
18720             }
18721         }
18722         this.fireEvent("afterselectionchange", this);
18723     },
18724     // private
18725     handleDragableRowClick :  function(grid, rowIndex, e) 
18726     {
18727         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
18728             this.selectRow(rowIndex, false);
18729             grid.view.focusRow(rowIndex);
18730              this.fireEvent("afterselectionchange", this);
18731         }
18732     },
18733     
18734     /**
18735      * Selects multiple rows.
18736      * @param {Array} rows Array of the indexes of the row to select
18737      * @param {Boolean} keepExisting (optional) True to keep existing selections
18738      */
18739     selectRows : function(rows, keepExisting){
18740         if(!keepExisting){
18741             this.clearSelections();
18742         }
18743         for(var i = 0, len = rows.length; i < len; i++){
18744             this.selectRow(rows[i], true);
18745         }
18746     },
18747
18748     /**
18749      * Selects a range of rows. All rows in between startRow and endRow are also selected.
18750      * @param {Number} startRow The index of the first row in the range
18751      * @param {Number} endRow The index of the last row in the range
18752      * @param {Boolean} keepExisting (optional) True to retain existing selections
18753      */
18754     selectRange : function(startRow, endRow, keepExisting){
18755         if(this.locked) return;
18756         if(!keepExisting){
18757             this.clearSelections();
18758         }
18759         if(startRow <= endRow){
18760             for(var i = startRow; i <= endRow; i++){
18761                 this.selectRow(i, true);
18762             }
18763         }else{
18764             for(var i = startRow; i >= endRow; i--){
18765                 this.selectRow(i, true);
18766             }
18767         }
18768     },
18769
18770     /**
18771      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
18772      * @param {Number} startRow The index of the first row in the range
18773      * @param {Number} endRow The index of the last row in the range
18774      */
18775     deselectRange : function(startRow, endRow, preventViewNotify){
18776         if(this.locked) return;
18777         for(var i = startRow; i <= endRow; i++){
18778             this.deselectRow(i, preventViewNotify);
18779         }
18780     },
18781
18782     /**
18783      * Selects a row.
18784      * @param {Number} row The index of the row to select
18785      * @param {Boolean} keepExisting (optional) True to keep existing selections
18786      */
18787     selectRow : function(index, keepExisting, preventViewNotify){
18788         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
18789         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
18790             if(!keepExisting || this.singleSelect){
18791                 this.clearSelections();
18792             }
18793             var r = this.grid.dataSource.getAt(index);
18794             this.selections.add(r);
18795             this.last = this.lastActive = index;
18796             if(!preventViewNotify){
18797                 this.grid.getView().onRowSelect(index);
18798             }
18799             this.fireEvent("rowselect", this, index, r);
18800             this.fireEvent("selectionchange", this);
18801         }
18802     },
18803
18804     /**
18805      * Deselects a row.
18806      * @param {Number} row The index of the row to deselect
18807      */
18808     deselectRow : function(index, preventViewNotify){
18809         if(this.locked) return;
18810         if(this.last == index){
18811             this.last = false;
18812         }
18813         if(this.lastActive == index){
18814             this.lastActive = false;
18815         }
18816         var r = this.grid.dataSource.getAt(index);
18817         this.selections.remove(r);
18818         if(!preventViewNotify){
18819             this.grid.getView().onRowDeselect(index);
18820         }
18821         this.fireEvent("rowdeselect", this, index);
18822         this.fireEvent("selectionchange", this);
18823     },
18824
18825     // private
18826     restoreLast : function(){
18827         if(this._last){
18828             this.last = this._last;
18829         }
18830     },
18831
18832     // private
18833     acceptsNav : function(row, col, cm){
18834         return !cm.isHidden(col) && cm.isCellEditable(col, row);
18835     },
18836
18837     // private
18838     onEditorKey : function(field, e){
18839         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
18840         if(k == e.TAB){
18841             e.stopEvent();
18842             ed.completeEdit();
18843             if(e.shiftKey){
18844                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
18845             }else{
18846                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
18847             }
18848         }else if(k == e.ENTER && !e.ctrlKey){
18849             e.stopEvent();
18850             ed.completeEdit();
18851             if(e.shiftKey){
18852                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
18853             }else{
18854                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
18855             }
18856         }else if(k == e.ESC){
18857             ed.cancelEdit();
18858         }
18859         if(newCell){
18860             g.startEditing(newCell[0], newCell[1]);
18861         }
18862     }
18863 });/*
18864  * Based on:
18865  * Ext JS Library 1.1.1
18866  * Copyright(c) 2006-2007, Ext JS, LLC.
18867  *
18868  * Originally Released Under LGPL - original licence link has changed is not relivant.
18869  *
18870  * Fork - LGPL
18871  * <script type="text/javascript">
18872  */
18873  
18874 /**
18875  * @class Roo.bootstrap.PagingToolbar
18876  * @extends Roo.Row
18877  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
18878  * @constructor
18879  * Create a new PagingToolbar
18880  * @param {Object} config The config object
18881  */
18882 Roo.bootstrap.PagingToolbar = function(config)
18883 {
18884     // old args format still supported... - xtype is prefered..
18885         // created from xtype...
18886     var ds = config.dataSource;
18887     this.toolbarItems = [];
18888     if (config.items) {
18889         this.toolbarItems = config.items;
18890 //        config.items = [];
18891     }
18892     
18893     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
18894     this.ds = ds;
18895     this.cursor = 0;
18896     if (ds) { 
18897         this.bind(ds);
18898     }
18899     
18900     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
18901     
18902 };
18903
18904 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
18905     /**
18906      * @cfg {Roo.data.Store} dataSource
18907      * The underlying data store providing the paged data
18908      */
18909     /**
18910      * @cfg {String/HTMLElement/Element} container
18911      * container The id or element that will contain the toolbar
18912      */
18913     /**
18914      * @cfg {Boolean} displayInfo
18915      * True to display the displayMsg (defaults to false)
18916      */
18917     /**
18918      * @cfg {Number} pageSize
18919      * The number of records to display per page (defaults to 20)
18920      */
18921     pageSize: 20,
18922     /**
18923      * @cfg {String} displayMsg
18924      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
18925      */
18926     displayMsg : 'Displaying {0} - {1} of {2}',
18927     /**
18928      * @cfg {String} emptyMsg
18929      * The message to display when no records are found (defaults to "No data to display")
18930      */
18931     emptyMsg : 'No data to display',
18932     /**
18933      * Customizable piece of the default paging text (defaults to "Page")
18934      * @type String
18935      */
18936     beforePageText : "Page",
18937     /**
18938      * Customizable piece of the default paging text (defaults to "of %0")
18939      * @type String
18940      */
18941     afterPageText : "of {0}",
18942     /**
18943      * Customizable piece of the default paging text (defaults to "First Page")
18944      * @type String
18945      */
18946     firstText : "First Page",
18947     /**
18948      * Customizable piece of the default paging text (defaults to "Previous Page")
18949      * @type String
18950      */
18951     prevText : "Previous Page",
18952     /**
18953      * Customizable piece of the default paging text (defaults to "Next Page")
18954      * @type String
18955      */
18956     nextText : "Next Page",
18957     /**
18958      * Customizable piece of the default paging text (defaults to "Last Page")
18959      * @type String
18960      */
18961     lastText : "Last Page",
18962     /**
18963      * Customizable piece of the default paging text (defaults to "Refresh")
18964      * @type String
18965      */
18966     refreshText : "Refresh",
18967
18968     buttons : false,
18969     // private
18970     onRender : function(ct, position) 
18971     {
18972         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
18973         this.navgroup.parentId = this.id;
18974         this.navgroup.onRender(this.el, null);
18975         // add the buttons to the navgroup
18976         
18977         if(this.displayInfo){
18978             Roo.log(this.el.select('ul.navbar-nav',true).first());
18979             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
18980             this.displayEl = this.el.select('.x-paging-info', true).first();
18981 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
18982 //            this.displayEl = navel.el.select('span',true).first();
18983         }
18984         
18985         var _this = this;
18986         
18987         if(this.buttons){
18988             Roo.each(_this.buttons, function(e){
18989                Roo.factory(e).onRender(_this.el, null);
18990             });
18991         }
18992             
18993         Roo.each(_this.toolbarItems, function(e) {
18994             _this.navgroup.addItem(e);
18995         });
18996         
18997         this.first = this.navgroup.addItem({
18998             tooltip: this.firstText,
18999             cls: "prev",
19000             icon : 'fa fa-backward',
19001             disabled: true,
19002             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
19003         });
19004         
19005         this.prev =  this.navgroup.addItem({
19006             tooltip: this.prevText,
19007             cls: "prev",
19008             icon : 'fa fa-step-backward',
19009             disabled: true,
19010             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
19011         });
19012     //this.addSeparator();
19013         
19014         
19015         var field = this.navgroup.addItem( {
19016             tagtype : 'span',
19017             cls : 'x-paging-position',
19018             
19019             html : this.beforePageText  +
19020                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
19021                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
19022          } ); //?? escaped?
19023         
19024         this.field = field.el.select('input', true).first();
19025         this.field.on("keydown", this.onPagingKeydown, this);
19026         this.field.on("focus", function(){this.dom.select();});
19027     
19028     
19029         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
19030         //this.field.setHeight(18);
19031         //this.addSeparator();
19032         this.next = this.navgroup.addItem({
19033             tooltip: this.nextText,
19034             cls: "next",
19035             html : ' <i class="fa fa-step-forward">',
19036             disabled: true,
19037             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
19038         });
19039         this.last = this.navgroup.addItem({
19040             tooltip: this.lastText,
19041             icon : 'fa fa-forward',
19042             cls: "next",
19043             disabled: true,
19044             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
19045         });
19046     //this.addSeparator();
19047         this.loading = this.navgroup.addItem({
19048             tooltip: this.refreshText,
19049             icon: 'fa fa-refresh',
19050             
19051             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
19052         });
19053
19054     },
19055
19056     // private
19057     updateInfo : function(){
19058         if(this.displayEl){
19059             var count = this.ds.getCount();
19060             var msg = count == 0 ?
19061                 this.emptyMsg :
19062                 String.format(
19063                     this.displayMsg,
19064                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
19065                 );
19066             this.displayEl.update(msg);
19067         }
19068     },
19069
19070     // private
19071     onLoad : function(ds, r, o){
19072        this.cursor = o.params ? o.params.start : 0;
19073        var d = this.getPageData(),
19074             ap = d.activePage,
19075             ps = d.pages;
19076         
19077        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
19078        this.field.dom.value = ap;
19079        this.first.setDisabled(ap == 1);
19080        this.prev.setDisabled(ap == 1);
19081        this.next.setDisabled(ap == ps);
19082        this.last.setDisabled(ap == ps);
19083        this.loading.enable();
19084        this.updateInfo();
19085     },
19086
19087     // private
19088     getPageData : function(){
19089         var total = this.ds.getTotalCount();
19090         return {
19091             total : total,
19092             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
19093             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
19094         };
19095     },
19096
19097     // private
19098     onLoadError : function(){
19099         this.loading.enable();
19100     },
19101
19102     // private
19103     onPagingKeydown : function(e){
19104         var k = e.getKey();
19105         var d = this.getPageData();
19106         if(k == e.RETURN){
19107             var v = this.field.dom.value, pageNum;
19108             if(!v || isNaN(pageNum = parseInt(v, 10))){
19109                 this.field.dom.value = d.activePage;
19110                 return;
19111             }
19112             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
19113             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
19114             e.stopEvent();
19115         }
19116         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))
19117         {
19118           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
19119           this.field.dom.value = pageNum;
19120           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
19121           e.stopEvent();
19122         }
19123         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
19124         {
19125           var v = this.field.dom.value, pageNum; 
19126           var increment = (e.shiftKey) ? 10 : 1;
19127           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
19128             increment *= -1;
19129           if(!v || isNaN(pageNum = parseInt(v, 10))) {
19130             this.field.dom.value = d.activePage;
19131             return;
19132           }
19133           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
19134           {
19135             this.field.dom.value = parseInt(v, 10) + increment;
19136             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
19137             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
19138           }
19139           e.stopEvent();
19140         }
19141     },
19142
19143     // private
19144     beforeLoad : function(){
19145         if(this.loading){
19146             this.loading.disable();
19147         }
19148     },
19149
19150     // private
19151     onClick : function(which){
19152         var ds = this.ds;
19153         if (!ds) {
19154             return;
19155         }
19156         switch(which){
19157             case "first":
19158                 ds.load({params:{start: 0, limit: this.pageSize}});
19159             break;
19160             case "prev":
19161                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
19162             break;
19163             case "next":
19164                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
19165             break;
19166             case "last":
19167                 var total = ds.getTotalCount();
19168                 var extra = total % this.pageSize;
19169                 var lastStart = extra ? (total - extra) : total-this.pageSize;
19170                 ds.load({params:{start: lastStart, limit: this.pageSize}});
19171             break;
19172             case "refresh":
19173                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
19174             break;
19175         }
19176     },
19177
19178     /**
19179      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
19180      * @param {Roo.data.Store} store The data store to unbind
19181      */
19182     unbind : function(ds){
19183         ds.un("beforeload", this.beforeLoad, this);
19184         ds.un("load", this.onLoad, this);
19185         ds.un("loadexception", this.onLoadError, this);
19186         ds.un("remove", this.updateInfo, this);
19187         ds.un("add", this.updateInfo, this);
19188         this.ds = undefined;
19189     },
19190
19191     /**
19192      * Binds the paging toolbar to the specified {@link Roo.data.Store}
19193      * @param {Roo.data.Store} store The data store to bind
19194      */
19195     bind : function(ds){
19196         ds.on("beforeload", this.beforeLoad, this);
19197         ds.on("load", this.onLoad, this);
19198         ds.on("loadexception", this.onLoadError, this);
19199         ds.on("remove", this.updateInfo, this);
19200         ds.on("add", this.updateInfo, this);
19201         this.ds = ds;
19202     }
19203 });/*
19204  * - LGPL
19205  *
19206  * element
19207  * 
19208  */
19209
19210 /**
19211  * @class Roo.bootstrap.MessageBar
19212  * @extends Roo.bootstrap.Component
19213  * Bootstrap MessageBar class
19214  * @cfg {String} html contents of the MessageBar
19215  * @cfg {String} weight (info | success | warning | danger) default info
19216  * @cfg {String} beforeClass insert the bar before the given class
19217  * @cfg {Boolean} closable (true | false) default false
19218  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
19219  * 
19220  * @constructor
19221  * Create a new Element
19222  * @param {Object} config The config object
19223  */
19224
19225 Roo.bootstrap.MessageBar = function(config){
19226     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
19227 };
19228
19229 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
19230     
19231     html: '',
19232     weight: 'info',
19233     closable: false,
19234     fixed: false,
19235     beforeClass: 'bootstrap-sticky-wrap',
19236     
19237     getAutoCreate : function(){
19238         
19239         var cfg = {
19240             tag: 'div',
19241             cls: 'alert alert-dismissable alert-' + this.weight,
19242             cn: [
19243                 {
19244                     tag: 'span',
19245                     cls: 'message',
19246                     html: this.html || ''
19247                 }
19248             ]
19249         }
19250         
19251         if(this.fixed){
19252             cfg.cls += ' alert-messages-fixed';
19253         }
19254         
19255         if(this.closable){
19256             cfg.cn.push({
19257                 tag: 'button',
19258                 cls: 'close',
19259                 html: 'x'
19260             });
19261         }
19262         
19263         return cfg;
19264     },
19265     
19266     onRender : function(ct, position)
19267     {
19268         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19269         
19270         if(!this.el){
19271             var cfg = Roo.apply({},  this.getAutoCreate());
19272             cfg.id = Roo.id();
19273             
19274             if (this.cls) {
19275                 cfg.cls += ' ' + this.cls;
19276             }
19277             if (this.style) {
19278                 cfg.style = this.style;
19279             }
19280             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
19281             
19282             this.el.setVisibilityMode(Roo.Element.DISPLAY);
19283         }
19284         
19285         this.el.select('>button.close').on('click', this.hide, this);
19286         
19287     },
19288     
19289     show : function()
19290     {
19291         if (!this.rendered) {
19292             this.render();
19293         }
19294         
19295         this.el.show();
19296         
19297         this.fireEvent('show', this);
19298         
19299     },
19300     
19301     hide : function()
19302     {
19303         if (!this.rendered) {
19304             this.render();
19305         }
19306         
19307         this.el.hide();
19308         
19309         this.fireEvent('hide', this);
19310     },
19311     
19312     update : function()
19313     {
19314 //        var e = this.el.dom.firstChild;
19315 //        
19316 //        if(this.closable){
19317 //            e = e.nextSibling;
19318 //        }
19319 //        
19320 //        e.data = this.html || '';
19321
19322         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
19323     }
19324    
19325 });
19326
19327  
19328
19329      /*
19330  * - LGPL
19331  *
19332  * Graph
19333  * 
19334  */
19335
19336
19337 /**
19338  * @class Roo.bootstrap.Graph
19339  * @extends Roo.bootstrap.Component
19340  * Bootstrap Graph class
19341 > Prameters
19342  -sm {number} sm 4
19343  -md {number} md 5
19344  @cfg {String} graphtype  bar | vbar | pie
19345  @cfg {number} g_x coodinator | centre x (pie)
19346  @cfg {number} g_y coodinator | centre y (pie)
19347  @cfg {number} g_r radius (pie)
19348  @cfg {number} g_height height of the chart (respected by all elements in the set)
19349  @cfg {number} g_width width of the chart (respected by all elements in the set)
19350  @cfg {Object} title The title of the chart
19351     
19352  -{Array}  values
19353  -opts (object) options for the chart 
19354      o {
19355      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
19356      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
19357      o vgutter (number)
19358      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.
19359      o stacked (boolean) whether or not to tread values as in a stacked bar chart
19360      o to
19361      o stretch (boolean)
19362      o }
19363  -opts (object) options for the pie
19364      o{
19365      o cut
19366      o startAngle (number)
19367      o endAngle (number)
19368      } 
19369  *
19370  * @constructor
19371  * Create a new Input
19372  * @param {Object} config The config object
19373  */
19374
19375 Roo.bootstrap.Graph = function(config){
19376     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
19377     
19378     this.addEvents({
19379         // img events
19380         /**
19381          * @event click
19382          * The img click event for the img.
19383          * @param {Roo.EventObject} e
19384          */
19385         "click" : true
19386     });
19387 };
19388
19389 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
19390     
19391     sm: 4,
19392     md: 5,
19393     graphtype: 'bar',
19394     g_height: 250,
19395     g_width: 400,
19396     g_x: 50,
19397     g_y: 50,
19398     g_r: 30,
19399     opts:{
19400         //g_colors: this.colors,
19401         g_type: 'soft',
19402         g_gutter: '20%'
19403
19404     },
19405     title : false,
19406
19407     getAutoCreate : function(){
19408         
19409         var cfg = {
19410             tag: 'div',
19411             html : null
19412         }
19413         
19414         
19415         return  cfg;
19416     },
19417
19418     onRender : function(ct,position){
19419         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
19420         this.raphael = Raphael(this.el.dom);
19421         
19422                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19423                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19424                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19425                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
19426                 /*
19427                 r.text(160, 10, "Single Series Chart").attr(txtattr);
19428                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
19429                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
19430                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
19431                 
19432                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
19433                 r.barchart(330, 10, 300, 220, data1);
19434                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
19435                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
19436                 */
19437                 
19438                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19439                 // r.barchart(30, 30, 560, 250,  xdata, {
19440                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
19441                 //     axis : "0 0 1 1",
19442                 //     axisxlabels :  xdata
19443                 //     //yvalues : cols,
19444                    
19445                 // });
19446 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19447 //        
19448 //        this.load(null,xdata,{
19449 //                axis : "0 0 1 1",
19450 //                axisxlabels :  xdata
19451 //                });
19452
19453     },
19454
19455     load : function(graphtype,xdata,opts){
19456         this.raphael.clear();
19457         if(!graphtype) {
19458             graphtype = this.graphtype;
19459         }
19460         if(!opts){
19461             opts = this.opts;
19462         }
19463         var r = this.raphael,
19464             fin = function () {
19465                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
19466             },
19467             fout = function () {
19468                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
19469             },
19470             pfin = function() {
19471                 this.sector.stop();
19472                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
19473
19474                 if (this.label) {
19475                     this.label[0].stop();
19476                     this.label[0].attr({ r: 7.5 });
19477                     this.label[1].attr({ "font-weight": 800 });
19478                 }
19479             },
19480             pfout = function() {
19481                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
19482
19483                 if (this.label) {
19484                     this.label[0].animate({ r: 5 }, 500, "bounce");
19485                     this.label[1].attr({ "font-weight": 400 });
19486                 }
19487             };
19488
19489         switch(graphtype){
19490             case 'bar':
19491                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
19492                 break;
19493             case 'hbar':
19494                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
19495                 break;
19496             case 'pie':
19497 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
19498 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
19499 //            
19500                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
19501                 
19502                 break;
19503
19504         }
19505         
19506         if(this.title){
19507             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
19508         }
19509         
19510     },
19511     
19512     setTitle: function(o)
19513     {
19514         this.title = o;
19515     },
19516     
19517     initEvents: function() {
19518         
19519         if(!this.href){
19520             this.el.on('click', this.onClick, this);
19521         }
19522     },
19523     
19524     onClick : function(e)
19525     {
19526         Roo.log('img onclick');
19527         this.fireEvent('click', this, e);
19528     }
19529    
19530 });
19531
19532  
19533 /*
19534  * - LGPL
19535  *
19536  * numberBox
19537  * 
19538  */
19539 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19540
19541 /**
19542  * @class Roo.bootstrap.dash.NumberBox
19543  * @extends Roo.bootstrap.Component
19544  * Bootstrap NumberBox class
19545  * @cfg {String} bgcolor Box background color, such as (aqua | green | yellow | red etc..) Default aqua
19546  * @cfg {String} headline Box headline
19547  * @cfg {String} content Box content
19548  * @cfg {String} icon Box icon
19549  * @cfg {String} footer Footer text
19550  * @cfg {String} fhref Footer href
19551  * 
19552  * @constructor
19553  * Create a new NumberBox
19554  * @param {Object} config The config object
19555  */
19556
19557
19558 Roo.bootstrap.dash.NumberBox = function(config){
19559     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
19560     
19561 };
19562
19563 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
19564     
19565     bgcolor : 'aqua',
19566     headline : '',
19567     content : '',
19568     icon : '',
19569     footer : '',
19570     fhref : '',
19571     ficon : '',
19572     
19573     getAutoCreate : function(){
19574         
19575         var cfg = {
19576             tag : 'div',
19577             cls : 'small-box bg-' + this.bgcolor,
19578             cn : [
19579                 {
19580                     tag : 'div',
19581                     cls : 'inner',
19582                     cn :[
19583                         {
19584                             tag : 'h3',
19585                             cls : 'roo-headline',
19586                             html : this.headline
19587                         },
19588                         {
19589                             tag : 'p',
19590                             cls : 'roo-content',
19591                             html : this.content
19592                         }
19593                     ]
19594                 }
19595             ]
19596         }
19597         
19598         if(this.icon){
19599             cfg.cn.push({
19600                 tag : 'div',
19601                 cls : 'icon',
19602                 cn :[
19603                     {
19604                         tag : 'i',
19605                         cls : 'ion ' + this.icon
19606                     }
19607                 ]
19608             });
19609         }
19610         
19611         if(this.footer){
19612             var footer = {
19613                 tag : 'a',
19614                 cls : 'small-box-footer',
19615                 href : this.fhref || '#',
19616                 html : this.footer
19617             };
19618             
19619             cfg.cn.push(footer);
19620             
19621         }
19622         
19623         return  cfg;
19624     },
19625
19626     onRender : function(ct,position){
19627         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
19628
19629
19630        
19631                 
19632     },
19633
19634     setHeadline: function (value)
19635     {
19636         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
19637     },
19638     
19639     setFooter: function (value, href)
19640     {
19641         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
19642         
19643         if(href){
19644             this.el.select('a.small-box-footer',true).first().attr('href', href);
19645         }
19646         
19647     },
19648
19649     setContent: function (value)
19650     {
19651         this.el.select('.roo-content',true).first().dom.innerHTML = value;
19652     },
19653
19654     initEvents: function() 
19655     {   
19656         
19657     }
19658     
19659 });
19660
19661  
19662 /*
19663  * - LGPL
19664  *
19665  * TabBox
19666  * 
19667  */
19668 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19669
19670 /**
19671  * @class Roo.bootstrap.dash.TabBox
19672  * @extends Roo.bootstrap.Component
19673  * Bootstrap TabBox class
19674  * @cfg {String} title Title of the TabBox
19675  * @cfg {String} icon Icon of the TabBox
19676  * @cfg {Boolean} showtabs (true|false) show the tabs default true
19677  * 
19678  * @constructor
19679  * Create a new TabBox
19680  * @param {Object} config The config object
19681  */
19682
19683
19684 Roo.bootstrap.dash.TabBox = function(config){
19685     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
19686     this.addEvents({
19687         // raw events
19688         /**
19689          * @event addpane
19690          * When a pane is added
19691          * @param {Roo.bootstrap.dash.TabPane} pane
19692          */
19693         "addpane" : true
19694          
19695     });
19696 };
19697
19698 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
19699
19700     title : '',
19701     icon : false,
19702     showtabs : true,
19703     
19704     getChildContainer : function()
19705     {
19706         return this.el.select('.tab-content', true).first();
19707     },
19708     
19709     getAutoCreate : function(){
19710         
19711         var header = {
19712             tag: 'li',
19713             cls: 'pull-left header',
19714             html: this.title,
19715             cn : []
19716         };
19717         
19718         if(this.icon){
19719             header.cn.push({
19720                 tag: 'i',
19721                 cls: 'fa ' + this.icon
19722             });
19723         }
19724         
19725         
19726         var cfg = {
19727             tag: 'div',
19728             cls: 'nav-tabs-custom',
19729             cn: [
19730                 {
19731                     tag: 'ul',
19732                     cls: 'nav nav-tabs pull-right',
19733                     cn: [
19734                         header
19735                     ]
19736                 },
19737                 {
19738                     tag: 'div',
19739                     cls: 'tab-content no-padding',
19740                     cn: []
19741                 }
19742             ]
19743         }
19744
19745         return  cfg;
19746     },
19747     initEvents : function()
19748     {
19749         //Roo.log('add add pane handler');
19750         this.on('addpane', this.onAddPane, this);
19751     },
19752      /**
19753      * Updates the box title
19754      * @param {String} html to set the title to.
19755      */
19756     setTitle : function(value)
19757     {
19758         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
19759     },
19760     onAddPane : function(pane)
19761     {
19762         //Roo.log('addpane');
19763         //Roo.log(pane);
19764         // tabs are rendere left to right..
19765         if(!this.showtabs){
19766             return;
19767         }
19768         
19769         var ctr = this.el.select('.nav-tabs', true).first();
19770          
19771          
19772         var existing = ctr.select('.nav-tab',true);
19773         var qty = existing.getCount();;
19774         
19775         
19776         var tab = ctr.createChild({
19777             tag : 'li',
19778             cls : 'nav-tab' + (qty ? '' : ' active'),
19779             cn : [
19780                 {
19781                     tag : 'a',
19782                     href:'#',
19783                     html : pane.title
19784                 }
19785             ]
19786         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
19787         pane.tab = tab;
19788         
19789         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
19790         if (!qty) {
19791             pane.el.addClass('active');
19792         }
19793         
19794                 
19795     },
19796     onTabClick : function(ev,un,ob,pane)
19797     {
19798         //Roo.log('tab - prev default');
19799         ev.preventDefault();
19800         
19801         
19802         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
19803         pane.tab.addClass('active');
19804         //Roo.log(pane.title);
19805         this.getChildContainer().select('.tab-pane',true).removeClass('active');
19806         // technically we should have a deactivate event.. but maybe add later.
19807         // and it should not de-activate the selected tab...
19808         
19809         pane.el.addClass('active');
19810         pane.fireEvent('activate');
19811         
19812         
19813     }
19814     
19815     
19816 });
19817
19818  
19819 /*
19820  * - LGPL
19821  *
19822  * Tab pane
19823  * 
19824  */
19825 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19826 /**
19827  * @class Roo.bootstrap.TabPane
19828  * @extends Roo.bootstrap.Component
19829  * Bootstrap TabPane class
19830  * @cfg {Boolean} active (false | true) Default false
19831  * @cfg {String} title title of panel
19832
19833  * 
19834  * @constructor
19835  * Create a new TabPane
19836  * @param {Object} config The config object
19837  */
19838
19839 Roo.bootstrap.dash.TabPane = function(config){
19840     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
19841     
19842     this.addEvents({
19843         // raw events
19844         /**
19845          * @event activate
19846          * When a pane is activated
19847          * @param {Roo.bootstrap.dash.TabPane} pane
19848          */
19849         "activate" : true
19850          
19851     });
19852 };
19853
19854 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
19855     
19856     active : false,
19857     title : '',
19858     
19859     // the tabBox that this is attached to.
19860     tab : false,
19861      
19862     getAutoCreate : function() 
19863     {
19864         var cfg = {
19865             tag: 'div',
19866             cls: 'tab-pane'
19867         }
19868         
19869         if(this.active){
19870             cfg.cls += ' active';
19871         }
19872         
19873         return cfg;
19874     },
19875     initEvents  : function()
19876     {
19877         //Roo.log('trigger add pane handler');
19878         this.parent().fireEvent('addpane', this)
19879     },
19880     
19881      /**
19882      * Updates the tab title 
19883      * @param {String} html to set the title to.
19884      */
19885     setTitle: function(str)
19886     {
19887         if (!this.tab) {
19888             return;
19889         }
19890         this.title = str;
19891         this.tab.select('a', true).first().dom.innerHTML = str;
19892         
19893     }
19894     
19895     
19896     
19897 });
19898
19899  
19900
19901
19902  /*
19903  * - LGPL
19904  *
19905  * menu
19906  * 
19907  */
19908 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
19909
19910 /**
19911  * @class Roo.bootstrap.menu.Menu
19912  * @extends Roo.bootstrap.Component
19913  * Bootstrap Menu class - container for Menu
19914  * @cfg {String} html Text of the menu
19915  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
19916  * @cfg {String} icon Font awesome icon
19917  * @cfg {String} pos Menu align to (top | bottom) default bottom
19918  * 
19919  * 
19920  * @constructor
19921  * Create a new Menu
19922  * @param {Object} config The config object
19923  */
19924
19925
19926 Roo.bootstrap.menu.Menu = function(config){
19927     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
19928     
19929     this.addEvents({
19930         /**
19931          * @event beforeshow
19932          * Fires before this menu is displayed
19933          * @param {Roo.bootstrap.menu.Menu} this
19934          */
19935         beforeshow : true,
19936         /**
19937          * @event beforehide
19938          * Fires before this menu is hidden
19939          * @param {Roo.bootstrap.menu.Menu} this
19940          */
19941         beforehide : true,
19942         /**
19943          * @event show
19944          * Fires after this menu is displayed
19945          * @param {Roo.bootstrap.menu.Menu} this
19946          */
19947         show : true,
19948         /**
19949          * @event hide
19950          * Fires after this menu is hidden
19951          * @param {Roo.bootstrap.menu.Menu} this
19952          */
19953         hide : true,
19954         /**
19955          * @event click
19956          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19957          * @param {Roo.bootstrap.menu.Menu} this
19958          * @param {Roo.EventObject} e
19959          */
19960         click : true
19961     });
19962     
19963 };
19964
19965 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
19966     
19967     submenu : false,
19968     html : '',
19969     weight : 'default',
19970     icon : false,
19971     pos : 'bottom',
19972     
19973     
19974     getChildContainer : function() {
19975         if(this.isSubMenu){
19976             return this.el;
19977         }
19978         
19979         return this.el.select('ul.dropdown-menu', true).first();  
19980     },
19981     
19982     getAutoCreate : function()
19983     {
19984         var text = [
19985             {
19986                 tag : 'span',
19987                 cls : 'roo-menu-text',
19988                 html : this.html
19989             }
19990         ];
19991         
19992         if(this.icon){
19993             text.unshift({
19994                 tag : 'i',
19995                 cls : 'fa ' + this.icon
19996             })
19997         }
19998         
19999         
20000         var cfg = {
20001             tag : 'div',
20002             cls : 'btn-group',
20003             cn : [
20004                 {
20005                     tag : 'button',
20006                     cls : 'dropdown-button btn btn-' + this.weight,
20007                     cn : text
20008                 },
20009                 {
20010                     tag : 'button',
20011                     cls : 'dropdown-toggle btn btn-' + this.weight,
20012                     cn : [
20013                         {
20014                             tag : 'span',
20015                             cls : 'caret'
20016                         }
20017                     ]
20018                 },
20019                 {
20020                     tag : 'ul',
20021                     cls : 'dropdown-menu'
20022                 }
20023             ]
20024             
20025         };
20026         
20027         if(this.pos == 'top'){
20028             cfg.cls += ' dropup';
20029         }
20030         
20031         if(this.isSubMenu){
20032             cfg = {
20033                 tag : 'ul',
20034                 cls : 'dropdown-menu'
20035             }
20036         }
20037         
20038         return cfg;
20039     },
20040     
20041     onRender : function(ct, position)
20042     {
20043         this.isSubMenu = ct.hasClass('dropdown-submenu');
20044         
20045         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
20046     },
20047     
20048     initEvents : function() 
20049     {
20050         if(this.isSubMenu){
20051             return;
20052         }
20053         
20054         this.hidden = true;
20055         
20056         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
20057         this.triggerEl.on('click', this.onTriggerPress, this);
20058         
20059         this.buttonEl = this.el.select('button.dropdown-button', true).first();
20060         this.buttonEl.on('click', this.onClick, this);
20061         
20062     },
20063     
20064     list : function()
20065     {
20066         if(this.isSubMenu){
20067             return this.el;
20068         }
20069         
20070         return this.el.select('ul.dropdown-menu', true).first();
20071     },
20072     
20073     onClick : function(e)
20074     {
20075         this.fireEvent("click", this, e);
20076     },
20077     
20078     onTriggerPress  : function(e)
20079     {   
20080         if (this.isVisible()) {
20081             this.hide();
20082         } else {
20083             this.show();
20084         }
20085     },
20086     
20087     isVisible : function(){
20088         return !this.hidden;
20089     },
20090     
20091     show : function()
20092     {
20093         this.fireEvent("beforeshow", this);
20094         
20095         this.hidden = false;
20096         this.el.addClass('open');
20097         
20098         Roo.get(document).on("mouseup", this.onMouseUp, this);
20099         
20100         this.fireEvent("show", this);
20101         
20102         
20103     },
20104     
20105     hide : function()
20106     {
20107         this.fireEvent("beforehide", this);
20108         
20109         this.hidden = true;
20110         this.el.removeClass('open');
20111         
20112         Roo.get(document).un("mouseup", this.onMouseUp);
20113         
20114         this.fireEvent("hide", this);
20115     },
20116     
20117     onMouseUp : function()
20118     {
20119         this.hide();
20120     }
20121     
20122 });
20123
20124  
20125  /*
20126  * - LGPL
20127  *
20128  * menu item
20129  * 
20130  */
20131 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20132
20133 /**
20134  * @class Roo.bootstrap.menu.Item
20135  * @extends Roo.bootstrap.Component
20136  * Bootstrap MenuItem class
20137  * @cfg {Boolean} submenu (true | false) default false
20138  * @cfg {String} html text of the item
20139  * @cfg {String} href the link
20140  * @cfg {Boolean} disable (true | false) default false
20141  * @cfg {Boolean} preventDefault (true | false) default true
20142  * @cfg {String} icon Font awesome icon
20143  * @cfg {String} pos Submenu align to (left | right) default right 
20144  * 
20145  * 
20146  * @constructor
20147  * Create a new Item
20148  * @param {Object} config The config object
20149  */
20150
20151
20152 Roo.bootstrap.menu.Item = function(config){
20153     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
20154     this.addEvents({
20155         /**
20156          * @event mouseover
20157          * Fires when the mouse is hovering over this menu
20158          * @param {Roo.bootstrap.menu.Item} this
20159          * @param {Roo.EventObject} e
20160          */
20161         mouseover : true,
20162         /**
20163          * @event mouseout
20164          * Fires when the mouse exits this menu
20165          * @param {Roo.bootstrap.menu.Item} this
20166          * @param {Roo.EventObject} e
20167          */
20168         mouseout : true,
20169         // raw events
20170         /**
20171          * @event click
20172          * The raw click event for the entire grid.
20173          * @param {Roo.EventObject} e
20174          */
20175         click : true
20176     });
20177 };
20178
20179 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
20180     
20181     submenu : false,
20182     href : '',
20183     html : '',
20184     preventDefault: true,
20185     disable : false,
20186     icon : false,
20187     pos : 'right',
20188     
20189     getAutoCreate : function()
20190     {
20191         var text = [
20192             {
20193                 tag : 'span',
20194                 cls : 'roo-menu-item-text',
20195                 html : this.html
20196             }
20197         ];
20198         
20199         if(this.icon){
20200             text.unshift({
20201                 tag : 'i',
20202                 cls : 'fa ' + this.icon
20203             })
20204         }
20205         
20206         var cfg = {
20207             tag : 'li',
20208             cn : [
20209                 {
20210                     tag : 'a',
20211                     href : this.href || '#',
20212                     cn : text
20213                 }
20214             ]
20215         };
20216         
20217         if(this.disable){
20218             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
20219         }
20220         
20221         if(this.submenu){
20222             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
20223             
20224             if(this.pos == 'left'){
20225                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
20226             }
20227         }
20228         
20229         return cfg;
20230     },
20231     
20232     initEvents : function() 
20233     {
20234         this.el.on('mouseover', this.onMouseOver, this);
20235         this.el.on('mouseout', this.onMouseOut, this);
20236         
20237         this.el.select('a', true).first().on('click', this.onClick, this);
20238         
20239     },
20240     
20241     onClick : function(e)
20242     {
20243         if(this.preventDefault){
20244             e.preventDefault();
20245         }
20246         
20247         this.fireEvent("click", this, e);
20248     },
20249     
20250     onMouseOver : function(e)
20251     {
20252         if(this.submenu && this.pos == 'left'){
20253             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
20254         }
20255         
20256         this.fireEvent("mouseover", this, e);
20257     },
20258     
20259     onMouseOut : function(e)
20260     {
20261         this.fireEvent("mouseout", this, e);
20262     }
20263 });
20264
20265  
20266
20267  /*
20268  * - LGPL
20269  *
20270  * menu separator
20271  * 
20272  */
20273 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20274
20275 /**
20276  * @class Roo.bootstrap.menu.Separator
20277  * @extends Roo.bootstrap.Component
20278  * Bootstrap Separator class
20279  * 
20280  * @constructor
20281  * Create a new Separator
20282  * @param {Object} config The config object
20283  */
20284
20285
20286 Roo.bootstrap.menu.Separator = function(config){
20287     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
20288 };
20289
20290 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
20291     
20292     getAutoCreate : function(){
20293         var cfg = {
20294             tag : 'li',
20295             cls: 'divider'
20296         };
20297         
20298         return cfg;
20299     }
20300    
20301 });
20302
20303  
20304
20305