roojs-bootstrap.js
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * 
20  * @constructor
21  * Do not use directly - it does not do anything..
22  * @param {Object} config The config object
23  */
24
25
26
27 Roo.bootstrap.Component = function(config){
28     Roo.bootstrap.Component.superclass.constructor.call(this, config);
29 };
30
31 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
32     
33     
34     allowDomMove : false, // to stop relocations in parent onRender...
35     
36     cls : false,
37     
38     style : false,
39     
40     autoCreate : false,
41     
42     initEvents : function() {  },
43     
44     xattr : false,
45     
46     parentId : false,
47     
48     can_build_overlaid : true,
49     
50     dataId : false,
51     
52     name : false,
53     
54     parent: function() {
55         // returns the parent component..
56         return Roo.ComponentMgr.get(this.parentId)
57         
58         
59     },
60     
61     // private
62     onRender : function(ct, position)
63     {
64        // Roo.log("Call onRender: " + this.xtype);
65         
66         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
67         
68         if(this.el){
69             if (this.el.attr('xtype')) {
70                 this.el.attr('xtypex', this.el.attr('xtype'));
71                 this.el.dom.removeAttribute('xtype');
72                 
73                 this.initEvents();
74             }
75             
76             return;
77         }
78         
79          
80         
81         var cfg = Roo.apply({},  this.getAutoCreate());
82         cfg.id = Roo.id();
83         
84         // fill in the extra attributes 
85         if (this.xattr && typeof(this.xattr) =='object') {
86             for (var i in this.xattr) {
87                 cfg[i] = this.xattr[i];
88             }
89         }
90         
91         if(this.dataId){
92             cfg.dataId = this.dataId;
93         }
94         
95         if (this.cls) {
96             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
97         }
98         
99         if (this.style) { // fixme needs to support more complex style data.
100             cfg.style = this.style;
101         }
102         
103         if(this.name){
104             cfg.name = this.name;
105         }
106         
107         this.el = ct.createChild(cfg, position);
108         
109         if(this.tabIndex !== undefined){
110             this.el.dom.setAttribute('tabIndex', this.tabIndex);
111         }
112         this.initEvents();
113         
114         
115     },
116     
117     getChildContainer : function()
118     {
119         return this.el;
120     },
121     
122     
123     addxtype  : function(tree,cntr)
124     {
125         var cn = this;
126         
127         cn = Roo.factory(tree);
128            
129         cn.parentType = this.xtype; //??
130         cn.parentId = this.id;
131         
132         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
133         
134         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
135         
136         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
137         
138         var build_from_html =  Roo.XComponent.build_from_html;
139           
140         var is_body  = (tree.xtype == 'Body') ;
141           
142         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
143           
144         var self_cntr_el = Roo.get(this[cntr](false));
145         
146         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
147             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
148                 return this.addxtypeChild(tree,cntr);
149             }
150             
151             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
152                 
153             if(echild){
154                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
155             }
156             
157             Roo.log('skipping render');
158             return cn;
159             
160         }
161         
162         var ret = false;
163         
164         while (true) {
165             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
166             
167             if (!echild) {
168                 break;
169             }
170             
171             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
172                 break;
173             }
174             
175             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
176         }
177         return ret;
178     },
179     
180     addxtypeChild : function (tree, cntr)
181     {
182         Roo.log('addxtypeChild:' + cntr);
183         var cn = this;
184         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
185         
186         
187         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
188                     (typeof(tree['flexy:foreach']) != 'undefined');
189           
190         
191         
192         
193         // render the element if it's not BODY.
194         if (tree.xtype != 'Body') {
195            
196             cn = Roo.factory(tree);
197            
198             cn.parentType = this.xtype; //??
199             cn.parentId = this.id;
200             
201             var build_from_html =  Roo.XComponent.build_from_html;
202             
203             
204             // does the container contain child eleemnts with 'xtype' attributes.
205             // that match this xtype..
206             // note - when we render we create these as well..
207             // so we should check to see if body has xtype set.
208             if (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
209                
210                 var self_cntr_el = Roo.get(this[cntr](false));
211                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
212                 
213                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
214                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
215                   
216                   
217                   
218                     cn.el = echild;
219                   //  Roo.log("GOT");
220                     //echild.dom.removeAttribute('xtype');
221                 } else {
222                     Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
223                    
224                 }
225             }
226            
227             
228                
229             // if object has flexy:if - then it may or may not be rendered.
230             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
231                 // skip a flexy if element.
232                 Roo.log('skipping render');
233              } else {
234                  
235                 // actually if flexy:foreach is found, we really want to create 
236                 // multiple copies here...
237                 //Roo.log('render');
238                 //Roo.log(this[cntr]());
239                 cn.render(this[cntr](true));
240              }
241             // then add the element..
242         }
243         
244         
245         // handle the kids..
246         
247         var nitems = [];
248         /*
249         if (typeof (tree.menu) != 'undefined') {
250             tree.menu.parentType = cn.xtype;
251             tree.menu.triggerEl = cn.el;
252             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
253             
254         }
255         */
256         if (!tree.items || !tree.items.length) {
257             cn.items = nitems;
258             return cn;
259         }
260         var items = tree.items;
261         delete tree.items;
262         
263         //Roo.log(items.length);
264             // add the items..
265         for(var i =0;i < items.length;i++) {
266             nitems.push(cn.addxtype(Roo.apply({}, items[i])));
267         }
268         
269         cn.items = nitems;
270         
271         return cn;
272     }
273     
274     
275     
276     
277 });
278
279  /*
280  * - LGPL
281  *
282  * Body
283  * 
284  */
285
286 /**
287  * @class Roo.bootstrap.Body
288  * @extends Roo.bootstrap.Component
289  * Bootstrap Body class
290  * 
291  * @constructor
292  * Create a new body
293  * @param {Object} config The config object
294  */
295
296 Roo.bootstrap.Body = function(config){
297     Roo.bootstrap.Body.superclass.constructor.call(this, config);
298     this.el = Roo.get(document.body);
299     if (this.cls && this.cls.length) {
300         Roo.get(document.body).addClass(this.cls);
301     }
302 };
303
304 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
305       
306         autoCreate : {
307         cls: 'container'
308     },
309     onRender : function(ct, position)
310     {
311        /* Roo.log("Roo.bootstrap.Body - onRender");
312         if (this.cls && this.cls.length) {
313             Roo.get(document.body).addClass(this.cls);
314         }
315         // style??? xttr???
316         */
317     }
318     
319     
320  
321    
322 });
323
324  /*
325  * - LGPL
326  *
327  * button group
328  * 
329  */
330
331
332 /**
333  * @class Roo.bootstrap.ButtonGroup
334  * @extends Roo.bootstrap.Component
335  * Bootstrap ButtonGroup class
336  * @cfg {String} size lg | sm | xs (default empty normal)
337  * @cfg {String} align vertical | justified  (default none)
338  * @cfg {String} direction up | down (default down)
339  * @cfg {Boolean} toolbar false | true
340  * @cfg {Boolean} btn true | false
341  * 
342  * 
343  * @constructor
344  * Create a new Input
345  * @param {Object} config The config object
346  */
347
348 Roo.bootstrap.ButtonGroup = function(config){
349     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
350 };
351
352 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
353     
354     size: '',
355     align: '',
356     direction: '',
357     toolbar: false,
358     btn: true,
359
360     getAutoCreate : function(){
361         var cfg = {
362             cls: 'btn-group',
363             html : null
364         }
365         
366         cfg.html = this.html || cfg.html;
367         
368         if (this.toolbar) {
369             cfg = {
370                 cls: 'btn-toolbar',
371                 html: null
372             }
373             
374             return cfg;
375         }
376         
377         if (['vertical','justified'].indexOf(this.align)!==-1) {
378             cfg.cls = 'btn-group-' + this.align;
379             
380             if (this.align == 'justified') {
381                 console.log(this.items);
382             }
383         }
384         
385         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
386             cfg.cls += ' btn-group-' + this.size;
387         }
388         
389         if (this.direction == 'up') {
390             cfg.cls += ' dropup' ;
391         }
392         
393         return cfg;
394     }
395    
396 });
397
398  /*
399  * - LGPL
400  *
401  * button
402  * 
403  */
404
405 /**
406  * @class Roo.bootstrap.Button
407  * @extends Roo.bootstrap.Component
408  * Bootstrap Button class
409  * @cfg {String} html The button content
410  * @cfg {String} weight default (or empty) | primary | success | info | warning | danger | link
411  * @cfg {String} size empty | lg | sm | xs
412  * @cfg {String} tag empty | a | input | submit
413  * @cfg {String} href empty or href
414  * @cfg {Boolean} disabled false | true
415  * @cfg {Boolean} isClose false | true
416  * @cfg {String} glyphicon empty | adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out
417  * @cfg {String} badge text for badge
418  * @cfg {String} theme default (or empty) | glow
419  * @cfg {Boolean} inverse false | true
420  * @cfg {Boolean} toggle false | true
421  * @cfg {String} ontext text for on toggle state
422  * @cfg {String} offtext text for off toggle state
423  * @cfg {Boolean} defaulton true | false
424  * @cfg {Boolean} preventDefault (true | false) default true
425  * @cfg {Boolean} removeClass true | false remove the standard class..
426  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
427  * 
428  * @constructor
429  * Create a new button
430  * @param {Object} config The config object
431  */
432
433
434 Roo.bootstrap.Button = function(config){
435     Roo.bootstrap.Button.superclass.constructor.call(this, config);
436     this.addEvents({
437         // raw events
438         /**
439          * @event click
440          * When a butotn is pressed
441          * @param {Roo.EventObject} e
442          */
443         "click" : true,
444          /**
445          * @event toggle
446          * After the button has been toggles
447          * @param {Roo.EventObject} e
448          * @param {boolean} pressed (also available as button.pressed)
449          */
450         "toggle" : true
451     });
452 };
453
454 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
455     html: false,
456     active: false,
457     weight: '',
458     size: '',
459     tag: 'button',
460     href: '',
461     disabled: false,
462     isClose: false,
463     glyphicon: '',
464     badge: '',
465     theme: 'default',
466     inverse: false,
467     
468     toggle: false,
469     ontext: 'ON',
470     offtext: 'OFF',
471     defaulton: true,
472     preventDefault: true,
473     removeClass: false,
474     name: false,
475     target: false,
476     
477     
478     pressed : null,
479      
480     
481     getAutoCreate : function(){
482         
483         var cfg = {
484             tag : 'button',
485             cls : 'roo-button',
486             html: ''
487         };
488         
489         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
490             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
491             this.tag = 'button';
492         } else {
493             cfg.tag = this.tag;
494         }
495         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
496         
497         if (this.toggle == true) {
498             cfg={
499                 tag: 'div',
500                 cls: 'slider-frame roo-button',
501                 cn: [
502                     {
503                         tag: 'span',
504                         'data-on-text':'ON',
505                         'data-off-text':'OFF',
506                         cls: 'slider-button',
507                         html: this.offtext
508                     }
509                 ]
510             };
511             
512             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
513                 cfg.cls += ' '+this.weight;
514             }
515             
516             return cfg;
517         }
518         
519         if (this.isClose) {
520             cfg.cls += ' close';
521             
522             cfg["aria-hidden"] = true;
523             
524             cfg.html = "&times;";
525             
526             return cfg;
527         }
528         
529          
530         if (this.theme==='default') {
531             cfg.cls = 'btn roo-button';
532             
533             //if (this.parentType != 'Navbar') {
534             this.weight = this.weight.length ?  this.weight : 'default';
535             //}
536             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
537                 
538                 cfg.cls += ' btn-' + this.weight;
539             }
540         } else if (this.theme==='glow') {
541             
542             cfg.tag = 'a';
543             cfg.cls = 'btn-glow roo-button';
544             
545             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
546                 
547                 cfg.cls += ' ' + this.weight;
548             }
549         }
550    
551         
552         if (this.inverse) {
553             this.cls += ' inverse';
554         }
555         
556         
557         if (this.active) {
558             cfg.cls += ' active';
559         }
560         
561         if (this.disabled) {
562             cfg.disabled = 'disabled';
563         }
564         
565         if (this.items) {
566             Roo.log('changing to ul' );
567             cfg.tag = 'ul';
568             this.glyphicon = 'caret';
569         }
570         
571         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
572          
573         //gsRoo.log(this.parentType);
574         if (this.parentType === 'Navbar' && !this.parent().bar) {
575             Roo.log('changing to li?');
576             
577             cfg.tag = 'li';
578             
579             cfg.cls = '';
580             cfg.cn =  [{
581                 tag : 'a',
582                 cls : 'roo-button',
583                 html : this.html,
584                 href : this.href || '#'
585             }];
586             if (this.menu) {
587                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
588                 cfg.cls += ' dropdown';
589             }   
590             
591             delete cfg.html;
592             
593         }
594         
595        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
596         
597         if (this.glyphicon) {
598             cfg.html = ' ' + cfg.html;
599             
600             cfg.cn = [
601                 {
602                     tag: 'span',
603                     cls: 'glyphicon glyphicon-' + this.glyphicon
604                 }
605             ];
606         }
607         
608         if (this.badge) {
609             cfg.html += ' ';
610             
611             cfg.tag = 'a';
612             
613 //            cfg.cls='btn roo-button';
614             
615             cfg.href=this.href;
616             
617             var value = cfg.html;
618             
619             if(this.glyphicon){
620                 value = {
621                             tag: 'span',
622                             cls: 'glyphicon glyphicon-' + this.glyphicon,
623                             html: this.html
624                         };
625                 
626             }
627             
628             cfg.cn = [
629                 value,
630                 {
631                     tag: 'span',
632                     cls: 'badge',
633                     html: this.badge
634                 }
635             ];
636             
637             cfg.html='';
638         }
639         
640         if (this.menu) {
641             cfg.cls += ' dropdown';
642             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
643         }
644         
645         if (cfg.tag !== 'a' && this.href !== '') {
646             throw "Tag must be a to set href.";
647         } else if (this.href.length > 0) {
648             cfg.href = this.href;
649         }
650         
651         if(this.removeClass){
652             cfg.cls = '';
653         }
654         
655         if(this.target){
656             cfg.target = this.target;
657         }
658         
659         return cfg;
660     },
661     initEvents: function() {
662        // Roo.log('init events?');
663 //        Roo.log(this.el.dom);
664         // add the menu...
665         
666         if (typeof (this.menu) != 'undefined') {
667             this.menu.parentType = this.xtype;
668             this.menu.triggerEl = this.el;
669             this.addxtype(Roo.apply({}, this.menu));
670         }
671
672
673        if (this.el.hasClass('roo-button')) {
674             this.el.on('click', this.onClick, this);
675        } else {
676             this.el.select('.roo-button').on('click', this.onClick, this);
677        }
678        
679        if(this.removeClass){
680            this.el.on('click', this.onClick, this);
681        }
682        
683        this.el.enableDisplayMode();
684         
685     },
686     onClick : function(e)
687     {
688         if (this.disabled) {
689             return;
690         }
691         
692         Roo.log('button on click ');
693         if(this.preventDefault){
694             e.preventDefault();
695         }
696         if (this.pressed === true || this.pressed === false) {
697             this.pressed = !this.pressed;
698             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
699             this.fireEvent('toggle', this, e, this.pressed);
700         }
701         
702         
703         this.fireEvent('click', this, e);
704     },
705     
706     /**
707      * Enables this button
708      */
709     enable : function()
710     {
711         this.disabled = false;
712         this.el.removeClass('disabled');
713     },
714     
715     /**
716      * Disable this button
717      */
718     disable : function()
719     {
720         this.disabled = true;
721         this.el.addClass('disabled');
722     },
723      /**
724      * sets the active state on/off, 
725      * @param {Boolean} state (optional) Force a particular state
726      */
727     setActive : function(v) {
728         
729         this.el[v ? 'addClass' : 'removeClass']('active');
730     },
731      /**
732      * toggles the current active state 
733      */
734     toggleActive : function()
735     {
736        var active = this.el.hasClass('active');
737        this.setActive(!active);
738        
739         
740     },
741     setText : function(str)
742     {
743         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
744     },
745     getText : function()
746     {
747         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
748     },
749     hide: function() {
750        
751      
752         this.el.hide();   
753     },
754     show: function() {
755        
756         this.el.show();   
757     }
758     
759     
760 });
761
762  /*
763  * - LGPL
764  *
765  * column
766  * 
767  */
768
769 /**
770  * @class Roo.bootstrap.Column
771  * @extends Roo.bootstrap.Component
772  * Bootstrap Column class
773  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
774  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
775  * @cfg {Number} md colspan out of 12 for computer-sized screens
776  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
777  * @cfg {String} html content of column.
778  * 
779  * @constructor
780  * Create a new Column
781  * @param {Object} config The config object
782  */
783
784 Roo.bootstrap.Column = function(config){
785     Roo.bootstrap.Column.superclass.constructor.call(this, config);
786 };
787
788 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
789     
790     xs: null,
791     sm: null,
792     md: null,
793     lg: null,
794     html: '',
795     offset: 0,
796     
797     getAutoCreate : function(){
798         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
799         
800         cfg = {
801             tag: 'div',
802             cls: 'column'
803         };
804         
805         var settings=this;
806         ['xs','sm','md','lg'].map(function(size){
807             if (settings[size]) {
808                 cfg.cls += ' col-' + size + '-' + settings[size];
809             }
810         });
811         if (this.html.length) {
812             cfg.html = this.html;
813         }
814         
815         return cfg;
816     }
817    
818 });
819
820  
821
822  /*
823  * - LGPL
824  *
825  * page container.
826  * 
827  */
828
829
830 /**
831  * @class Roo.bootstrap.Container
832  * @extends Roo.bootstrap.Component
833  * Bootstrap Container class
834  * @cfg {Boolean} jumbotron is it a jumbotron element
835  * @cfg {String} html content of element
836  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
837  * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
838  * @cfg {String} header content of header (for panel)
839  * @cfg {String} footer content of footer (for panel)
840  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
841  * @cfg {String} tag (header|aside|section) type of HTML tag.
842
843  *     
844  * @constructor
845  * Create a new Container
846  * @param {Object} config The config object
847  */
848
849 Roo.bootstrap.Container = function(config){
850     Roo.bootstrap.Container.superclass.constructor.call(this, config);
851 };
852
853 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
854     
855     jumbotron : false,
856     well: '',
857     panel : '',
858     header: '',
859     footer : '',
860     sticky: '',
861     tag : false,
862   
863      
864     getChildContainer : function() {
865         
866         if(!this.el){
867             return false;
868         }
869         
870         if (this.panel.length) {
871             return this.el.select('.panel-body',true).first();
872         }
873         
874         return this.el;
875     },
876     
877     
878     getAutoCreate : function(){
879         
880         var cfg = {
881             tag : this.tag || 'div',
882             html : '',
883             cls : ''
884         };
885         if (this.jumbotron) {
886             cfg.cls = 'jumbotron';
887         }
888         // - this is applied by the parent..
889         //if (this.cls) {
890         //    cfg.cls = this.cls + '';
891         //}
892         
893         if (this.sticky.length) {
894             
895             var bd = Roo.get(document.body);
896             if (!bd.hasClass('bootstrap-sticky')) {
897                 bd.addClass('bootstrap-sticky');
898                 Roo.select('html',true).setStyle('height', '100%');
899             }
900              
901             cfg.cls += 'bootstrap-sticky-' + this.sticky;
902         }
903         
904         
905         if (this.well.length) {
906             switch (this.well) {
907                 case 'lg':
908                 case 'sm':
909                     cfg.cls +=' well well-' +this.well;
910                     break;
911                 default:
912                     cfg.cls +=' well';
913                     break;
914             }
915         }
916         
917         var body = cfg;
918         
919         if (this.panel.length) {
920             cfg.cls += ' panel panel-' + this.panel;
921             cfg.cn = [];
922             if (this.header.length) {
923                 cfg.cn.push({
924                     
925                     cls : 'panel-heading',
926                     cn : [{
927                         tag: 'h3',
928                         cls : 'panel-title',
929                         html : this.header
930                     }]
931                     
932                 });
933             }
934             body = false;
935             cfg.cn.push({
936                 cls : 'panel-body',
937                 html : this.html
938             });
939             
940             
941             if (this.footer.length) {
942                 cfg.cn.push({
943                     cls : 'panel-footer',
944                     html : this.footer
945                     
946                 });
947             }
948             
949         }
950         
951         if (body) {
952             body.html = this.html || cfg.html;
953         }
954         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
955             cfg.cls =  'container';
956         }
957         
958         return cfg;
959     },
960     
961     titleEl : function()
962     {
963         if(!this.el || !this.panel.length || !this.header.length){
964             return;
965         }
966         
967         return this.el.select('.panel-title',true).first();
968     },
969     
970     setTitle : function(v)
971     {
972         var titleEl = this.titleEl();
973         
974         if(!titleEl){
975             return;
976         }
977         
978         titleEl.dom.innerHTML = v;
979     },
980     
981     getTitle : function()
982     {
983         
984         var titleEl = this.titleEl();
985         
986         if(!titleEl){
987             return '';
988         }
989         
990         return titleEl.dom.innerHTML;
991     }
992    
993 });
994
995  /*
996  * - LGPL
997  *
998  * image
999  * 
1000  */
1001
1002
1003 /**
1004  * @class Roo.bootstrap.Img
1005  * @extends Roo.bootstrap.Component
1006  * Bootstrap Img class
1007  * @cfg {Boolean} imgResponsive false | true
1008  * @cfg {String} border rounded | circle | thumbnail
1009  * @cfg {String} src image source
1010  * @cfg {String} alt image alternative text
1011  * @cfg {String} href a tag href
1012  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1013  * 
1014  * @constructor
1015  * Create a new Input
1016  * @param {Object} config The config object
1017  */
1018
1019 Roo.bootstrap.Img = function(config){
1020     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1021     
1022     this.addEvents({
1023         // img events
1024         /**
1025          * @event click
1026          * The img click event for the img.
1027          * @param {Roo.EventObject} e
1028          */
1029         "click" : true
1030     });
1031 };
1032
1033 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1034     
1035     imgResponsive: true,
1036     border: '',
1037     src: '',
1038     href: false,
1039     target: false,
1040
1041     getAutoCreate : function(){
1042         
1043         var cfg = {
1044             tag: 'img',
1045             cls: (this.imgResponsive) ? 'img-responsive' : '',
1046             html : null
1047         }
1048         
1049         cfg.html = this.html || cfg.html;
1050         
1051         cfg.src = this.src || cfg.src;
1052         
1053         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1054             cfg.cls += ' img-' + this.border;
1055         }
1056         
1057         if(this.alt){
1058             cfg.alt = this.alt;
1059         }
1060         
1061         if(this.href){
1062             var a = {
1063                 tag: 'a',
1064                 href: this.href,
1065                 cn: [
1066                     cfg
1067                 ]
1068             }
1069             
1070             if(this.target){
1071                 a.target = this.target;
1072             }
1073             
1074         }
1075         
1076         
1077         return (this.href) ? a : cfg;
1078     },
1079     
1080     initEvents: function() {
1081         
1082         if(!this.href){
1083             this.el.on('click', this.onClick, this);
1084         }
1085     },
1086     
1087     onClick : function(e)
1088     {
1089         Roo.log('img onclick');
1090         this.fireEvent('click', this, e);
1091     }
1092    
1093 });
1094
1095  /*
1096  * - LGPL
1097  *
1098  * image
1099  * 
1100  */
1101
1102
1103 /**
1104  * @class Roo.bootstrap.Link
1105  * @extends Roo.bootstrap.Component
1106  * Bootstrap Link Class
1107  * @cfg {String} alt image alternative text
1108  * @cfg {String} href a tag href
1109  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1110  * @cfg {String} html the content of the link.
1111  * @cfg {Boolean} preventDefault (true | false) default false
1112
1113  * 
1114  * @constructor
1115  * Create a new Input
1116  * @param {Object} config The config object
1117  */
1118
1119 Roo.bootstrap.Link = function(config){
1120     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1121     
1122     this.addEvents({
1123         // img events
1124         /**
1125          * @event click
1126          * The img click event for the img.
1127          * @param {Roo.EventObject} e
1128          */
1129         "click" : true
1130     });
1131 };
1132
1133 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1134     
1135     href: false,
1136     target: false,
1137     preventDefault: false,
1138
1139     getAutoCreate : function(){
1140         
1141         var cfg = {
1142             tag: 'a',
1143             html : this.html || 'html-missing'
1144         }
1145         
1146         
1147         if(this.alt){
1148             cfg.alt = this.alt;
1149         }
1150         cfg.href = this.href || '#';
1151         if(this.target){
1152             cfg.target = this.target;
1153         }
1154         
1155         return cfg;
1156     },
1157     
1158     initEvents: function() {
1159         
1160         if(!this.href || this.preventDefault){
1161             this.el.on('click', this.onClick, this);
1162         }
1163     },
1164     
1165     onClick : function(e)
1166     {
1167         if(this.preventDefault){
1168             e.preventDefault();
1169         }
1170         //Roo.log('img onclick');
1171         this.fireEvent('click', this, e);
1172     }
1173    
1174 });
1175
1176  /*
1177  * - LGPL
1178  *
1179  * header
1180  * 
1181  */
1182
1183 /**
1184  * @class Roo.bootstrap.Header
1185  * @extends Roo.bootstrap.Component
1186  * Bootstrap Header class
1187  * @cfg {String} html content of header
1188  * @cfg {Number} level (1|2|3|4|5|6) default 1
1189  * 
1190  * @constructor
1191  * Create a new Header
1192  * @param {Object} config The config object
1193  */
1194
1195
1196 Roo.bootstrap.Header  = function(config){
1197     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1198 };
1199
1200 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1201     
1202     //href : false,
1203     html : false,
1204     level : 1,
1205     
1206     
1207     
1208     getAutoCreate : function(){
1209         
1210         var cfg = {
1211             tag: 'h' + (1 *this.level),
1212             html: this.html || 'fill in html'
1213         } ;
1214         
1215         return cfg;
1216     }
1217    
1218 });
1219
1220  
1221
1222  /*
1223  * Based on:
1224  * Ext JS Library 1.1.1
1225  * Copyright(c) 2006-2007, Ext JS, LLC.
1226  *
1227  * Originally Released Under LGPL - original licence link has changed is not relivant.
1228  *
1229  * Fork - LGPL
1230  * <script type="text/javascript">
1231  */
1232  
1233 /**
1234  * @class Roo.bootstrap.MenuMgr
1235  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1236  * @singleton
1237  */
1238 Roo.bootstrap.MenuMgr = function(){
1239    var menus, active, groups = {}, attached = false, lastShow = new Date();
1240
1241    // private - called when first menu is created
1242    function init(){
1243        menus = {};
1244        active = new Roo.util.MixedCollection();
1245        Roo.get(document).addKeyListener(27, function(){
1246            if(active.length > 0){
1247                hideAll();
1248            }
1249        });
1250    }
1251
1252    // private
1253    function hideAll(){
1254        if(active && active.length > 0){
1255            var c = active.clone();
1256            c.each(function(m){
1257                m.hide();
1258            });
1259        }
1260    }
1261
1262    // private
1263    function onHide(m){
1264        active.remove(m);
1265        if(active.length < 1){
1266            Roo.get(document).un("mouseup", onMouseDown);
1267             
1268            attached = false;
1269        }
1270    }
1271
1272    // private
1273    function onShow(m){
1274        var last = active.last();
1275        lastShow = new Date();
1276        active.add(m);
1277        if(!attached){
1278           Roo.get(document).on("mouseup", onMouseDown);
1279            
1280            attached = true;
1281        }
1282        if(m.parentMenu){
1283           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1284           m.parentMenu.activeChild = m;
1285        }else if(last && last.isVisible()){
1286           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1287        }
1288    }
1289
1290    // private
1291    function onBeforeHide(m){
1292        if(m.activeChild){
1293            m.activeChild.hide();
1294        }
1295        if(m.autoHideTimer){
1296            clearTimeout(m.autoHideTimer);
1297            delete m.autoHideTimer;
1298        }
1299    }
1300
1301    // private
1302    function onBeforeShow(m){
1303        var pm = m.parentMenu;
1304        if(!pm && !m.allowOtherMenus){
1305            hideAll();
1306        }else if(pm && pm.activeChild && active != m){
1307            pm.activeChild.hide();
1308        }
1309    }
1310
1311    // private
1312    function onMouseDown(e){
1313         Roo.log("on MouseDown");
1314         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
1315            hideAll();
1316         }
1317         
1318         
1319    }
1320
1321    // private
1322    function onBeforeCheck(mi, state){
1323        if(state){
1324            var g = groups[mi.group];
1325            for(var i = 0, l = g.length; i < l; i++){
1326                if(g[i] != mi){
1327                    g[i].setChecked(false);
1328                }
1329            }
1330        }
1331    }
1332
1333    return {
1334
1335        /**
1336         * Hides all menus that are currently visible
1337         */
1338        hideAll : function(){
1339             hideAll();  
1340        },
1341
1342        // private
1343        register : function(menu){
1344            if(!menus){
1345                init();
1346            }
1347            menus[menu.id] = menu;
1348            menu.on("beforehide", onBeforeHide);
1349            menu.on("hide", onHide);
1350            menu.on("beforeshow", onBeforeShow);
1351            menu.on("show", onShow);
1352            var g = menu.group;
1353            if(g && menu.events["checkchange"]){
1354                if(!groups[g]){
1355                    groups[g] = [];
1356                }
1357                groups[g].push(menu);
1358                menu.on("checkchange", onCheck);
1359            }
1360        },
1361
1362         /**
1363          * Returns a {@link Roo.menu.Menu} object
1364          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1365          * be used to generate and return a new Menu instance.
1366          */
1367        get : function(menu){
1368            if(typeof menu == "string"){ // menu id
1369                return menus[menu];
1370            }else if(menu.events){  // menu instance
1371                return menu;
1372            }
1373            /*else if(typeof menu.length == 'number'){ // array of menu items?
1374                return new Roo.bootstrap.Menu({items:menu});
1375            }else{ // otherwise, must be a config
1376                return new Roo.bootstrap.Menu(menu);
1377            }
1378            */
1379            return false;
1380        },
1381
1382        // private
1383        unregister : function(menu){
1384            delete menus[menu.id];
1385            menu.un("beforehide", onBeforeHide);
1386            menu.un("hide", onHide);
1387            menu.un("beforeshow", onBeforeShow);
1388            menu.un("show", onShow);
1389            var g = menu.group;
1390            if(g && menu.events["checkchange"]){
1391                groups[g].remove(menu);
1392                menu.un("checkchange", onCheck);
1393            }
1394        },
1395
1396        // private
1397        registerCheckable : function(menuItem){
1398            var g = menuItem.group;
1399            if(g){
1400                if(!groups[g]){
1401                    groups[g] = [];
1402                }
1403                groups[g].push(menuItem);
1404                menuItem.on("beforecheckchange", onBeforeCheck);
1405            }
1406        },
1407
1408        // private
1409        unregisterCheckable : function(menuItem){
1410            var g = menuItem.group;
1411            if(g){
1412                groups[g].remove(menuItem);
1413                menuItem.un("beforecheckchange", onBeforeCheck);
1414            }
1415        }
1416    };
1417 }();/*
1418  * - LGPL
1419  *
1420  * menu
1421  * 
1422  */
1423
1424 /**
1425  * @class Roo.bootstrap.Menu
1426  * @extends Roo.bootstrap.Component
1427  * Bootstrap Menu class - container for MenuItems
1428  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1429  * 
1430  * @constructor
1431  * Create a new Menu
1432  * @param {Object} config The config object
1433  */
1434
1435
1436 Roo.bootstrap.Menu = function(config){
1437     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1438     if (this.registerMenu) {
1439         Roo.bootstrap.MenuMgr.register(this);
1440     }
1441     this.addEvents({
1442         /**
1443          * @event beforeshow
1444          * Fires before this menu is displayed
1445          * @param {Roo.menu.Menu} this
1446          */
1447         beforeshow : true,
1448         /**
1449          * @event beforehide
1450          * Fires before this menu is hidden
1451          * @param {Roo.menu.Menu} this
1452          */
1453         beforehide : true,
1454         /**
1455          * @event show
1456          * Fires after this menu is displayed
1457          * @param {Roo.menu.Menu} this
1458          */
1459         show : true,
1460         /**
1461          * @event hide
1462          * Fires after this menu is hidden
1463          * @param {Roo.menu.Menu} this
1464          */
1465         hide : true,
1466         /**
1467          * @event click
1468          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1469          * @param {Roo.menu.Menu} this
1470          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1471          * @param {Roo.EventObject} e
1472          */
1473         click : true,
1474         /**
1475          * @event mouseover
1476          * Fires when the mouse is hovering over this menu
1477          * @param {Roo.menu.Menu} this
1478          * @param {Roo.EventObject} e
1479          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1480          */
1481         mouseover : true,
1482         /**
1483          * @event mouseout
1484          * Fires when the mouse exits this menu
1485          * @param {Roo.menu.Menu} this
1486          * @param {Roo.EventObject} e
1487          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1488          */
1489         mouseout : true,
1490         /**
1491          * @event itemclick
1492          * Fires when a menu item contained in this menu is clicked
1493          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1494          * @param {Roo.EventObject} e
1495          */
1496         itemclick: true
1497     });
1498     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1499 };
1500
1501 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1502     
1503    /// html : false,
1504     //align : '',
1505     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1506     type: false,
1507     /**
1508      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1509      */
1510     registerMenu : true,
1511     
1512     menuItems :false, // stores the menu items..
1513     
1514     hidden:true,
1515     
1516     parentMenu : false,
1517     
1518     getChildContainer : function() {
1519         return this.el;  
1520     },
1521     
1522     getAutoCreate : function(){
1523          
1524         //if (['right'].indexOf(this.align)!==-1) {
1525         //    cfg.cn[1].cls += ' pull-right'
1526         //}
1527         
1528         
1529         var cfg = {
1530             tag : 'ul',
1531             cls : 'dropdown-menu' ,
1532             style : 'z-index:1000'
1533             
1534         }
1535         
1536         if (this.type === 'submenu') {
1537             cfg.cls = 'submenu active';
1538         }
1539         if (this.type === 'treeview') {
1540             cfg.cls = 'treeview-menu';
1541         }
1542         
1543         return cfg;
1544     },
1545     initEvents : function() {
1546         
1547        // Roo.log("ADD event");
1548        // Roo.log(this.triggerEl.dom);
1549         this.triggerEl.on('click', this.onTriggerPress, this);
1550         this.triggerEl.addClass('dropdown-toggle');
1551         this.el.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
1552
1553         this.el.on("mouseover", this.onMouseOver, this);
1554         this.el.on("mouseout", this.onMouseOut, this);
1555         
1556         
1557     },
1558     findTargetItem : function(e){
1559         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
1560         if(!t){
1561             return false;
1562         }
1563         //Roo.log(t);         Roo.log(t.id);
1564         if(t && t.id){
1565             //Roo.log(this.menuitems);
1566             return this.menuitems.get(t.id);
1567             
1568             //return this.items.get(t.menuItemId);
1569         }
1570         
1571         return false;
1572     },
1573     onClick : function(e){
1574         Roo.log("menu.onClick");
1575         var t = this.findTargetItem(e);
1576         if(!t){
1577             return;
1578         }
1579         Roo.log(e);
1580         /*
1581         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
1582             if(t == this.activeItem && t.shouldDeactivate(e)){
1583                 this.activeItem.deactivate();
1584                 delete this.activeItem;
1585                 return;
1586             }
1587             if(t.canActivate){
1588                 this.setActiveItem(t, true);
1589             }
1590             return;
1591             
1592             
1593         }
1594         */
1595         Roo.log('pass click event');
1596         
1597         t.onClick(e);
1598         
1599         this.fireEvent("click", this, t, e);
1600         
1601         this.hide();
1602     },
1603      onMouseOver : function(e){
1604         var t  = this.findTargetItem(e);
1605         //Roo.log(t);
1606         //if(t){
1607         //    if(t.canActivate && !t.disabled){
1608         //        this.setActiveItem(t, true);
1609         //    }
1610         //}
1611         
1612         this.fireEvent("mouseover", this, e, t);
1613     },
1614     isVisible : function(){
1615         return !this.hidden;
1616     },
1617      onMouseOut : function(e){
1618         var t  = this.findTargetItem(e);
1619         
1620         //if(t ){
1621         //    if(t == this.activeItem && t.shouldDeactivate(e)){
1622         //        this.activeItem.deactivate();
1623         //        delete this.activeItem;
1624         //    }
1625         //}
1626         this.fireEvent("mouseout", this, e, t);
1627     },
1628     
1629     
1630     /**
1631      * Displays this menu relative to another element
1632      * @param {String/HTMLElement/Roo.Element} element The element to align to
1633      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
1634      * the element (defaults to this.defaultAlign)
1635      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1636      */
1637     show : function(el, pos, parentMenu){
1638         this.parentMenu = parentMenu;
1639         if(!this.el){
1640             this.render();
1641         }
1642         this.fireEvent("beforeshow", this);
1643         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
1644     },
1645      /**
1646      * Displays this menu at a specific xy position
1647      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
1648      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1649      */
1650     showAt : function(xy, parentMenu, /* private: */_e){
1651         this.parentMenu = parentMenu;
1652         if(!this.el){
1653             this.render();
1654         }
1655         if(_e !== false){
1656             this.fireEvent("beforeshow", this);
1657             
1658             //xy = this.el.adjustForConstraints(xy);
1659         }
1660         //this.el.setXY(xy);
1661         //this.el.show();
1662         this.hideMenuItems();
1663         this.hidden = false;
1664         this.triggerEl.addClass('open');
1665         this.focus();
1666         this.fireEvent("show", this);
1667     },
1668     
1669     focus : function(){
1670         return;
1671         if(!this.hidden){
1672             this.doFocus.defer(50, this);
1673         }
1674     },
1675
1676     doFocus : function(){
1677         if(!this.hidden){
1678             this.focusEl.focus();
1679         }
1680     },
1681
1682     /**
1683      * Hides this menu and optionally all parent menus
1684      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
1685      */
1686     hide : function(deep){
1687         
1688         this.hideMenuItems();
1689         if(this.el && this.isVisible()){
1690             this.fireEvent("beforehide", this);
1691             if(this.activeItem){
1692                 this.activeItem.deactivate();
1693                 this.activeItem = null;
1694             }
1695             this.triggerEl.removeClass('open');;
1696             this.hidden = true;
1697             this.fireEvent("hide", this);
1698         }
1699         if(deep === true && this.parentMenu){
1700             this.parentMenu.hide(true);
1701         }
1702     },
1703     
1704     onTriggerPress  : function(e)
1705     {
1706         
1707         Roo.log('trigger press');
1708         //Roo.log(e.getTarget());
1709        // Roo.log(this.triggerEl.dom);
1710         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
1711             return;
1712         }
1713         if (this.isVisible()) {
1714             Roo.log('hide');
1715             this.hide();
1716         } else {
1717             this.show(this.triggerEl, false, false);
1718         }
1719         
1720         
1721     },
1722     
1723          
1724        
1725     
1726     hideMenuItems : function()
1727     {
1728         //$(backdrop).remove()
1729         Roo.select('.open',true).each(function(aa) {
1730             
1731             aa.removeClass('open');
1732           //var parent = getParent($(this))
1733           //var relatedTarget = { relatedTarget: this }
1734           
1735            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
1736           //if (e.isDefaultPrevented()) return
1737            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
1738         })
1739     },
1740     addxtypeChild : function (tree, cntr) {
1741         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
1742           
1743         this.menuitems.add(comp);
1744         return comp;
1745
1746     },
1747     getEl : function()
1748     {
1749         Roo.log(this.el);
1750         return this.el;
1751     }
1752 });
1753
1754  
1755  /*
1756  * - LGPL
1757  *
1758  * menu item
1759  * 
1760  */
1761
1762
1763 /**
1764  * @class Roo.bootstrap.MenuItem
1765  * @extends Roo.bootstrap.Component
1766  * Bootstrap MenuItem class
1767  * @cfg {String} html the menu label
1768  * @cfg {String} href the link
1769  * @cfg {Boolean} preventDefault (true | false) default true
1770  * 
1771  * 
1772  * @constructor
1773  * Create a new MenuItem
1774  * @param {Object} config The config object
1775  */
1776
1777
1778 Roo.bootstrap.MenuItem = function(config){
1779     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
1780     this.addEvents({
1781         // raw events
1782         /**
1783          * @event click
1784          * The raw click event for the entire grid.
1785          * @param {Roo.EventObject} e
1786          */
1787         "click" : true
1788     });
1789 };
1790
1791 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
1792     
1793     href : false,
1794     html : false,
1795     preventDefault: true,
1796     
1797     getAutoCreate : function(){
1798         var cfg= {
1799             tag: 'li',
1800             cls: 'dropdown-menu-item',
1801             cn: [
1802                     {
1803                         tag : 'a',
1804                         href : '#',
1805                         html : 'Link'
1806                     }
1807                 ]
1808         };
1809         if (this.parent().type == 'treeview') {
1810             cfg.cls = 'treeview-menu';
1811         }
1812         
1813         cfg.cn[0].href = this.href || cfg.cn[0].href ;
1814         cfg.cn[0].html = this.html || cfg.cn[0].html ;
1815         return cfg;
1816     },
1817     
1818     initEvents: function() {
1819         
1820         //this.el.select('a').on('click', this.onClick, this);
1821         
1822     },
1823     onClick : function(e)
1824     {
1825         Roo.log('item on click ');
1826         //if(this.preventDefault){
1827         //    e.preventDefault();
1828         //}
1829         //this.parent().hideMenuItems();
1830         
1831         this.fireEvent('click', this, e);
1832     },
1833     getEl : function()
1834     {
1835         return this.el;
1836     }
1837 });
1838
1839  
1840
1841  /*
1842  * - LGPL
1843  *
1844  * menu separator
1845  * 
1846  */
1847
1848
1849 /**
1850  * @class Roo.bootstrap.MenuSeparator
1851  * @extends Roo.bootstrap.Component
1852  * Bootstrap MenuSeparator class
1853  * 
1854  * @constructor
1855  * Create a new MenuItem
1856  * @param {Object} config The config object
1857  */
1858
1859
1860 Roo.bootstrap.MenuSeparator = function(config){
1861     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
1862 };
1863
1864 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
1865     
1866     getAutoCreate : function(){
1867         var cfg = {
1868             cls: 'divider',
1869             tag : 'li'
1870         };
1871         
1872         return cfg;
1873     }
1874    
1875 });
1876
1877  
1878
1879  
1880 /*
1881 <div class="modal fade">
1882   <div class="modal-dialog">
1883     <div class="modal-content">
1884       <div class="modal-header">
1885         <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
1886         <h4 class="modal-title">Modal title</h4>
1887       </div>
1888       <div class="modal-body">
1889         <p>One fine body&hellip;</p>
1890       </div>
1891       <div class="modal-footer">
1892         <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
1893         <button type="button" class="btn btn-primary">Save changes</button>
1894       </div>
1895     </div><!-- /.modal-content -->
1896   </div><!-- /.modal-dialog -->
1897 </div><!-- /.modal -->
1898 */
1899 /*
1900  * - LGPL
1901  *
1902  * page contgainer.
1903  * 
1904  */
1905
1906 /**
1907  * @class Roo.bootstrap.Modal
1908  * @extends Roo.bootstrap.Component
1909  * Bootstrap Modal class
1910  * @cfg {String} title Title of dialog
1911  * @cfg {Boolean} specificTitle (true|false) default false
1912  * @cfg {Array} buttons Array of buttons or standard button set..
1913  * @cfg {String} buttonPosition (left|right|center) default right
1914  * 
1915  * @constructor
1916  * Create a new Modal Dialog
1917  * @param {Object} config The config object
1918  */
1919
1920 Roo.bootstrap.Modal = function(config){
1921     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
1922     this.addEvents({
1923         // raw events
1924         /**
1925          * @event btnclick
1926          * The raw btnclick event for the button
1927          * @param {Roo.EventObject} e
1928          */
1929         "btnclick" : true
1930     });
1931     this.buttons = this.buttons || [];
1932 };
1933
1934 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
1935     
1936     title : 'test dialog',
1937    
1938     buttons : false,
1939     
1940     // set on load...
1941     body:  false,
1942     
1943     specificTitle: false,
1944     
1945     buttonPosition: 'right',
1946     
1947     onRender : function(ct, position)
1948     {
1949         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
1950      
1951         if(!this.el){
1952             var cfg = Roo.apply({},  this.getAutoCreate());
1953             cfg.id = Roo.id();
1954             //if(!cfg.name){
1955             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
1956             //}
1957             //if (!cfg.name.length) {
1958             //    delete cfg.name;
1959            // }
1960             if (this.cls) {
1961                 cfg.cls += ' ' + this.cls;
1962             }
1963             if (this.style) {
1964                 cfg.style = this.style;
1965             }
1966             this.el = Roo.get(document.body).createChild(cfg, position);
1967         }
1968         //var type = this.el.dom.type;
1969         
1970         if(this.tabIndex !== undefined){
1971             this.el.dom.setAttribute('tabIndex', this.tabIndex);
1972         }
1973         
1974         
1975         
1976         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
1977         this.maskEl.enableDisplayMode("block");
1978         this.maskEl.hide();
1979         //this.el.addClass("x-dlg-modal");
1980     
1981         if (this.buttons.length) {
1982             Roo.each(this.buttons, function(bb) {
1983                 b = Roo.apply({}, bb);
1984                 b.xns = b.xns || Roo.bootstrap;
1985                 b.xtype = b.xtype || 'Button';
1986                 if (typeof(b.listeners) == 'undefined') {
1987                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
1988                 }
1989                 
1990                 var btn = Roo.factory(b);
1991                 
1992                 btn.onRender(this.el.select('.modal-footer div').first());
1993                 
1994             },this);
1995         }
1996         // render the children.
1997         var nitems = [];
1998         
1999         if(typeof(this.items) != 'undefined'){
2000             var items = this.items;
2001             delete this.items;
2002
2003             for(var i =0;i < items.length;i++) {
2004                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2005             }
2006         }
2007         
2008         this.items = nitems;
2009         
2010         this.body = this.el.select('.modal-body',true).first();
2011         this.close = this.el.select('.modal-header .close', true).first();
2012         this.footer = this.el.select('.modal-footer',true).first();
2013         this.initEvents();
2014         //this.el.addClass([this.fieldClass, this.cls]);
2015         
2016     },
2017     getAutoCreate : function(){
2018         
2019         
2020         var bdy = {
2021                 cls : 'modal-body',
2022                 html : this.html || ''
2023         };
2024         
2025         var title = {
2026             tag: 'h4',
2027             cls : 'modal-title',
2028             html : this.title
2029         };
2030         
2031         if(this.specificTitle){
2032             title = this.title;
2033         };
2034         
2035         return modal = {
2036             cls: "modal fade",
2037             style : 'display: none',
2038             cn : [
2039                 {
2040                     cls: "modal-dialog",
2041                     cn : [
2042                         {
2043                             cls : "modal-content",
2044                             cn : [
2045                                 {
2046                                     cls : 'modal-header',
2047                                     cn : [
2048                                         {
2049                                             tag: 'button',
2050                                             cls : 'close',
2051                                             html : '&times'
2052                                         },
2053                                         title
2054                                     ]
2055                                 },
2056                                 bdy,
2057                                 {
2058                                     cls : 'modal-footer',
2059                                     cn : [
2060                                         {
2061                                             tag: 'div',
2062                                             cls: 'btn-' + this.buttonPosition
2063                                         }
2064                                     ]
2065                                     
2066                                 }
2067                                 
2068                                 
2069                             ]
2070                             
2071                         }
2072                     ]
2073                         
2074                 }
2075             ]
2076             
2077             
2078         };
2079           
2080     },
2081     getChildContainer : function() {
2082          
2083          return this.el.select('.modal-body',true).first();
2084         
2085     },
2086     getButtonContainer : function() {
2087          return this.el.select('.modal-footer div',true).first();
2088         
2089     },
2090     initEvents : function()
2091     {
2092         this.el.select('.modal-header .close').on('click', this.hide, this);
2093 //        
2094 //        this.addxtype(this);
2095     },
2096     show : function() {
2097         
2098         if (!this.rendered) {
2099             this.render();
2100         }
2101        
2102         this.el.addClass('on');
2103         this.el.removeClass('fade');
2104         this.el.setStyle('display', 'block');
2105         Roo.get(document.body).addClass("x-body-masked");
2106         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2107         this.maskEl.show();
2108         this.el.setStyle('zIndex', '10001');
2109         this.fireEvent('show', this);
2110         
2111         
2112     },
2113     hide : function()
2114     {
2115         Roo.log('Modal hide?!');
2116         this.maskEl.hide();
2117         Roo.get(document.body).removeClass("x-body-masked");
2118         this.el.removeClass('on');
2119         this.el.addClass('fade');
2120         this.el.setStyle('display', 'none');
2121         this.fireEvent('hide', this);
2122     },
2123     
2124     addButton : function(str, cb)
2125     {
2126          
2127         
2128         var b = Roo.apply({}, { html : str } );
2129         b.xns = b.xns || Roo.bootstrap;
2130         b.xtype = b.xtype || 'Button';
2131         if (typeof(b.listeners) == 'undefined') {
2132             b.listeners = { click : cb.createDelegate(this)  };
2133         }
2134         
2135         var btn = Roo.factory(b);
2136            
2137         btn.onRender(this.el.select('.modal-footer div').first());
2138         
2139         return btn;   
2140        
2141     },
2142     
2143     setDefaultButton : function(btn)
2144     {
2145         //this.el.select('.modal-footer').()
2146     },
2147     resizeTo: function(w,h)
2148     {
2149         // skip..
2150     },
2151     setContentSize  : function(w, h)
2152     {
2153         
2154     },
2155     onButtonClick: function(btn,e)
2156     {
2157         //Roo.log([a,b,c]);
2158         this.fireEvent('btnclick', btn.name, e);
2159     },
2160     setTitle: function(str) {
2161         this.el.select('.modal-title',true).first().dom.innerHTML = str;
2162         
2163     }
2164 });
2165
2166
2167 Roo.apply(Roo.bootstrap.Modal,  {
2168     /**
2169          * Button config that displays a single OK button
2170          * @type Object
2171          */
2172         OK :  [{
2173             name : 'ok',
2174             weight : 'primary',
2175             html : 'OK'
2176         }], 
2177         /**
2178          * Button config that displays Yes and No buttons
2179          * @type Object
2180          */
2181         YESNO : [
2182             {
2183                 name  : 'no',
2184                 html : 'No'
2185             },
2186             {
2187                 name  :'yes',
2188                 weight : 'primary',
2189                 html : 'Yes'
2190             }
2191         ],
2192         
2193         /**
2194          * Button config that displays OK and Cancel buttons
2195          * @type Object
2196          */
2197         OKCANCEL : [
2198             {
2199                name : 'cancel',
2200                 html : 'Cancel'
2201             },
2202             {
2203                 name : 'ok',
2204                 weight : 'primary',
2205                 html : 'OK'
2206             }
2207         ],
2208         /**
2209          * Button config that displays Yes, No and Cancel buttons
2210          * @type Object
2211          */
2212         YESNOCANCEL : [
2213             {
2214                 name : 'yes',
2215                 weight : 'primary',
2216                 html : 'Yes'
2217             },
2218             {
2219                 name : 'no',
2220                 html : 'No'
2221             },
2222             {
2223                 name : 'cancel',
2224                 html : 'Cancel'
2225             }
2226         ]
2227 });
2228  /*
2229  * - LGPL
2230  *
2231  * messagebox - can be used as a replace
2232  * 
2233  */
2234 /**
2235  * @class Roo.MessageBox
2236  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2237  * Example usage:
2238  *<pre><code>
2239 // Basic alert:
2240 Roo.Msg.alert('Status', 'Changes saved successfully.');
2241
2242 // Prompt for user data:
2243 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2244     if (btn == 'ok'){
2245         // process text value...
2246     }
2247 });
2248
2249 // Show a dialog using config options:
2250 Roo.Msg.show({
2251    title:'Save Changes?',
2252    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2253    buttons: Roo.Msg.YESNOCANCEL,
2254    fn: processResult,
2255    animEl: 'elId'
2256 });
2257 </code></pre>
2258  * @singleton
2259  */
2260 Roo.bootstrap.MessageBox = function(){
2261     var dlg, opt, mask, waitTimer;
2262     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2263     var buttons, activeTextEl, bwidth;
2264
2265     
2266     // private
2267     var handleButton = function(button){
2268         dlg.hide();
2269         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2270     };
2271
2272     // private
2273     var handleHide = function(){
2274         if(opt && opt.cls){
2275             dlg.el.removeClass(opt.cls);
2276         }
2277         //if(waitTimer){
2278         //    Roo.TaskMgr.stop(waitTimer);
2279         //    waitTimer = null;
2280         //}
2281     };
2282
2283     // private
2284     var updateButtons = function(b){
2285         var width = 0;
2286         if(!b){
2287             buttons["ok"].hide();
2288             buttons["cancel"].hide();
2289             buttons["yes"].hide();
2290             buttons["no"].hide();
2291             //dlg.footer.dom.style.display = 'none';
2292             return width;
2293         }
2294         dlg.footer.dom.style.display = '';
2295         for(var k in buttons){
2296             if(typeof buttons[k] != "function"){
2297                 if(b[k]){
2298                     buttons[k].show();
2299                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2300                     width += buttons[k].el.getWidth()+15;
2301                 }else{
2302                     buttons[k].hide();
2303                 }
2304             }
2305         }
2306         return width;
2307     };
2308
2309     // private
2310     var handleEsc = function(d, k, e){
2311         if(opt && opt.closable !== false){
2312             dlg.hide();
2313         }
2314         if(e){
2315             e.stopEvent();
2316         }
2317     };
2318
2319     return {
2320         /**
2321          * Returns a reference to the underlying {@link Roo.BasicDialog} element
2322          * @return {Roo.BasicDialog} The BasicDialog element
2323          */
2324         getDialog : function(){
2325            if(!dlg){
2326                 dlg = new Roo.bootstrap.Modal( {
2327                     //draggable: true,
2328                     //resizable:false,
2329                     //constraintoviewport:false,
2330                     //fixedcenter:true,
2331                     //collapsible : false,
2332                     //shim:true,
2333                     //modal: true,
2334                   //  width:400,
2335                   //  height:100,
2336                     //buttonAlign:"center",
2337                     closeClick : function(){
2338                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2339                             handleButton("no");
2340                         }else{
2341                             handleButton("cancel");
2342                         }
2343                     }
2344                 });
2345                 dlg.render();
2346                 dlg.on("hide", handleHide);
2347                 mask = dlg.mask;
2348                 //dlg.addKeyListener(27, handleEsc);
2349                 buttons = {};
2350                 this.buttons = buttons;
2351                 var bt = this.buttonText;
2352                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2353                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2354                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2355                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2356                 Roo.log(buttons)
2357                 bodyEl = dlg.body.createChild({
2358
2359                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2360                         '<textarea class="roo-mb-textarea"></textarea>' +
2361                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
2362                 });
2363                 msgEl = bodyEl.dom.firstChild;
2364                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2365                 textboxEl.enableDisplayMode();
2366                 textboxEl.addKeyListener([10,13], function(){
2367                     if(dlg.isVisible() && opt && opt.buttons){
2368                         if(opt.buttons.ok){
2369                             handleButton("ok");
2370                         }else if(opt.buttons.yes){
2371                             handleButton("yes");
2372                         }
2373                     }
2374                 });
2375                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2376                 textareaEl.enableDisplayMode();
2377                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2378                 progressEl.enableDisplayMode();
2379                 var pf = progressEl.dom.firstChild;
2380                 if (pf) {
2381                     pp = Roo.get(pf.firstChild);
2382                     pp.setHeight(pf.offsetHeight);
2383                 }
2384                 
2385             }
2386             return dlg;
2387         },
2388
2389         /**
2390          * Updates the message box body text
2391          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2392          * the XHTML-compliant non-breaking space character '&amp;#160;')
2393          * @return {Roo.MessageBox} This message box
2394          */
2395         updateText : function(text){
2396             if(!dlg.isVisible() && !opt.width){
2397                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2398             }
2399             msgEl.innerHTML = text || '&#160;';
2400       
2401             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2402             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2403             var w = Math.max(
2404                     Math.min(opt.width || cw , this.maxWidth), 
2405                     Math.max(opt.minWidth || this.minWidth, bwidth)
2406             );
2407             if(opt.prompt){
2408                 activeTextEl.setWidth(w);
2409             }
2410             if(dlg.isVisible()){
2411                 dlg.fixedcenter = false;
2412             }
2413             // to big, make it scroll. = But as usual stupid IE does not support
2414             // !important..
2415             
2416             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2417                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2418                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2419             } else {
2420                 bodyEl.dom.style.height = '';
2421                 bodyEl.dom.style.overflowY = '';
2422             }
2423             if (cw > w) {
2424                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2425             } else {
2426                 bodyEl.dom.style.overflowX = '';
2427             }
2428             
2429             dlg.setContentSize(w, bodyEl.getHeight());
2430             if(dlg.isVisible()){
2431                 dlg.fixedcenter = true;
2432             }
2433             return this;
2434         },
2435
2436         /**
2437          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
2438          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2439          * @param {Number} value Any number between 0 and 1 (e.g., .5)
2440          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2441          * @return {Roo.MessageBox} This message box
2442          */
2443         updateProgress : function(value, text){
2444             if(text){
2445                 this.updateText(text);
2446             }
2447             if (pp) { // weird bug on my firefox - for some reason this is not defined
2448                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2449             }
2450             return this;
2451         },        
2452
2453         /**
2454          * Returns true if the message box is currently displayed
2455          * @return {Boolean} True if the message box is visible, else false
2456          */
2457         isVisible : function(){
2458             return dlg && dlg.isVisible();  
2459         },
2460
2461         /**
2462          * Hides the message box if it is displayed
2463          */
2464         hide : function(){
2465             if(this.isVisible()){
2466                 dlg.hide();
2467             }  
2468         },
2469
2470         /**
2471          * Displays a new message box, or reinitializes an existing message box, based on the config options
2472          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2473          * The following config object properties are supported:
2474          * <pre>
2475 Property    Type             Description
2476 ----------  ---------------  ------------------------------------------------------------------------------------
2477 animEl            String/Element   An id or Element from which the message box should animate as it opens and
2478                                    closes (defaults to undefined)
2479 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
2480                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
2481 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
2482                                    progress and wait dialogs will ignore this property and always hide the
2483                                    close button as they can only be closed programmatically.
2484 cls               String           A custom CSS class to apply to the message box element
2485 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
2486                                    displayed (defaults to 75)
2487 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
2488                                    function will be btn (the name of the button that was clicked, if applicable,
2489                                    e.g. "ok"), and text (the value of the active text field, if applicable).
2490                                    Progress and wait dialogs will ignore this option since they do not respond to
2491                                    user actions and can only be closed programmatically, so any required function
2492                                    should be called by the same code after it closes the dialog.
2493 icon              String           A CSS class that provides a background image to be used as an icon for
2494                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
2495 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
2496 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
2497 modal             Boolean          False to allow user interaction with the page while the message box is
2498                                    displayed (defaults to true)
2499 msg               String           A string that will replace the existing message box body text (defaults
2500                                    to the XHTML-compliant non-breaking space character '&#160;')
2501 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
2502 progress          Boolean          True to display a progress bar (defaults to false)
2503 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
2504 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
2505 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
2506 title             String           The title text
2507 value             String           The string value to set into the active textbox element if displayed
2508 wait              Boolean          True to display a progress bar (defaults to false)
2509 width             Number           The width of the dialog in pixels
2510 </pre>
2511          *
2512          * Example usage:
2513          * <pre><code>
2514 Roo.Msg.show({
2515    title: 'Address',
2516    msg: 'Please enter your address:',
2517    width: 300,
2518    buttons: Roo.MessageBox.OKCANCEL,
2519    multiline: true,
2520    fn: saveAddress,
2521    animEl: 'addAddressBtn'
2522 });
2523 </code></pre>
2524          * @param {Object} config Configuration options
2525          * @return {Roo.MessageBox} This message box
2526          */
2527         show : function(options)
2528         {
2529             
2530             // this causes nightmares if you show one dialog after another
2531             // especially on callbacks..
2532              
2533             if(this.isVisible()){
2534                 
2535                 this.hide();
2536                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
2537                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
2538                 Roo.log("New Dialog Message:" +  options.msg )
2539                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
2540                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
2541                 
2542             }
2543             var d = this.getDialog();
2544             opt = options;
2545             d.setTitle(opt.title || "&#160;");
2546             d.close.setDisplayed(opt.closable !== false);
2547             activeTextEl = textboxEl;
2548             opt.prompt = opt.prompt || (opt.multiline ? true : false);
2549             if(opt.prompt){
2550                 if(opt.multiline){
2551                     textboxEl.hide();
2552                     textareaEl.show();
2553                     textareaEl.setHeight(typeof opt.multiline == "number" ?
2554                         opt.multiline : this.defaultTextHeight);
2555                     activeTextEl = textareaEl;
2556                 }else{
2557                     textboxEl.show();
2558                     textareaEl.hide();
2559                 }
2560             }else{
2561                 textboxEl.hide();
2562                 textareaEl.hide();
2563             }
2564             progressEl.setDisplayed(opt.progress === true);
2565             this.updateProgress(0);
2566             activeTextEl.dom.value = opt.value || "";
2567             if(opt.prompt){
2568                 dlg.setDefaultButton(activeTextEl);
2569             }else{
2570                 var bs = opt.buttons;
2571                 var db = null;
2572                 if(bs && bs.ok){
2573                     db = buttons["ok"];
2574                 }else if(bs && bs.yes){
2575                     db = buttons["yes"];
2576                 }
2577                 dlg.setDefaultButton(db);
2578             }
2579             bwidth = updateButtons(opt.buttons);
2580             this.updateText(opt.msg);
2581             if(opt.cls){
2582                 d.el.addClass(opt.cls);
2583             }
2584             d.proxyDrag = opt.proxyDrag === true;
2585             d.modal = opt.modal !== false;
2586             d.mask = opt.modal !== false ? mask : false;
2587             if(!d.isVisible()){
2588                 // force it to the end of the z-index stack so it gets a cursor in FF
2589                 document.body.appendChild(dlg.el.dom);
2590                 d.animateTarget = null;
2591                 d.show(options.animEl);
2592             }
2593             return this;
2594         },
2595
2596         /**
2597          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
2598          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
2599          * and closing the message box when the process is complete.
2600          * @param {String} title The title bar text
2601          * @param {String} msg The message box body text
2602          * @return {Roo.MessageBox} This message box
2603          */
2604         progress : function(title, msg){
2605             this.show({
2606                 title : title,
2607                 msg : msg,
2608                 buttons: false,
2609                 progress:true,
2610                 closable:false,
2611                 minWidth: this.minProgressWidth,
2612                 modal : true
2613             });
2614             return this;
2615         },
2616
2617         /**
2618          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
2619          * If a callback function is passed it will be called after the user clicks the button, and the
2620          * id of the button that was clicked will be passed as the only parameter to the callback
2621          * (could also be the top-right close button).
2622          * @param {String} title The title bar text
2623          * @param {String} msg The message box body text
2624          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2625          * @param {Object} scope (optional) The scope of the callback function
2626          * @return {Roo.MessageBox} This message box
2627          */
2628         alert : function(title, msg, fn, scope){
2629             this.show({
2630                 title : title,
2631                 msg : msg,
2632                 buttons: this.OK,
2633                 fn: fn,
2634                 scope : scope,
2635                 modal : true
2636             });
2637             return this;
2638         },
2639
2640         /**
2641          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
2642          * interaction while waiting for a long-running process to complete that does not have defined intervals.
2643          * You are responsible for closing the message box when the process is complete.
2644          * @param {String} msg The message box body text
2645          * @param {String} title (optional) The title bar text
2646          * @return {Roo.MessageBox} This message box
2647          */
2648         wait : function(msg, title){
2649             this.show({
2650                 title : title,
2651                 msg : msg,
2652                 buttons: false,
2653                 closable:false,
2654                 progress:true,
2655                 modal:true,
2656                 width:300,
2657                 wait:true
2658             });
2659             waitTimer = Roo.TaskMgr.start({
2660                 run: function(i){
2661                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
2662                 },
2663                 interval: 1000
2664             });
2665             return this;
2666         },
2667
2668         /**
2669          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
2670          * If a callback function is passed it will be called after the user clicks either button, and the id of the
2671          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
2672          * @param {String} title The title bar text
2673          * @param {String} msg The message box body text
2674          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2675          * @param {Object} scope (optional) The scope of the callback function
2676          * @return {Roo.MessageBox} This message box
2677          */
2678         confirm : function(title, msg, fn, scope){
2679             this.show({
2680                 title : title,
2681                 msg : msg,
2682                 buttons: this.YESNO,
2683                 fn: fn,
2684                 scope : scope,
2685                 modal : true
2686             });
2687             return this;
2688         },
2689
2690         /**
2691          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
2692          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
2693          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
2694          * (could also be the top-right close button) and the text that was entered will be passed as the two
2695          * parameters to the callback.
2696          * @param {String} title The title bar text
2697          * @param {String} msg The message box body text
2698          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2699          * @param {Object} scope (optional) The scope of the callback function
2700          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
2701          * property, or the height in pixels to create the textbox (defaults to false / single-line)
2702          * @return {Roo.MessageBox} This message box
2703          */
2704         prompt : function(title, msg, fn, scope, multiline){
2705             this.show({
2706                 title : title,
2707                 msg : msg,
2708                 buttons: this.OKCANCEL,
2709                 fn: fn,
2710                 minWidth:250,
2711                 scope : scope,
2712                 prompt:true,
2713                 multiline: multiline,
2714                 modal : true
2715             });
2716             return this;
2717         },
2718
2719         /**
2720          * Button config that displays a single OK button
2721          * @type Object
2722          */
2723         OK : {ok:true},
2724         /**
2725          * Button config that displays Yes and No buttons
2726          * @type Object
2727          */
2728         YESNO : {yes:true, no:true},
2729         /**
2730          * Button config that displays OK and Cancel buttons
2731          * @type Object
2732          */
2733         OKCANCEL : {ok:true, cancel:true},
2734         /**
2735          * Button config that displays Yes, No and Cancel buttons
2736          * @type Object
2737          */
2738         YESNOCANCEL : {yes:true, no:true, cancel:true},
2739
2740         /**
2741          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
2742          * @type Number
2743          */
2744         defaultTextHeight : 75,
2745         /**
2746          * The maximum width in pixels of the message box (defaults to 600)
2747          * @type Number
2748          */
2749         maxWidth : 600,
2750         /**
2751          * The minimum width in pixels of the message box (defaults to 100)
2752          * @type Number
2753          */
2754         minWidth : 100,
2755         /**
2756          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
2757          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
2758          * @type Number
2759          */
2760         minProgressWidth : 250,
2761         /**
2762          * An object containing the default button text strings that can be overriden for localized language support.
2763          * Supported properties are: ok, cancel, yes and no.
2764          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
2765          * @type Object
2766          */
2767         buttonText : {
2768             ok : "OK",
2769             cancel : "Cancel",
2770             yes : "Yes",
2771             no : "No"
2772         }
2773     };
2774 }();
2775
2776 /**
2777  * Shorthand for {@link Roo.MessageBox}
2778  */
2779 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox 
2780 Roo.Msg = Roo.Msg || Roo.MessageBox;
2781 /*
2782  * - LGPL
2783  *
2784  * navbar
2785  * 
2786  */
2787
2788 /**
2789  * @class Roo.bootstrap.Navbar
2790  * @extends Roo.bootstrap.Component
2791  * Bootstrap Navbar class
2792
2793  * @constructor
2794  * Create a new Navbar
2795  * @param {Object} config The config object
2796  */
2797
2798
2799 Roo.bootstrap.Navbar = function(config){
2800     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
2801     
2802 };
2803
2804 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
2805     
2806     
2807    
2808     // private
2809     navItems : false,
2810     loadMask : false,
2811     
2812     
2813     getAutoCreate : function(){
2814         
2815         
2816         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
2817         
2818     },
2819     
2820     initEvents :function ()
2821     {
2822         //Roo.log(this.el.select('.navbar-toggle',true));
2823         this.el.select('.navbar-toggle',true).on('click', function() {
2824            // Roo.log('click');
2825             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
2826         }, this);
2827         
2828         var mark = {
2829             tag: "div",
2830             cls:"x-dlg-mask"
2831         }
2832         
2833         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
2834         
2835         var size = this.el.getSize();
2836         this.maskEl.setSize(size.width, size.height);
2837         this.maskEl.enableDisplayMode("block");
2838         this.maskEl.hide();
2839         
2840         if(this.loadMask){
2841             this.maskEl.show();
2842         }
2843     },
2844     
2845     
2846     getChildContainer : function()
2847     {
2848         if (this.el.select('.collapse').getCount()) {
2849             return this.el.select('.collapse',true).first();
2850         }
2851         
2852         return this.el;
2853     },
2854     
2855     mask : function()
2856     {
2857         this.maskEl.show();
2858     },
2859     
2860     unmask : function()
2861     {
2862         this.maskEl.hide();
2863     } 
2864     
2865     
2866     
2867     
2868 });
2869
2870
2871
2872  
2873
2874  /*
2875  * - LGPL
2876  *
2877  * navbar
2878  * 
2879  */
2880
2881 /**
2882  * @class Roo.bootstrap.NavSimplebar
2883  * @extends Roo.bootstrap.Navbar
2884  * Bootstrap Sidebar class
2885  *
2886  * @cfg {Boolean} inverse is inverted color
2887  * 
2888  * @cfg {String} type (nav | pills | tabs)
2889  * @cfg {Boolean} arrangement stacked | justified
2890  * @cfg {String} align (left | right) alignment
2891  * 
2892  * @cfg {Boolean} main (true|false) main nav bar? default false
2893  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
2894  * 
2895  * @cfg {String} tag (header|footer|nav|div) default is nav 
2896
2897  * 
2898  * 
2899  * 
2900  * @constructor
2901  * Create a new Sidebar
2902  * @param {Object} config The config object
2903  */
2904
2905
2906 Roo.bootstrap.NavSimplebar = function(config){
2907     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
2908 };
2909
2910 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
2911     
2912     inverse: false,
2913     
2914     type: false,
2915     arrangement: '',
2916     align : false,
2917     
2918     
2919     
2920     main : false,
2921     
2922     
2923     tag : false,
2924     
2925     
2926     getAutoCreate : function(){
2927         
2928         
2929         var cfg = {
2930             tag : this.tag || 'div',
2931             cls : 'navbar'
2932         };
2933           
2934         
2935         cfg.cn = [
2936             {
2937                 cls: 'nav',
2938                 tag : 'ul'
2939             }
2940         ];
2941         
2942          
2943         this.type = this.type || 'nav';
2944         if (['tabs','pills'].indexOf(this.type)!==-1) {
2945             cfg.cn[0].cls += ' nav-' + this.type
2946         
2947         
2948         } else {
2949             if (this.type!=='nav') {
2950                 Roo.log('nav type must be nav/tabs/pills')
2951             }
2952             cfg.cn[0].cls += ' navbar-nav'
2953         }
2954         
2955         
2956         
2957         
2958         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
2959             cfg.cn[0].cls += ' nav-' + this.arrangement;
2960         }
2961         
2962         
2963         if (this.align === 'right') {
2964             cfg.cn[0].cls += ' navbar-right';
2965         }
2966         
2967         if (this.inverse) {
2968             cfg.cls += ' navbar-inverse';
2969             
2970         }
2971         
2972         
2973         return cfg;
2974     
2975         
2976     }
2977     
2978     
2979     
2980 });
2981
2982
2983
2984  
2985
2986  
2987        /*
2988  * - LGPL
2989  *
2990  * navbar
2991  * 
2992  */
2993
2994 /**
2995  * @class Roo.bootstrap.NavHeaderbar
2996  * @extends Roo.bootstrap.NavSimplebar
2997  * Bootstrap Sidebar class
2998  *
2999  * @cfg {String} brand what is brand
3000  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3001  * @cfg {String} brand_href href of the brand
3002  * @cfg {Boolean} srButton generate the sr-only button (true | false) default true
3003  * 
3004  * @constructor
3005  * Create a new Sidebar
3006  * @param {Object} config The config object
3007  */
3008
3009
3010 Roo.bootstrap.NavHeaderbar = function(config){
3011     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3012 };
3013
3014 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3015     
3016     position: '',
3017     brand: '',
3018     brand_href: false,
3019     srButton : true,
3020     
3021     
3022     getAutoCreate : function(){
3023         
3024         var   cfg = {
3025             tag: this.nav || 'nav',
3026             cls: 'navbar',
3027             role: 'navigation',
3028             cn: []
3029         };
3030         
3031         if(this.srButton){
3032             cfg.cn.push({
3033                 tag: 'div',
3034                 cls: 'navbar-header',
3035                 cn: [
3036                     {
3037                         tag: 'button',
3038                         type: 'button',
3039                         cls: 'navbar-toggle',
3040                         'data-toggle': 'collapse',
3041                         cn: [
3042                             {
3043                                 tag: 'span',
3044                                 cls: 'sr-only',
3045                                 html: 'Toggle navigation'
3046                             },
3047                             {
3048                                 tag: 'span',
3049                                 cls: 'icon-bar'
3050                             },
3051                             {
3052                                 tag: 'span',
3053                                 cls: 'icon-bar'
3054                             },
3055                             {
3056                                 tag: 'span',
3057                                 cls: 'icon-bar'
3058                             }
3059                         ]
3060                     }
3061                 ]
3062             });
3063         }
3064         
3065         cfg.cn.push({
3066             tag: 'div',
3067             cls: 'collapse navbar-collapse',
3068             cn : []
3069         });
3070         
3071         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3072         
3073         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3074             cfg.cls += ' navbar-' + this.position;
3075             
3076             // tag can override this..
3077             
3078             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3079         }
3080         
3081         if (this.brand !== '') {
3082             cfg.cn[0].cn.push({
3083                 tag: 'a',
3084                 href: this.brand_href ? this.brand_href : '#',
3085                 cls: 'navbar-brand',
3086                 cn: [
3087                 this.brand
3088                 ]
3089             });
3090         }
3091         
3092         if(this.main){
3093             cfg.cls += ' main-nav';
3094         }
3095         
3096         
3097         return cfg;
3098
3099         
3100     }
3101     
3102     
3103     
3104 });
3105
3106
3107
3108  
3109
3110  /*
3111  * - LGPL
3112  *
3113  * navbar
3114  * 
3115  */
3116
3117 /**
3118  * @class Roo.bootstrap.NavSidebar
3119  * @extends Roo.bootstrap.Navbar
3120  * Bootstrap Sidebar class
3121  * 
3122  * @constructor
3123  * Create a new Sidebar
3124  * @param {Object} config The config object
3125  */
3126
3127
3128 Roo.bootstrap.NavSidebar = function(config){
3129     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3130 };
3131
3132 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3133     
3134     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3135     
3136     getAutoCreate : function(){
3137         
3138         
3139         return  {
3140             tag: 'div',
3141             cls: 'sidebar sidebar-nav'
3142         };
3143     
3144         
3145     }
3146     
3147     
3148     
3149 });
3150
3151
3152
3153  
3154
3155  /*
3156  * - LGPL
3157  *
3158  * nav group
3159  * 
3160  */
3161
3162 /**
3163  * @class Roo.bootstrap.NavGroup
3164  * @extends Roo.bootstrap.Component
3165  * Bootstrap NavGroup class
3166  * @cfg {String} align left | right
3167  * @cfg {Boolean} inverse false | true
3168  * @cfg {String} type (nav|pills|tab) default nav
3169  * @cfg {String} navId - reference Id for navbar.
3170
3171  * 
3172  * @constructor
3173  * Create a new nav group
3174  * @param {Object} config The config object
3175  */
3176
3177 Roo.bootstrap.NavGroup = function(config){
3178     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3179     this.navItems = [];
3180    
3181     Roo.bootstrap.NavGroup.register(this);
3182      this.addEvents({
3183         /**
3184              * @event changed
3185              * Fires when the active item changes
3186              * @param {Roo.bootstrap.NavGroup} this
3187              * @param {Roo.bootstrap.Navbar.Item} item The item selected
3188              * @param {Roo.bootstrap.Navbar.Item} item The previously selected item 
3189          */
3190         'changed': true
3191      });
3192     
3193 };
3194
3195 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3196     
3197     align: '',
3198     inverse: false,
3199     form: false,
3200     type: 'nav',
3201     navId : '',
3202     // private
3203     
3204     navItems : false, 
3205     
3206     getAutoCreate : function()
3207     {
3208         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3209         
3210         cfg = {
3211             tag : 'ul',
3212             cls: 'nav' 
3213         }
3214         
3215         if (['tabs','pills'].indexOf(this.type)!==-1) {
3216             cfg.cls += ' nav-' + this.type
3217         } else {
3218             if (this.type!=='nav') {
3219                 Roo.log('nav type must be nav/tabs/pills')
3220             }
3221             cfg.cls += ' navbar-nav'
3222         }
3223         
3224         if (this.parent().sidebar) {
3225             cfg = {
3226                 tag: 'ul',
3227                 cls: 'dashboard-menu sidebar-menu'
3228             }
3229             
3230             return cfg;
3231         }
3232         
3233         if (this.form === true) {
3234             cfg = {
3235                 tag: 'form',
3236                 cls: 'navbar-form'
3237             }
3238             
3239             if (this.align === 'right') {
3240                 cfg.cls += ' navbar-right';
3241             } else {
3242                 cfg.cls += ' navbar-left';
3243             }
3244         }
3245         
3246         if (this.align === 'right') {
3247             cfg.cls += ' navbar-right';
3248         }
3249         
3250         if (this.inverse) {
3251             cfg.cls += ' navbar-inverse';
3252             
3253         }
3254         
3255         
3256         return cfg;
3257     },
3258     /**
3259     * sets the active Navigation item
3260     * @param {Roo.bootstrap.NavItem} the new current navitem
3261     */
3262     setActiveItem : function(item)
3263     {
3264         var prev = false;
3265         Roo.each(this.navItems, function(v){
3266             if (v == item) {
3267                 return ;
3268             }
3269             if (v.isActive()) {
3270                 v.setActive(false, true);
3271                 prev = v;
3272                 
3273             }
3274             
3275         });
3276
3277         item.setActive(true, true);
3278         this.fireEvent('changed', this, item, prev);
3279         
3280         
3281     },
3282     /**
3283     * gets the active Navigation item
3284     * @return {Roo.bootstrap.NavItem} the current navitem
3285     */
3286     getActive : function()
3287     {
3288         
3289         var prev = false;
3290         Roo.each(this.navItems, function(v){
3291             
3292             if (v.isActive()) {
3293                 prev = v;
3294                 
3295             }
3296             
3297         });
3298         return prev;
3299     },
3300     
3301     indexOfNav : function()
3302     {
3303         
3304         var prev = false;
3305         Roo.each(this.navItems, function(v,i){
3306             
3307             if (v.isActive()) {
3308                 prev = i;
3309                 
3310             }
3311             
3312         });
3313         return prev;
3314     },
3315     /**
3316     * adds a Navigation item
3317     * @param {Roo.bootstrap.NavItem} the navitem to add
3318     */
3319     addItem : function(cfg)
3320     {
3321         var cn = new Roo.bootstrap.NavItem(cfg);
3322         this.register(cn);
3323         cn.parentId = this.id;
3324         cn.onRender(this.el, null);
3325         return cn;
3326     },
3327     /**
3328     * register a Navigation item
3329     * @param {Roo.bootstrap.NavItem} the navitem to add
3330     */
3331     register : function(item)
3332     {
3333         this.navItems.push( item);
3334         item.navId = this.navId;
3335     
3336     },
3337   
3338     
3339     getNavItem: function(tabId)
3340     {
3341         var ret = false;
3342         Roo.each(this.navItems, function(e) {
3343             if (e.tabId == tabId) {
3344                ret =  e;
3345                return false;
3346             }
3347             return true;
3348             
3349         });
3350         return ret;
3351     },
3352     
3353     setActiveNext : function()
3354     {
3355         var i = this.indexOfNav(this.getActive());
3356         if (i > this.navItems.length) {
3357             return;
3358         }
3359         this.setActiveItem(this.navItems[i+1]);
3360     },
3361     setActivePrev : function()
3362     {
3363         var i = this.indexOfNav(this.getActive());
3364         if (i  < 1) {
3365             return;
3366         }
3367         this.setActiveItem(this.navItems[i-1]);
3368     },
3369     clearWasActive : function(except) {
3370         Roo.each(this.navItems, function(e) {
3371             if (e.tabId != except.tabId && e.was_active) {
3372                e.was_active = false;
3373                return false;
3374             }
3375             return true;
3376             
3377         });
3378     },
3379     getWasActive : function ()
3380     {
3381         var r = false;
3382         Roo.each(this.navItems, function(e) {
3383             if (e.was_active) {
3384                r = e;
3385                return false;
3386             }
3387             return true;
3388             
3389         });
3390         return r;
3391     }
3392     
3393     
3394 });
3395
3396  
3397 Roo.apply(Roo.bootstrap.NavGroup, {
3398     
3399     groups: {},
3400      /**
3401     * register a Navigation Group
3402     * @param {Roo.bootstrap.NavGroup} the navgroup to add
3403     */
3404     register : function(navgrp)
3405     {
3406         this.groups[navgrp.navId] = navgrp;
3407         
3408     },
3409     /**
3410     * fetch a Navigation Group based on the navigation ID
3411     * @param {string} the navgroup to add
3412     * @returns {Roo.bootstrap.NavGroup} the navgroup 
3413     */
3414     get: function(navId) {
3415         if (typeof(this.groups[navId]) == 'undefined') {
3416             return false;
3417             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
3418         }
3419         return this.groups[navId] ;
3420     }
3421     
3422     
3423     
3424 });
3425
3426  /*
3427  * - LGPL
3428  *
3429  * row
3430  * 
3431  */
3432
3433 /**
3434  * @class Roo.bootstrap.NavItem
3435  * @extends Roo.bootstrap.Component
3436  * Bootstrap Navbar.NavItem class
3437  * @cfg {String} href  link to
3438  * @cfg {String} html content of button
3439  * @cfg {String} badge text inside badge
3440  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
3441  * @cfg {String} glyphicon name of glyphicon
3442  * @cfg {String} icon name of font awesome icon
3443  * @cfg {Boolean} active Is item active
3444  * @cfg {Boolean} disabled Is item disabled
3445  
3446  * @cfg {Boolean} preventDefault (true | false) default false
3447  * @cfg {String} tabId the tab that this item activates.
3448  * @cfg {String} tagtype (a|span) render as a href or span?
3449   
3450  * @constructor
3451  * Create a new Navbar Item
3452  * @param {Object} config The config object
3453  */
3454 Roo.bootstrap.NavItem = function(config){
3455     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
3456     this.addEvents({
3457         // raw events
3458         /**
3459          * @event click
3460          * The raw click event for the entire grid.
3461          * @param {Roo.EventObject} e
3462          */
3463         "click" : true,
3464          /**
3465             * @event changed
3466             * Fires when the active item active state changes
3467             * @param {Roo.bootstrap.NavItem} this
3468             * @param {boolean} state the new state
3469              
3470          */
3471         'changed': true
3472     });
3473    
3474 };
3475
3476 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
3477     
3478     href: false,
3479     html: '',
3480     badge: '',
3481     icon: false,
3482     glyphicon: false,
3483     active: false,
3484     preventDefault : false,
3485     tabId : false,
3486     tagtype : 'a',
3487     disabled : false,
3488     
3489     was_active : false,
3490     
3491     getAutoCreate : function(){
3492          
3493         var cfg = {
3494             tag: 'li',
3495             cls: 'nav-item'
3496             
3497         }
3498         if (this.active) {
3499             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
3500         }
3501         if (this.disabled) {
3502             cfg.cls += ' disabled';
3503         }
3504         
3505         if (this.href || this.html || this.glyphicon || this.icon) {
3506             cfg.cn = [
3507                 {
3508                     tag: this.tagtype,
3509                     href : this.href || "#",
3510                     html: this.html || ''
3511                 }
3512             ];
3513             
3514             if (this.icon) {
3515                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
3516             }
3517
3518             if(this.glyphicon) {
3519                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
3520             }
3521             
3522             if (this.menu) {
3523                 
3524                 cfg.cn[0].html += " <span class='caret'></span>";
3525              
3526             }
3527             
3528             if (this.badge !== '') {
3529                  
3530                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
3531             }
3532         }
3533         
3534         
3535         
3536         return cfg;
3537     },
3538     initEvents: function() {
3539        // Roo.log('init events?');
3540        // Roo.log(this.el.dom);
3541         if (typeof (this.menu) != 'undefined') {
3542             this.menu.parentType = this.xtype;
3543             this.menu.triggerEl = this.el;
3544             this.addxtype(Roo.apply({}, this.menu));
3545         }
3546
3547        
3548         this.el.select('a',true).on('click', this.onClick, this);
3549         // at this point parent should be available..
3550         this.parent().register(this);
3551     },
3552     
3553     onClick : function(e)
3554     {
3555          
3556         if(this.preventDefault){
3557             e.preventDefault();
3558         }
3559         if (this.disabled) {
3560             return;
3561         }
3562         
3563         var tg = Roo.bootstrap.TabGroup.get(this.navId);
3564         if (tg && tg.transition) {
3565             Roo.log("waiting for the transitionend");
3566             return;
3567         }
3568         
3569         Roo.log("fire event clicked");
3570         if(this.fireEvent('click', this, e) === false){
3571             return;
3572         };
3573         
3574         if (['tabs','pills'].indexOf(this.parent().type)!==-1) {
3575             if (typeof(this.parent().setActiveItem) !== 'undefined') {
3576                 this.parent().setActiveItem(this);
3577             }
3578         } 
3579     },
3580     
3581     isActive: function () {
3582         return this.active
3583     },
3584     setActive : function(state, fire, is_was_active)
3585     {
3586         if (this.active && !state & this.navId) {
3587             this.was_active = true;
3588             var nv = Roo.bootstrap.NavGroup.get(this.navId);
3589             if (nv) {
3590                 nv.clearWasActive(this);
3591             }
3592             
3593         }
3594         this.active = state;
3595         
3596         if (!state ) {
3597             this.el.removeClass('active');
3598         } else if (!this.el.hasClass('active')) {
3599             this.el.addClass('active');
3600         }
3601         if (fire) {
3602             this.fireEvent('changed', this, state);
3603         }
3604         
3605         // show a panel if it's registered and related..
3606         
3607         if (!this.navId || !this.tabId || !state || is_was_active) {
3608             return;
3609         }
3610         
3611         var tg = Roo.bootstrap.TabGroup.get(this.navId);
3612         if (!tg) {
3613             return;
3614         }
3615         var pan = tg.getPanelByName(this.tabId);
3616         if (!pan) {
3617             return;
3618         }
3619         // if we can not flip to new panel - go back to old nav highlight..
3620         if (false == tg.showPanel(pan)) {
3621             var nv = Roo.bootstrap.NavGroup.get(this.navId);
3622             if (nv) {
3623                 var onav = nv.getWasActive();
3624                 if (onav) {
3625                     onav.setActive(true, false, true);
3626                 }
3627             }
3628             
3629         }
3630         
3631         
3632         
3633     },
3634      // this should not be here...
3635     setDisabled : function(state)
3636     {
3637         this.disabled = state;
3638         if (!state ) {
3639             this.el.removeClass('disabled');
3640         } else if (!this.el.hasClass('disabled')) {
3641             this.el.addClass('disabled');
3642         }
3643         
3644     }
3645 });
3646  
3647
3648  /*
3649  * - LGPL
3650  *
3651  * sidebar item
3652  *
3653  *  li
3654  *    <span> icon </span>
3655  *    <span> text </span>
3656  *    <span>badge </span>
3657  */
3658
3659 /**
3660  * @class Roo.bootstrap.NavSidebarItem
3661  * @extends Roo.bootstrap.NavItem
3662  * Bootstrap Navbar.NavSidebarItem class
3663  * @constructor
3664  * Create a new Navbar Button
3665  * @param {Object} config The config object
3666  */
3667 Roo.bootstrap.NavSidebarItem = function(config){
3668     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
3669     this.addEvents({
3670         // raw events
3671         /**
3672          * @event click
3673          * The raw click event for the entire grid.
3674          * @param {Roo.EventObject} e
3675          */
3676         "click" : true,
3677          /**
3678             * @event changed
3679             * Fires when the active item active state changes
3680             * @param {Roo.bootstrap.NavSidebarItem} this
3681             * @param {boolean} state the new state
3682              
3683          */
3684         'changed': true
3685     });
3686    
3687 };
3688
3689 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
3690     
3691     
3692     getAutoCreate : function(){
3693         
3694         
3695         var a = {
3696                 tag: 'a',
3697                 href : this.href || '#',
3698                 cls: '',
3699                 html : '',
3700                 cn : []
3701         };
3702         var cfg = {
3703             tag: 'li',
3704             cls: '',
3705             cn: [ a ]
3706         }
3707         var span = {
3708             tag: 'span',
3709             html : this.html || ''
3710         }
3711         
3712         
3713         if (this.active) {
3714             cfg.cls += ' active';
3715         }
3716         
3717         // left icon..
3718         if (this.glyphicon || this.icon) {
3719             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
3720             a.cn.push({ tag : 'i', cls : c }) ;
3721         }
3722         // html..
3723         a.cn.push(span);
3724         // then badge..
3725         if (this.badge !== '') {
3726             a.cn.push({ tag: 'span',  cls : 'badge pull-right ' + (this.badgecls || ''), html: this.badge }); 
3727         }
3728         // fi
3729         if (this.menu) {
3730             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
3731             a.cls += 'dropdown-toggle treeview' ;
3732             
3733         }
3734         
3735         
3736         
3737         return cfg;
3738          
3739            
3740     }
3741    
3742      
3743  
3744 });
3745  
3746
3747  /*
3748  * - LGPL
3749  *
3750  * row
3751  * 
3752  */
3753
3754 /**
3755  * @class Roo.bootstrap.Row
3756  * @extends Roo.bootstrap.Component
3757  * Bootstrap Row class (contains columns...)
3758  * 
3759  * @constructor
3760  * Create a new Row
3761  * @param {Object} config The config object
3762  */
3763
3764 Roo.bootstrap.Row = function(config){
3765     Roo.bootstrap.Row.superclass.constructor.call(this, config);
3766 };
3767
3768 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
3769     
3770     getAutoCreate : function(){
3771        return {
3772             cls: 'row clearfix'
3773        };
3774     }
3775     
3776     
3777 });
3778
3779  
3780
3781  /*
3782  * - LGPL
3783  *
3784  * element
3785  * 
3786  */
3787
3788 /**
3789  * @class Roo.bootstrap.Element
3790  * @extends Roo.bootstrap.Component
3791  * Bootstrap Element class
3792  * @cfg {String} html contents of the element
3793  * @cfg {String} tag tag of the element
3794  * @cfg {String} cls class of the element
3795  * 
3796  * @constructor
3797  * Create a new Element
3798  * @param {Object} config The config object
3799  */
3800
3801 Roo.bootstrap.Element = function(config){
3802     Roo.bootstrap.Element.superclass.constructor.call(this, config);
3803 };
3804
3805 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
3806     
3807     tag: 'div',
3808     cls: '',
3809     html: '',
3810      
3811     
3812     getAutoCreate : function(){
3813         
3814         var cfg = {
3815             tag: this.tag,
3816             cls: this.cls,
3817             html: this.html
3818         }
3819         
3820         
3821         
3822         return cfg;
3823     }
3824    
3825 });
3826
3827  
3828
3829  /*
3830  * - LGPL
3831  *
3832  * pagination
3833  * 
3834  */
3835
3836 /**
3837  * @class Roo.bootstrap.Pagination
3838  * @extends Roo.bootstrap.Component
3839  * Bootstrap Pagination class
3840  * @cfg {String} size xs | sm | md | lg
3841  * @cfg {Boolean} inverse false | true
3842  * 
3843  * @constructor
3844  * Create a new Pagination
3845  * @param {Object} config The config object
3846  */
3847
3848 Roo.bootstrap.Pagination = function(config){
3849     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
3850 };
3851
3852 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
3853     
3854     cls: false,
3855     size: false,
3856     inverse: false,
3857     
3858     getAutoCreate : function(){
3859         var cfg = {
3860             tag: 'ul',
3861                 cls: 'pagination'
3862         };
3863         if (this.inverse) {
3864             cfg.cls += ' inverse';
3865         }
3866         if (this.html) {
3867             cfg.html=this.html;
3868         }
3869         if (this.cls) {
3870             cfg.cls += " " + this.cls;
3871         }
3872         return cfg;
3873     }
3874    
3875 });
3876
3877  
3878
3879  /*
3880  * - LGPL
3881  *
3882  * Pagination item
3883  * 
3884  */
3885
3886
3887 /**
3888  * @class Roo.bootstrap.PaginationItem
3889  * @extends Roo.bootstrap.Component
3890  * Bootstrap PaginationItem class
3891  * @cfg {String} html text
3892  * @cfg {String} href the link
3893  * @cfg {Boolean} preventDefault (true | false) default true
3894  * @cfg {Boolean} active (true | false) default false
3895  * 
3896  * 
3897  * @constructor
3898  * Create a new PaginationItem
3899  * @param {Object} config The config object
3900  */
3901
3902
3903 Roo.bootstrap.PaginationItem = function(config){
3904     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
3905     this.addEvents({
3906         // raw events
3907         /**
3908          * @event click
3909          * The raw click event for the entire grid.
3910          * @param {Roo.EventObject} e
3911          */
3912         "click" : true
3913     });
3914 };
3915
3916 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
3917     
3918     href : false,
3919     html : false,
3920     preventDefault: true,
3921     active : false,
3922     cls : false,
3923     
3924     getAutoCreate : function(){
3925         var cfg= {
3926             tag: 'li',
3927             cn: [
3928                 {
3929                     tag : 'a',
3930                     href : this.href ? this.href : '#',
3931                     html : this.html ? this.html : ''
3932                 }
3933             ]
3934         };
3935         
3936         if(this.cls){
3937             cfg.cls = this.cls;
3938         }
3939         
3940         if(this.active){
3941             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
3942         }
3943         
3944         return cfg;
3945     },
3946     
3947     initEvents: function() {
3948         
3949         this.el.on('click', this.onClick, this);
3950         
3951     },
3952     onClick : function(e)
3953     {
3954         Roo.log('PaginationItem on click ');
3955         if(this.preventDefault){
3956             e.preventDefault();
3957         }
3958         
3959         this.fireEvent('click', this, e);
3960     }
3961    
3962 });
3963
3964  
3965
3966  /*
3967  * - LGPL
3968  *
3969  * slider
3970  * 
3971  */
3972
3973
3974 /**
3975  * @class Roo.bootstrap.Slider
3976  * @extends Roo.bootstrap.Component
3977  * Bootstrap Slider class
3978  *    
3979  * @constructor
3980  * Create a new Slider
3981  * @param {Object} config The config object
3982  */
3983
3984 Roo.bootstrap.Slider = function(config){
3985     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
3986 };
3987
3988 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
3989     
3990     getAutoCreate : function(){
3991         
3992         var cfg = {
3993             tag: 'div',
3994             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
3995             cn: [
3996                 {
3997                     tag: 'a',
3998                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
3999                 }
4000             ]
4001         }
4002         
4003         return cfg;
4004     }
4005    
4006 });
4007
4008  /*
4009  * Based on:
4010  * Ext JS Library 1.1.1
4011  * Copyright(c) 2006-2007, Ext JS, LLC.
4012  *
4013  * Originally Released Under LGPL - original licence link has changed is not relivant.
4014  *
4015  * Fork - LGPL
4016  * <script type="text/javascript">
4017  */
4018  
4019
4020 /**
4021  * @class Roo.grid.ColumnModel
4022  * @extends Roo.util.Observable
4023  * This is the default implementation of a ColumnModel used by the Grid. It defines
4024  * the columns in the grid.
4025  * <br>Usage:<br>
4026  <pre><code>
4027  var colModel = new Roo.grid.ColumnModel([
4028         {header: "Ticker", width: 60, sortable: true, locked: true},
4029         {header: "Company Name", width: 150, sortable: true},
4030         {header: "Market Cap.", width: 100, sortable: true},
4031         {header: "$ Sales", width: 100, sortable: true, renderer: money},
4032         {header: "Employees", width: 100, sortable: true, resizable: false}
4033  ]);
4034  </code></pre>
4035  * <p>
4036  
4037  * The config options listed for this class are options which may appear in each
4038  * individual column definition.
4039  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4040  * @constructor
4041  * @param {Object} config An Array of column config objects. See this class's
4042  * config objects for details.
4043 */
4044 Roo.grid.ColumnModel = function(config){
4045         /**
4046      * The config passed into the constructor
4047      */
4048     this.config = config;
4049     this.lookup = {};
4050
4051     // if no id, create one
4052     // if the column does not have a dataIndex mapping,
4053     // map it to the order it is in the config
4054     for(var i = 0, len = config.length; i < len; i++){
4055         var c = config[i];
4056         if(typeof c.dataIndex == "undefined"){
4057             c.dataIndex = i;
4058         }
4059         if(typeof c.renderer == "string"){
4060             c.renderer = Roo.util.Format[c.renderer];
4061         }
4062         if(typeof c.id == "undefined"){
4063             c.id = Roo.id();
4064         }
4065         if(c.editor && c.editor.xtype){
4066             c.editor  = Roo.factory(c.editor, Roo.grid);
4067         }
4068         if(c.editor && c.editor.isFormField){
4069             c.editor = new Roo.grid.GridEditor(c.editor);
4070         }
4071         this.lookup[c.id] = c;
4072     }
4073
4074     /**
4075      * The width of columns which have no width specified (defaults to 100)
4076      * @type Number
4077      */
4078     this.defaultWidth = 100;
4079
4080     /**
4081      * Default sortable of columns which have no sortable specified (defaults to false)
4082      * @type Boolean
4083      */
4084     this.defaultSortable = false;
4085
4086     this.addEvents({
4087         /**
4088              * @event widthchange
4089              * Fires when the width of a column changes.
4090              * @param {ColumnModel} this
4091              * @param {Number} columnIndex The column index
4092              * @param {Number} newWidth The new width
4093              */
4094             "widthchange": true,
4095         /**
4096              * @event headerchange
4097              * Fires when the text of a header changes.
4098              * @param {ColumnModel} this
4099              * @param {Number} columnIndex The column index
4100              * @param {Number} newText The new header text
4101              */
4102             "headerchange": true,
4103         /**
4104              * @event hiddenchange
4105              * Fires when a column is hidden or "unhidden".
4106              * @param {ColumnModel} this
4107              * @param {Number} columnIndex The column index
4108              * @param {Boolean} hidden true if hidden, false otherwise
4109              */
4110             "hiddenchange": true,
4111             /**
4112          * @event columnmoved
4113          * Fires when a column is moved.
4114          * @param {ColumnModel} this
4115          * @param {Number} oldIndex
4116          * @param {Number} newIndex
4117          */
4118         "columnmoved" : true,
4119         /**
4120          * @event columlockchange
4121          * Fires when a column's locked state is changed
4122          * @param {ColumnModel} this
4123          * @param {Number} colIndex
4124          * @param {Boolean} locked true if locked
4125          */
4126         "columnlockchange" : true
4127     });
4128     Roo.grid.ColumnModel.superclass.constructor.call(this);
4129 };
4130 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4131     /**
4132      * @cfg {String} header The header text to display in the Grid view.
4133      */
4134     /**
4135      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4136      * {@link Roo.data.Record} definition from which to draw the column's value. If not
4137      * specified, the column's index is used as an index into the Record's data Array.
4138      */
4139     /**
4140      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4141      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4142      */
4143     /**
4144      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4145      * Defaults to the value of the {@link #defaultSortable} property.
4146      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4147      */
4148     /**
4149      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
4150      */
4151     /**
4152      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
4153      */
4154     /**
4155      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4156      */
4157     /**
4158      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4159      */
4160     /**
4161      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4162      * given the cell's data value. See {@link #setRenderer}. If not specified, the
4163      * default renderer uses the raw data value. If an object is returned (bootstrap only)
4164      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4165      */
4166        /**
4167      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
4168      */
4169     /**
4170      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
4171      */
4172
4173     /**
4174      * Returns the id of the column at the specified index.
4175      * @param {Number} index The column index
4176      * @return {String} the id
4177      */
4178     getColumnId : function(index){
4179         return this.config[index].id;
4180     },
4181
4182     /**
4183      * Returns the column for a specified id.
4184      * @param {String} id The column id
4185      * @return {Object} the column
4186      */
4187     getColumnById : function(id){
4188         return this.lookup[id];
4189     },
4190
4191     
4192     /**
4193      * Returns the column for a specified dataIndex.
4194      * @param {String} dataIndex The column dataIndex
4195      * @return {Object|Boolean} the column or false if not found
4196      */
4197     getColumnByDataIndex: function(dataIndex){
4198         var index = this.findColumnIndex(dataIndex);
4199         return index > -1 ? this.config[index] : false;
4200     },
4201     
4202     /**
4203      * Returns the index for a specified column id.
4204      * @param {String} id The column id
4205      * @return {Number} the index, or -1 if not found
4206      */
4207     getIndexById : function(id){
4208         for(var i = 0, len = this.config.length; i < len; i++){
4209             if(this.config[i].id == id){
4210                 return i;
4211             }
4212         }
4213         return -1;
4214     },
4215     
4216     /**
4217      * Returns the index for a specified column dataIndex.
4218      * @param {String} dataIndex The column dataIndex
4219      * @return {Number} the index, or -1 if not found
4220      */
4221     
4222     findColumnIndex : function(dataIndex){
4223         for(var i = 0, len = this.config.length; i < len; i++){
4224             if(this.config[i].dataIndex == dataIndex){
4225                 return i;
4226             }
4227         }
4228         return -1;
4229     },
4230     
4231     
4232     moveColumn : function(oldIndex, newIndex){
4233         var c = this.config[oldIndex];
4234         this.config.splice(oldIndex, 1);
4235         this.config.splice(newIndex, 0, c);
4236         this.dataMap = null;
4237         this.fireEvent("columnmoved", this, oldIndex, newIndex);
4238     },
4239
4240     isLocked : function(colIndex){
4241         return this.config[colIndex].locked === true;
4242     },
4243
4244     setLocked : function(colIndex, value, suppressEvent){
4245         if(this.isLocked(colIndex) == value){
4246             return;
4247         }
4248         this.config[colIndex].locked = value;
4249         if(!suppressEvent){
4250             this.fireEvent("columnlockchange", this, colIndex, value);
4251         }
4252     },
4253
4254     getTotalLockedWidth : function(){
4255         var totalWidth = 0;
4256         for(var i = 0; i < this.config.length; i++){
4257             if(this.isLocked(i) && !this.isHidden(i)){
4258                 this.totalWidth += this.getColumnWidth(i);
4259             }
4260         }
4261         return totalWidth;
4262     },
4263
4264     getLockedCount : function(){
4265         for(var i = 0, len = this.config.length; i < len; i++){
4266             if(!this.isLocked(i)){
4267                 return i;
4268             }
4269         }
4270     },
4271
4272     /**
4273      * Returns the number of columns.
4274      * @return {Number}
4275      */
4276     getColumnCount : function(visibleOnly){
4277         if(visibleOnly === true){
4278             var c = 0;
4279             for(var i = 0, len = this.config.length; i < len; i++){
4280                 if(!this.isHidden(i)){
4281                     c++;
4282                 }
4283             }
4284             return c;
4285         }
4286         return this.config.length;
4287     },
4288
4289     /**
4290      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
4291      * @param {Function} fn
4292      * @param {Object} scope (optional)
4293      * @return {Array} result
4294      */
4295     getColumnsBy : function(fn, scope){
4296         var r = [];
4297         for(var i = 0, len = this.config.length; i < len; i++){
4298             var c = this.config[i];
4299             if(fn.call(scope||this, c, i) === true){
4300                 r[r.length] = c;
4301             }
4302         }
4303         return r;
4304     },
4305
4306     /**
4307      * Returns true if the specified column is sortable.
4308      * @param {Number} col The column index
4309      * @return {Boolean}
4310      */
4311     isSortable : function(col){
4312         if(typeof this.config[col].sortable == "undefined"){
4313             return this.defaultSortable;
4314         }
4315         return this.config[col].sortable;
4316     },
4317
4318     /**
4319      * Returns the rendering (formatting) function defined for the column.
4320      * @param {Number} col The column index.
4321      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
4322      */
4323     getRenderer : function(col){
4324         if(!this.config[col].renderer){
4325             return Roo.grid.ColumnModel.defaultRenderer;
4326         }
4327         return this.config[col].renderer;
4328     },
4329
4330     /**
4331      * Sets the rendering (formatting) function for a column.
4332      * @param {Number} col The column index
4333      * @param {Function} fn The function to use to process the cell's raw data
4334      * to return HTML markup for the grid view. The render function is called with
4335      * the following parameters:<ul>
4336      * <li>Data value.</li>
4337      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
4338      * <li>css A CSS style string to apply to the table cell.</li>
4339      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
4340      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
4341      * <li>Row index</li>
4342      * <li>Column index</li>
4343      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
4344      */
4345     setRenderer : function(col, fn){
4346         this.config[col].renderer = fn;
4347     },
4348
4349     /**
4350      * Returns the width for the specified column.
4351      * @param {Number} col The column index
4352      * @return {Number}
4353      */
4354     getColumnWidth : function(col){
4355         return this.config[col].width * 1 || this.defaultWidth;
4356     },
4357
4358     /**
4359      * Sets the width for a column.
4360      * @param {Number} col The column index
4361      * @param {Number} width The new width
4362      */
4363     setColumnWidth : function(col, width, suppressEvent){
4364         this.config[col].width = width;
4365         this.totalWidth = null;
4366         if(!suppressEvent){
4367              this.fireEvent("widthchange", this, col, width);
4368         }
4369     },
4370
4371     /**
4372      * Returns the total width of all columns.
4373      * @param {Boolean} includeHidden True to include hidden column widths
4374      * @return {Number}
4375      */
4376     getTotalWidth : function(includeHidden){
4377         if(!this.totalWidth){
4378             this.totalWidth = 0;
4379             for(var i = 0, len = this.config.length; i < len; i++){
4380                 if(includeHidden || !this.isHidden(i)){
4381                     this.totalWidth += this.getColumnWidth(i);
4382                 }
4383             }
4384         }
4385         return this.totalWidth;
4386     },
4387
4388     /**
4389      * Returns the header for the specified column.
4390      * @param {Number} col The column index
4391      * @return {String}
4392      */
4393     getColumnHeader : function(col){
4394         return this.config[col].header;
4395     },
4396
4397     /**
4398      * Sets the header for a column.
4399      * @param {Number} col The column index
4400      * @param {String} header The new header
4401      */
4402     setColumnHeader : function(col, header){
4403         this.config[col].header = header;
4404         this.fireEvent("headerchange", this, col, header);
4405     },
4406
4407     /**
4408      * Returns the tooltip for the specified column.
4409      * @param {Number} col The column index
4410      * @return {String}
4411      */
4412     getColumnTooltip : function(col){
4413             return this.config[col].tooltip;
4414     },
4415     /**
4416      * Sets the tooltip for a column.
4417      * @param {Number} col The column index
4418      * @param {String} tooltip The new tooltip
4419      */
4420     setColumnTooltip : function(col, tooltip){
4421             this.config[col].tooltip = tooltip;
4422     },
4423
4424     /**
4425      * Returns the dataIndex for the specified column.
4426      * @param {Number} col The column index
4427      * @return {Number}
4428      */
4429     getDataIndex : function(col){
4430         return this.config[col].dataIndex;
4431     },
4432
4433     /**
4434      * Sets the dataIndex for a column.
4435      * @param {Number} col The column index
4436      * @param {Number} dataIndex The new dataIndex
4437      */
4438     setDataIndex : function(col, dataIndex){
4439         this.config[col].dataIndex = dataIndex;
4440     },
4441
4442     
4443     
4444     /**
4445      * Returns true if the cell is editable.
4446      * @param {Number} colIndex The column index
4447      * @param {Number} rowIndex The row index
4448      * @return {Boolean}
4449      */
4450     isCellEditable : function(colIndex, rowIndex){
4451         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
4452     },
4453
4454     /**
4455      * Returns the editor defined for the cell/column.
4456      * return false or null to disable editing.
4457      * @param {Number} colIndex The column index
4458      * @param {Number} rowIndex The row index
4459      * @return {Object}
4460      */
4461     getCellEditor : function(colIndex, rowIndex){
4462         return this.config[colIndex].editor;
4463     },
4464
4465     /**
4466      * Sets if a column is editable.
4467      * @param {Number} col The column index
4468      * @param {Boolean} editable True if the column is editable
4469      */
4470     setEditable : function(col, editable){
4471         this.config[col].editable = editable;
4472     },
4473
4474
4475     /**
4476      * Returns true if the column is hidden.
4477      * @param {Number} colIndex The column index
4478      * @return {Boolean}
4479      */
4480     isHidden : function(colIndex){
4481         return this.config[colIndex].hidden;
4482     },
4483
4484
4485     /**
4486      * Returns true if the column width cannot be changed
4487      */
4488     isFixed : function(colIndex){
4489         return this.config[colIndex].fixed;
4490     },
4491
4492     /**
4493      * Returns true if the column can be resized
4494      * @return {Boolean}
4495      */
4496     isResizable : function(colIndex){
4497         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
4498     },
4499     /**
4500      * Sets if a column is hidden.
4501      * @param {Number} colIndex The column index
4502      * @param {Boolean} hidden True if the column is hidden
4503      */
4504     setHidden : function(colIndex, hidden){
4505         this.config[colIndex].hidden = hidden;
4506         this.totalWidth = null;
4507         this.fireEvent("hiddenchange", this, colIndex, hidden);
4508     },
4509
4510     /**
4511      * Sets the editor for a column.
4512      * @param {Number} col The column index
4513      * @param {Object} editor The editor object
4514      */
4515     setEditor : function(col, editor){
4516         this.config[col].editor = editor;
4517     }
4518 });
4519
4520 Roo.grid.ColumnModel.defaultRenderer = function(value){
4521         if(typeof value == "string" && value.length < 1){
4522             return "&#160;";
4523         }
4524         return value;
4525 };
4526
4527 // Alias for backwards compatibility
4528 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
4529 /*
4530  * Based on:
4531  * Ext JS Library 1.1.1
4532  * Copyright(c) 2006-2007, Ext JS, LLC.
4533  *
4534  * Originally Released Under LGPL - original licence link has changed is not relivant.
4535  *
4536  * Fork - LGPL
4537  * <script type="text/javascript">
4538  */
4539  
4540 /**
4541  * @class Roo.LoadMask
4542  * A simple utility class for generically masking elements while loading data.  If the element being masked has
4543  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
4544  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
4545  * element's UpdateManager load indicator and will be destroyed after the initial load.
4546  * @constructor
4547  * Create a new LoadMask
4548  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
4549  * @param {Object} config The config object
4550  */
4551 Roo.LoadMask = function(el, config){
4552     this.el = Roo.get(el);
4553     Roo.apply(this, config);
4554     if(this.store){
4555         this.store.on('beforeload', this.onBeforeLoad, this);
4556         this.store.on('load', this.onLoad, this);
4557         this.store.on('loadexception', this.onLoadException, this);
4558         this.removeMask = false;
4559     }else{
4560         var um = this.el.getUpdateManager();
4561         um.showLoadIndicator = false; // disable the default indicator
4562         um.on('beforeupdate', this.onBeforeLoad, this);
4563         um.on('update', this.onLoad, this);
4564         um.on('failure', this.onLoad, this);
4565         this.removeMask = true;
4566     }
4567 };
4568
4569 Roo.LoadMask.prototype = {
4570     /**
4571      * @cfg {Boolean} removeMask
4572      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
4573      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
4574      */
4575     /**
4576      * @cfg {String} msg
4577      * The text to display in a centered loading message box (defaults to 'Loading...')
4578      */
4579     msg : 'Loading...',
4580     /**
4581      * @cfg {String} msgCls
4582      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
4583      */
4584     msgCls : 'x-mask-loading',
4585
4586     /**
4587      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
4588      * @type Boolean
4589      */
4590     disabled: false,
4591
4592     /**
4593      * Disables the mask to prevent it from being displayed
4594      */
4595     disable : function(){
4596        this.disabled = true;
4597     },
4598
4599     /**
4600      * Enables the mask so that it can be displayed
4601      */
4602     enable : function(){
4603         this.disabled = false;
4604     },
4605     
4606     onLoadException : function()
4607     {
4608         Roo.log(arguments);
4609         
4610         if (typeof(arguments[3]) != 'undefined') {
4611             Roo.MessageBox.alert("Error loading",arguments[3]);
4612         } 
4613         /*
4614         try {
4615             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
4616                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
4617             }   
4618         } catch(e) {
4619             
4620         }
4621         */
4622     
4623         
4624         
4625         this.el.unmask(this.removeMask);
4626     },
4627     // private
4628     onLoad : function()
4629     {
4630         this.el.unmask(this.removeMask);
4631     },
4632
4633     // private
4634     onBeforeLoad : function(){
4635         if(!this.disabled){
4636             this.el.mask(this.msg, this.msgCls);
4637         }
4638     },
4639
4640     // private
4641     destroy : function(){
4642         if(this.store){
4643             this.store.un('beforeload', this.onBeforeLoad, this);
4644             this.store.un('load', this.onLoad, this);
4645             this.store.un('loadexception', this.onLoadException, this);
4646         }else{
4647             var um = this.el.getUpdateManager();
4648             um.un('beforeupdate', this.onBeforeLoad, this);
4649             um.un('update', this.onLoad, this);
4650             um.un('failure', this.onLoad, this);
4651         }
4652     }
4653 };/*
4654  * - LGPL
4655  *
4656  * table
4657  * 
4658  */
4659
4660 /**
4661  * @class Roo.bootstrap.Table
4662  * @extends Roo.bootstrap.Component
4663  * Bootstrap Table class
4664  * @cfg {String} cls table class
4665  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
4666  * @cfg {String} bgcolor Specifies the background color for a table
4667  * @cfg {Number} border Specifies whether the table cells should have borders or not
4668  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
4669  * @cfg {Number} cellspacing Specifies the space between cells
4670  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
4671  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
4672  * @cfg {String} sortable Specifies that the table should be sortable
4673  * @cfg {String} summary Specifies a summary of the content of a table
4674  * @cfg {Number} width Specifies the width of a table
4675  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
4676  * 
4677  * @cfg {boolean} striped Should the rows be alternative striped
4678  * @cfg {boolean} bordered Add borders to the table
4679  * @cfg {boolean} hover Add hover highlighting
4680  * @cfg {boolean} condensed Format condensed
4681  * @cfg {boolean} responsive Format condensed
4682  * @cfg {Boolean} loadMask (true|false) default false
4683  * @cfg {Boolean} tfoot (true|false) generate tfoot, default true
4684  * @cfg {Boolean} thead (true|false) generate thead, default true
4685  * @cfg {Boolean} RowSelection (true|false) default false
4686  * @cfg {Boolean} CellSelection (true|false) default false
4687  *
4688  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
4689  
4690  * 
4691  * @constructor
4692  * Create a new Table
4693  * @param {Object} config The config object
4694  */
4695
4696 Roo.bootstrap.Table = function(config){
4697     Roo.bootstrap.Table.superclass.constructor.call(this, config);
4698     
4699     if (this.sm) {
4700         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
4701         this.sm = this.selModel;
4702         this.sm.xmodule = this.xmodule || false;
4703     }
4704     if (this.cm && typeof(this.cm.config) == 'undefined') {
4705         this.colModel = new Roo.grid.ColumnModel(this.cm);
4706         this.cm = this.colModel;
4707         this.cm.xmodule = this.xmodule || false;
4708     }
4709     if (this.store) {
4710         this.store= Roo.factory(this.store, Roo.data);
4711         this.ds = this.store;
4712         this.ds.xmodule = this.xmodule || false;
4713          
4714     }
4715     if (this.footer && this.store) {
4716         this.footer.dataSource = this.ds;
4717         this.footer = Roo.factory(this.footer);
4718     }
4719     
4720     /** @private */
4721     this.addEvents({
4722         /**
4723          * @event cellclick
4724          * Fires when a cell is clicked
4725          * @param {Roo.bootstrap.Table} this
4726          * @param {Roo.Element} el
4727          * @param {Number} rowIndex
4728          * @param {Number} columnIndex
4729          * @param {Roo.EventObject} e
4730          */
4731         "cellclick" : true,
4732         /**
4733          * @event celldblclick
4734          * Fires when a cell is double clicked
4735          * @param {Roo.bootstrap.Table} this
4736          * @param {Roo.Element} el
4737          * @param {Number} rowIndex
4738          * @param {Number} columnIndex
4739          * @param {Roo.EventObject} e
4740          */
4741         "celldblclick" : true,
4742         /**
4743          * @event rowclick
4744          * Fires when a row is clicked
4745          * @param {Roo.bootstrap.Table} this
4746          * @param {Roo.Element} el
4747          * @param {Number} rowIndex
4748          * @param {Roo.EventObject} e
4749          */
4750         "rowclick" : true,
4751         /**
4752          * @event rowdblclick
4753          * Fires when a row is double clicked
4754          * @param {Roo.bootstrap.Table} this
4755          * @param {Roo.Element} el
4756          * @param {Number} rowIndex
4757          * @param {Roo.EventObject} e
4758          */
4759         "rowdblclick" : true,
4760         /**
4761          * @event mouseover
4762          * Fires when a mouseover occur
4763          * @param {Roo.bootstrap.Table} this
4764          * @param {Roo.Element} el
4765          * @param {Number} rowIndex
4766          * @param {Number} columnIndex
4767          * @param {Roo.EventObject} e
4768          */
4769         "mouseover" : true,
4770         /**
4771          * @event mouseout
4772          * Fires when a mouseout occur
4773          * @param {Roo.bootstrap.Table} this
4774          * @param {Roo.Element} el
4775          * @param {Number} rowIndex
4776          * @param {Number} columnIndex
4777          * @param {Roo.EventObject} e
4778          */
4779         "mouseout" : true,
4780         /**
4781          * @event rowclass
4782          * Fires when a row is rendered, so you can change add a style to it.
4783          * @param {Roo.bootstrap.Table} this
4784          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
4785          */
4786         'rowclass' : true
4787         
4788     });
4789 };
4790
4791 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
4792     
4793     cls: false,
4794     align: false,
4795     bgcolor: false,
4796     border: false,
4797     cellpadding: false,
4798     cellspacing: false,
4799     frame: false,
4800     rules: false,
4801     sortable: false,
4802     summary: false,
4803     width: false,
4804     striped : false,
4805     bordered: false,
4806     hover:  false,
4807     condensed : false,
4808     responsive : false,
4809     sm : false,
4810     cm : false,
4811     store : false,
4812     loadMask : false,
4813     tfoot : true,
4814     thead : true,
4815     RowSelection : false,
4816     CellSelection : false,
4817     layout : false,
4818     
4819     // Roo.Element - the tbody
4820     mainBody: false, 
4821     
4822     getAutoCreate : function(){
4823         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
4824         
4825         cfg = {
4826             tag: 'table',
4827             cls : 'table',
4828             cn : []
4829         }
4830             
4831         if (this.striped) {
4832             cfg.cls += ' table-striped';
4833         }
4834         
4835         if (this.hover) {
4836             cfg.cls += ' table-hover';
4837         }
4838         if (this.bordered) {
4839             cfg.cls += ' table-bordered';
4840         }
4841         if (this.condensed) {
4842             cfg.cls += ' table-condensed';
4843         }
4844         if (this.responsive) {
4845             cfg.cls += ' table-responsive';
4846         }
4847         
4848         if (this.cls) {
4849             cfg.cls+=  ' ' +this.cls;
4850         }
4851         
4852         // this lot should be simplifed...
4853         
4854         if (this.align) {
4855             cfg.align=this.align;
4856         }
4857         if (this.bgcolor) {
4858             cfg.bgcolor=this.bgcolor;
4859         }
4860         if (this.border) {
4861             cfg.border=this.border;
4862         }
4863         if (this.cellpadding) {
4864             cfg.cellpadding=this.cellpadding;
4865         }
4866         if (this.cellspacing) {
4867             cfg.cellspacing=this.cellspacing;
4868         }
4869         if (this.frame) {
4870             cfg.frame=this.frame;
4871         }
4872         if (this.rules) {
4873             cfg.rules=this.rules;
4874         }
4875         if (this.sortable) {
4876             cfg.sortable=this.sortable;
4877         }
4878         if (this.summary) {
4879             cfg.summary=this.summary;
4880         }
4881         if (this.width) {
4882             cfg.width=this.width;
4883         }
4884         if (this.layout) {
4885             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
4886         }
4887         
4888         if(this.store || this.cm){
4889             if(this.thead){
4890                 cfg.cn.push(this.renderHeader());
4891             }
4892             
4893             cfg.cn.push(this.renderBody());
4894             
4895             if(this.tfoot){
4896                 cfg.cn.push(this.renderFooter());
4897             }
4898             
4899             cfg.cls+=  ' TableGrid';
4900         }
4901         
4902         return { cn : [ cfg ] };
4903     },
4904     
4905     initEvents : function()
4906     {   
4907         if(!this.store || !this.cm){
4908             return;
4909         }
4910         
4911         //Roo.log('initEvents with ds!!!!');
4912         
4913         this.mainBody = this.el.select('tbody', true).first();
4914         
4915         
4916         var _this = this;
4917         
4918         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
4919             e.on('click', _this.sort, _this);
4920         });
4921         
4922         this.el.on("click", this.onClick, this);
4923         this.el.on("dblclick", this.onDblClick, this);
4924         
4925         this.parent().el.setStyle('position', 'relative');
4926         if (this.footer) {
4927             this.footer.parentId = this.id;
4928             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
4929         }
4930         
4931         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
4932         
4933         this.store.on('load', this.onLoad, this);
4934         this.store.on('beforeload', this.onBeforeLoad, this);
4935         this.store.on('update', this.onUpdate, this);
4936         
4937     },
4938     
4939     onMouseover : function(e, el)
4940     {
4941         var cell = Roo.get(el);
4942         
4943         if(!cell){
4944             return;
4945         }
4946         
4947         if(e.getTarget().nodeName.toLowerCase() != 'td'){
4948             cell = cell.findParent('td', false, true);
4949         }
4950         
4951         var row = cell.findParent('tr', false, true);
4952         var cellIndex = cell.dom.cellIndex;
4953         var rowIndex = row.dom.rowIndex - 1; // start from 0
4954         
4955         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
4956         
4957     },
4958     
4959     onMouseout : function(e, el)
4960     {
4961         var cell = Roo.get(el);
4962         
4963         if(!cell){
4964             return;
4965         }
4966         
4967         if(e.getTarget().nodeName.toLowerCase() != 'td'){
4968             cell = cell.findParent('td', false, true);
4969         }
4970         
4971         var row = cell.findParent('tr', false, true);
4972         var cellIndex = cell.dom.cellIndex;
4973         var rowIndex = row.dom.rowIndex - 1; // start from 0
4974         
4975         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
4976         
4977     },
4978     
4979     onClick : function(e, el)
4980     {
4981         var cell = Roo.get(el);
4982         
4983         if(!cell || (!this.CellSelection && !this.RowSelection)){
4984             return;
4985         }
4986         
4987         
4988         if(e.getTarget().nodeName.toLowerCase() != 'td'){
4989             cell = cell.findParent('td', false, true);
4990         }
4991         
4992         var row = cell.findParent('tr', false, true);
4993         var cellIndex = cell.dom.cellIndex;
4994         var rowIndex = row.dom.rowIndex - 1;
4995         
4996         if(this.CellSelection){
4997             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
4998         }
4999         
5000         if(this.RowSelection){
5001             this.fireEvent('rowclick', this, row, rowIndex, e);
5002         }
5003         
5004         
5005     },
5006     
5007     onDblClick : function(e,el)
5008     {
5009         var cell = Roo.get(el);
5010         
5011         if(!cell || (!this.CellSelection && !this.RowSelection)){
5012             return;
5013         }
5014         
5015         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5016             cell = cell.findParent('td', false, true);
5017         }
5018         
5019         var row = cell.findParent('tr', false, true);
5020         var cellIndex = cell.dom.cellIndex;
5021         var rowIndex = row.dom.rowIndex - 1;
5022         
5023         if(this.CellSelection){
5024             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5025         }
5026         
5027         if(this.RowSelection){
5028             this.fireEvent('rowdblclick', this, row, rowIndex, e);
5029         }
5030     },
5031     
5032     sort : function(e,el)
5033     {
5034         var col = Roo.get(el)
5035         
5036         if(!col.hasClass('sortable')){
5037             return;
5038         }
5039         
5040         var sort = col.attr('sort');
5041         var dir = 'ASC';
5042         
5043         if(col.hasClass('glyphicon-arrow-up')){
5044             dir = 'DESC';
5045         }
5046         
5047         this.store.sortInfo = {field : sort, direction : dir};
5048         
5049         if (this.footer) {
5050             Roo.log("calling footer first");
5051             this.footer.onClick('first');
5052         } else {
5053         
5054             this.store.load({ params : { start : 0 } });
5055         }
5056     },
5057     
5058     renderHeader : function()
5059     {
5060         var header = {
5061             tag: 'thead',
5062             cn : []
5063         };
5064         
5065         var cm = this.cm;
5066         
5067         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5068             
5069             var config = cm.config[i];
5070                     
5071             var c = {
5072                 tag: 'th',
5073                 style : '',
5074                 html: cm.getColumnHeader(i)
5075             };
5076             
5077             if(typeof(config.hidden) != 'undefined' && config.hidden){
5078                 c.style += ' display:none;';
5079             }
5080             
5081             if(typeof(config.dataIndex) != 'undefined'){
5082                 c.sort = config.dataIndex;
5083             }
5084             
5085             if(typeof(config.sortable) != 'undefined' && config.sortable){
5086                 c.cls = 'sortable';
5087             }
5088             
5089             if(typeof(config.align) != 'undefined' && config.align.length){
5090                 c.style += ' text-align:' + config.align + ';';
5091             }
5092             
5093             if(typeof(config.width) != 'undefined'){
5094                 c.style += ' width:' + config.width + 'px;';
5095             }
5096             
5097             header.cn.push(c)
5098         }
5099         
5100         return header;
5101     },
5102     
5103     renderBody : function()
5104     {
5105         var body = {
5106             tag: 'tbody',
5107             cn : [
5108                 {
5109                     tag: 'tr',
5110                     cn : [
5111                         {
5112                             tag : 'td',
5113                             colspan :  this.cm.getColumnCount()
5114                         }
5115                     ]
5116                 }
5117             ]
5118         };
5119         
5120         return body;
5121     },
5122     
5123     renderFooter : function()
5124     {
5125         var footer = {
5126             tag: 'tfoot',
5127             cn : [
5128                 {
5129                     tag: 'tr',
5130                     cn : [
5131                         {
5132                             tag : 'td',
5133                             colspan :  this.cm.getColumnCount()
5134                         }
5135                     ]
5136                 }
5137             ]
5138         };
5139         
5140         return footer;
5141     },
5142     
5143     
5144     
5145     onLoad : function()
5146     {
5147         Roo.log('ds onload');
5148         this.clear();
5149         
5150         var _this = this;
5151         var cm = this.cm;
5152         var ds = this.store;
5153         
5154         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5155             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
5156             
5157             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
5158                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
5159             }
5160             
5161             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
5162                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
5163             }
5164         });
5165         
5166         var tbody =  this.mainBody;
5167               
5168         if(ds.getCount() > 0){
5169             ds.data.each(function(d,rowIndex){
5170                 var row =  this.renderRow(cm, ds, rowIndex);
5171                 
5172                 tbody.createChild(row);
5173                 
5174                 var _this = this;
5175                 
5176                 if(row.cellObjects.length){
5177                     Roo.each(row.cellObjects, function(r){
5178                         _this.renderCellObject(r);
5179                     })
5180                 }
5181                 
5182             }, this);
5183         }
5184         
5185         Roo.each(this.el.select('tbody td', true).elements, function(e){
5186             e.on('mouseover', _this.onMouseover, _this);
5187         });
5188         
5189         Roo.each(this.el.select('tbody td', true).elements, function(e){
5190             e.on('mouseout', _this.onMouseout, _this);
5191         });
5192
5193         //if(this.loadMask){
5194         //    this.maskEl.hide();
5195         //}
5196     },
5197     
5198     
5199     onUpdate : function(ds,record)
5200     {
5201         this.refreshRow(record);
5202     },
5203     onRemove : function(ds, record, index, isUpdate){
5204         if(isUpdate !== true){
5205             this.fireEvent("beforerowremoved", this, index, record);
5206         }
5207         var bt = this.mainBody.dom;
5208         if(bt.rows[index]){
5209             bt.removeChild(bt.rows[index]);
5210         }
5211         
5212         if(isUpdate !== true){
5213             //this.stripeRows(index);
5214             //this.syncRowHeights(index, index);
5215             //this.layout();
5216             this.fireEvent("rowremoved", this, index, record);
5217         }
5218     },
5219     
5220     
5221     refreshRow : function(record){
5222         var ds = this.store, index;
5223         if(typeof record == 'number'){
5224             index = record;
5225             record = ds.getAt(index);
5226         }else{
5227             index = ds.indexOf(record);
5228         }
5229         this.insertRow(ds, index, true);
5230         this.onRemove(ds, record, index+1, true);
5231         //this.syncRowHeights(index, index);
5232         //this.layout();
5233         this.fireEvent("rowupdated", this, index, record);
5234     },
5235     
5236     insertRow : function(dm, rowIndex, isUpdate){
5237         
5238         if(!isUpdate){
5239             this.fireEvent("beforerowsinserted", this, rowIndex);
5240         }
5241             //var s = this.getScrollState();
5242         var row = this.renderRow(this.cm, this.store, rowIndex);
5243         // insert before rowIndex..
5244         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
5245         
5246         var _this = this;
5247                 
5248         if(row.cellObjects.length){
5249             Roo.each(row.cellObjects, function(r){
5250                 _this.renderCellObject(r);
5251             })
5252         }
5253             
5254         if(!isUpdate){
5255             this.fireEvent("rowsinserted", this, rowIndex);
5256             //this.syncRowHeights(firstRow, lastRow);
5257             //this.stripeRows(firstRow);
5258             //this.layout();
5259         }
5260         
5261     },
5262     
5263     
5264     getRowDom : function(rowIndex)
5265     {
5266         // not sure if I need to check this.. but let's do it anyway..
5267         return (this.mainBody.dom.rows && (rowIndex-1) < this.mainBody.dom.rows.length ) ?
5268                 this.mainBody.dom.rows[rowIndex] : false
5269     },
5270     // returns the object tree for a tr..
5271   
5272     
5273     renderRow : function(cm, ds, rowIndex) {
5274         
5275         var d = ds.getAt(rowIndex);
5276         
5277         var row = {
5278             tag : 'tr',
5279             cn : []
5280         };
5281             
5282         var cellObjects = [];
5283         
5284         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5285             var config = cm.config[i];
5286             
5287             var renderer = cm.getRenderer(i);
5288             var value = '';
5289             var id = false;
5290             
5291             if(typeof(renderer) !== 'undefined'){
5292                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
5293             }
5294             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
5295             // and are rendered into the cells after the row is rendered - using the id for the element.
5296             
5297             if(typeof(value) === 'object'){
5298                 id = Roo.id();
5299                 cellObjects.push({
5300                     container : id,
5301                     cfg : value 
5302                 })
5303             }
5304             
5305             var rowcfg = {
5306                 record: d,
5307                 rowIndex : rowIndex,
5308                 colIndex : i,
5309                 rowClass : ''
5310             }
5311
5312             this.fireEvent('rowclass', this, rowcfg);
5313             
5314             var td = {
5315                 tag: 'td',
5316                 cls : rowcfg.rowClass,
5317                 style: '',
5318                 html: (typeof(value) === 'object') ? '' : value
5319             };
5320             
5321             if (id) {
5322                 td.id = id;
5323             }
5324             
5325             if(typeof(config.hidden) != 'undefined' && config.hidden){
5326                 td.style += ' display:none;';
5327             }
5328             
5329             if(typeof(config.align) != 'undefined' && config.align.length){
5330                 td.style += ' text-align:' + config.align + ';';
5331             }
5332             
5333             if(typeof(config.width) != 'undefined'){
5334                 td.style += ' width:' +  config.width + 'px;';
5335             }
5336              
5337             row.cn.push(td);
5338            
5339         }
5340         
5341         row.cellObjects = cellObjects;
5342         
5343         return row;
5344           
5345     },
5346     
5347     
5348     
5349     onBeforeLoad : function()
5350     {
5351         //Roo.log('ds onBeforeLoad');
5352         
5353         //this.clear();
5354         
5355         //if(this.loadMask){
5356         //    this.maskEl.show();
5357         //}
5358     },
5359     
5360     clear : function()
5361     {
5362         this.el.select('tbody', true).first().dom.innerHTML = '';
5363     },
5364     
5365     getSelectionModel : function(){
5366         if(!this.selModel){
5367             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
5368         }
5369         return this.selModel;
5370     },
5371     /*
5372      * Render the Roo.bootstrap object from renderder
5373      */
5374     renderCellObject : function(r)
5375     {
5376         var _this = this;
5377         
5378         var t = r.cfg.render(r.container);
5379         
5380         if(r.cfg.cn){
5381             Roo.each(r.cfg.cn, function(c){
5382                 var child = {
5383                     container: t.getChildContainer(),
5384                     cfg: c
5385                 }
5386                 _this.renderCellObject(child);
5387             })
5388         }
5389     }
5390    
5391 });
5392
5393  
5394
5395  /*
5396  * - LGPL
5397  *
5398  * table cell
5399  * 
5400  */
5401
5402 /**
5403  * @class Roo.bootstrap.TableCell
5404  * @extends Roo.bootstrap.Component
5405  * Bootstrap TableCell class
5406  * @cfg {String} html cell contain text
5407  * @cfg {String} cls cell class
5408  * @cfg {String} tag cell tag (td|th) default td
5409  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
5410  * @cfg {String} align Aligns the content in a cell
5411  * @cfg {String} axis Categorizes cells
5412  * @cfg {String} bgcolor Specifies the background color of a cell
5413  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5414  * @cfg {Number} colspan Specifies the number of columns a cell should span
5415  * @cfg {String} headers Specifies one or more header cells a cell is related to
5416  * @cfg {Number} height Sets the height of a cell
5417  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
5418  * @cfg {Number} rowspan Sets the number of rows a cell should span
5419  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
5420  * @cfg {String} valign Vertical aligns the content in a cell
5421  * @cfg {Number} width Specifies the width of a cell
5422  * 
5423  * @constructor
5424  * Create a new TableCell
5425  * @param {Object} config The config object
5426  */
5427
5428 Roo.bootstrap.TableCell = function(config){
5429     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
5430 };
5431
5432 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
5433     
5434     html: false,
5435     cls: false,
5436     tag: false,
5437     abbr: false,
5438     align: false,
5439     axis: false,
5440     bgcolor: false,
5441     charoff: false,
5442     colspan: false,
5443     headers: false,
5444     height: false,
5445     nowrap: false,
5446     rowspan: false,
5447     scope: false,
5448     valign: false,
5449     width: false,
5450     
5451     
5452     getAutoCreate : function(){
5453         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
5454         
5455         cfg = {
5456             tag: 'td'
5457         }
5458         
5459         if(this.tag){
5460             cfg.tag = this.tag;
5461         }
5462         
5463         if (this.html) {
5464             cfg.html=this.html
5465         }
5466         if (this.cls) {
5467             cfg.cls=this.cls
5468         }
5469         if (this.abbr) {
5470             cfg.abbr=this.abbr
5471         }
5472         if (this.align) {
5473             cfg.align=this.align
5474         }
5475         if (this.axis) {
5476             cfg.axis=this.axis
5477         }
5478         if (this.bgcolor) {
5479             cfg.bgcolor=this.bgcolor
5480         }
5481         if (this.charoff) {
5482             cfg.charoff=this.charoff
5483         }
5484         if (this.colspan) {
5485             cfg.colspan=this.colspan
5486         }
5487         if (this.headers) {
5488             cfg.headers=this.headers
5489         }
5490         if (this.height) {
5491             cfg.height=this.height
5492         }
5493         if (this.nowrap) {
5494             cfg.nowrap=this.nowrap
5495         }
5496         if (this.rowspan) {
5497             cfg.rowspan=this.rowspan
5498         }
5499         if (this.scope) {
5500             cfg.scope=this.scope
5501         }
5502         if (this.valign) {
5503             cfg.valign=this.valign
5504         }
5505         if (this.width) {
5506             cfg.width=this.width
5507         }
5508         
5509         
5510         return cfg;
5511     }
5512    
5513 });
5514
5515  
5516
5517  /*
5518  * - LGPL
5519  *
5520  * table row
5521  * 
5522  */
5523
5524 /**
5525  * @class Roo.bootstrap.TableRow
5526  * @extends Roo.bootstrap.Component
5527  * Bootstrap TableRow class
5528  * @cfg {String} cls row class
5529  * @cfg {String} align Aligns the content in a table row
5530  * @cfg {String} bgcolor Specifies a background color for a table row
5531  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5532  * @cfg {String} valign Vertical aligns the content in a table row
5533  * 
5534  * @constructor
5535  * Create a new TableRow
5536  * @param {Object} config The config object
5537  */
5538
5539 Roo.bootstrap.TableRow = function(config){
5540     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
5541 };
5542
5543 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
5544     
5545     cls: false,
5546     align: false,
5547     bgcolor: false,
5548     charoff: false,
5549     valign: false,
5550     
5551     getAutoCreate : function(){
5552         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
5553         
5554         cfg = {
5555             tag: 'tr'
5556         }
5557             
5558         if(this.cls){
5559             cfg.cls = this.cls;
5560         }
5561         if(this.align){
5562             cfg.align = this.align;
5563         }
5564         if(this.bgcolor){
5565             cfg.bgcolor = this.bgcolor;
5566         }
5567         if(this.charoff){
5568             cfg.charoff = this.charoff;
5569         }
5570         if(this.valign){
5571             cfg.valign = this.valign;
5572         }
5573         
5574         return cfg;
5575     }
5576    
5577 });
5578
5579  
5580
5581  /*
5582  * - LGPL
5583  *
5584  * table body
5585  * 
5586  */
5587
5588 /**
5589  * @class Roo.bootstrap.TableBody
5590  * @extends Roo.bootstrap.Component
5591  * Bootstrap TableBody class
5592  * @cfg {String} cls element class
5593  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
5594  * @cfg {String} align Aligns the content inside the element
5595  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
5596  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
5597  * 
5598  * @constructor
5599  * Create a new TableBody
5600  * @param {Object} config The config object
5601  */
5602
5603 Roo.bootstrap.TableBody = function(config){
5604     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
5605 };
5606
5607 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
5608     
5609     cls: false,
5610     tag: false,
5611     align: false,
5612     charoff: false,
5613     valign: false,
5614     
5615     getAutoCreate : function(){
5616         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
5617         
5618         cfg = {
5619             tag: 'tbody'
5620         }
5621             
5622         if (this.cls) {
5623             cfg.cls=this.cls
5624         }
5625         if(this.tag){
5626             cfg.tag = this.tag;
5627         }
5628         
5629         if(this.align){
5630             cfg.align = this.align;
5631         }
5632         if(this.charoff){
5633             cfg.charoff = this.charoff;
5634         }
5635         if(this.valign){
5636             cfg.valign = this.valign;
5637         }
5638         
5639         return cfg;
5640     }
5641     
5642     
5643 //    initEvents : function()
5644 //    {
5645 //        
5646 //        if(!this.store){
5647 //            return;
5648 //        }
5649 //        
5650 //        this.store = Roo.factory(this.store, Roo.data);
5651 //        this.store.on('load', this.onLoad, this);
5652 //        
5653 //        this.store.load();
5654 //        
5655 //    },
5656 //    
5657 //    onLoad: function () 
5658 //    {   
5659 //        this.fireEvent('load', this);
5660 //    }
5661 //    
5662 //   
5663 });
5664
5665  
5666
5667  /*
5668  * Based on:
5669  * Ext JS Library 1.1.1
5670  * Copyright(c) 2006-2007, Ext JS, LLC.
5671  *
5672  * Originally Released Under LGPL - original licence link has changed is not relivant.
5673  *
5674  * Fork - LGPL
5675  * <script type="text/javascript">
5676  */
5677
5678 // as we use this in bootstrap.
5679 Roo.namespace('Roo.form');
5680  /**
5681  * @class Roo.form.Action
5682  * Internal Class used to handle form actions
5683  * @constructor
5684  * @param {Roo.form.BasicForm} el The form element or its id
5685  * @param {Object} config Configuration options
5686  */
5687
5688  
5689  
5690 // define the action interface
5691 Roo.form.Action = function(form, options){
5692     this.form = form;
5693     this.options = options || {};
5694 };
5695 /**
5696  * Client Validation Failed
5697  * @const 
5698  */
5699 Roo.form.Action.CLIENT_INVALID = 'client';
5700 /**
5701  * Server Validation Failed
5702  * @const 
5703  */
5704 Roo.form.Action.SERVER_INVALID = 'server';
5705  /**
5706  * Connect to Server Failed
5707  * @const 
5708  */
5709 Roo.form.Action.CONNECT_FAILURE = 'connect';
5710 /**
5711  * Reading Data from Server Failed
5712  * @const 
5713  */
5714 Roo.form.Action.LOAD_FAILURE = 'load';
5715
5716 Roo.form.Action.prototype = {
5717     type : 'default',
5718     failureType : undefined,
5719     response : undefined,
5720     result : undefined,
5721
5722     // interface method
5723     run : function(options){
5724
5725     },
5726
5727     // interface method
5728     success : function(response){
5729
5730     },
5731
5732     // interface method
5733     handleResponse : function(response){
5734
5735     },
5736
5737     // default connection failure
5738     failure : function(response){
5739         
5740         this.response = response;
5741         this.failureType = Roo.form.Action.CONNECT_FAILURE;
5742         this.form.afterAction(this, false);
5743     },
5744
5745     processResponse : function(response){
5746         this.response = response;
5747         if(!response.responseText){
5748             return true;
5749         }
5750         this.result = this.handleResponse(response);
5751         return this.result;
5752     },
5753
5754     // utility functions used internally
5755     getUrl : function(appendParams){
5756         var url = this.options.url || this.form.url || this.form.el.dom.action;
5757         if(appendParams){
5758             var p = this.getParams();
5759             if(p){
5760                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
5761             }
5762         }
5763         return url;
5764     },
5765
5766     getMethod : function(){
5767         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
5768     },
5769
5770     getParams : function(){
5771         var bp = this.form.baseParams;
5772         var p = this.options.params;
5773         if(p){
5774             if(typeof p == "object"){
5775                 p = Roo.urlEncode(Roo.applyIf(p, bp));
5776             }else if(typeof p == 'string' && bp){
5777                 p += '&' + Roo.urlEncode(bp);
5778             }
5779         }else if(bp){
5780             p = Roo.urlEncode(bp);
5781         }
5782         return p;
5783     },
5784
5785     createCallback : function(){
5786         return {
5787             success: this.success,
5788             failure: this.failure,
5789             scope: this,
5790             timeout: (this.form.timeout*1000),
5791             upload: this.form.fileUpload ? this.success : undefined
5792         };
5793     }
5794 };
5795
5796 Roo.form.Action.Submit = function(form, options){
5797     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
5798 };
5799
5800 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
5801     type : 'submit',
5802
5803     haveProgress : false,
5804     uploadComplete : false,
5805     
5806     // uploadProgress indicator.
5807     uploadProgress : function()
5808     {
5809         if (!this.form.progressUrl) {
5810             return;
5811         }
5812         
5813         if (!this.haveProgress) {
5814             Roo.MessageBox.progress("Uploading", "Uploading");
5815         }
5816         if (this.uploadComplete) {
5817            Roo.MessageBox.hide();
5818            return;
5819         }
5820         
5821         this.haveProgress = true;
5822    
5823         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
5824         
5825         var c = new Roo.data.Connection();
5826         c.request({
5827             url : this.form.progressUrl,
5828             params: {
5829                 id : uid
5830             },
5831             method: 'GET',
5832             success : function(req){
5833                //console.log(data);
5834                 var rdata = false;
5835                 var edata;
5836                 try  {
5837                    rdata = Roo.decode(req.responseText)
5838                 } catch (e) {
5839                     Roo.log("Invalid data from server..");
5840                     Roo.log(edata);
5841                     return;
5842                 }
5843                 if (!rdata || !rdata.success) {
5844                     Roo.log(rdata);
5845                     Roo.MessageBox.alert(Roo.encode(rdata));
5846                     return;
5847                 }
5848                 var data = rdata.data;
5849                 
5850                 if (this.uploadComplete) {
5851                    Roo.MessageBox.hide();
5852                    return;
5853                 }
5854                    
5855                 if (data){
5856                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
5857                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
5858                     );
5859                 }
5860                 this.uploadProgress.defer(2000,this);
5861             },
5862        
5863             failure: function(data) {
5864                 Roo.log('progress url failed ');
5865                 Roo.log(data);
5866             },
5867             scope : this
5868         });
5869            
5870     },
5871     
5872     
5873     run : function()
5874     {
5875         // run get Values on the form, so it syncs any secondary forms.
5876         this.form.getValues();
5877         
5878         var o = this.options;
5879         var method = this.getMethod();
5880         var isPost = method == 'POST';
5881         if(o.clientValidation === false || this.form.isValid()){
5882             
5883             if (this.form.progressUrl) {
5884                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
5885                     (new Date() * 1) + '' + Math.random());
5886                     
5887             } 
5888             
5889             
5890             Roo.Ajax.request(Roo.apply(this.createCallback(), {
5891                 form:this.form.el.dom,
5892                 url:this.getUrl(!isPost),
5893                 method: method,
5894                 params:isPost ? this.getParams() : null,
5895                 isUpload: this.form.fileUpload
5896             }));
5897             
5898             this.uploadProgress();
5899
5900         }else if (o.clientValidation !== false){ // client validation failed
5901             this.failureType = Roo.form.Action.CLIENT_INVALID;
5902             this.form.afterAction(this, false);
5903         }
5904     },
5905
5906     success : function(response)
5907     {
5908         this.uploadComplete= true;
5909         if (this.haveProgress) {
5910             Roo.MessageBox.hide();
5911         }
5912         
5913         
5914         var result = this.processResponse(response);
5915         if(result === true || result.success){
5916             this.form.afterAction(this, true);
5917             return;
5918         }
5919         if(result.errors){
5920             this.form.markInvalid(result.errors);
5921             this.failureType = Roo.form.Action.SERVER_INVALID;
5922         }
5923         this.form.afterAction(this, false);
5924     },
5925     failure : function(response)
5926     {
5927         this.uploadComplete= true;
5928         if (this.haveProgress) {
5929             Roo.MessageBox.hide();
5930         }
5931         
5932         this.response = response;
5933         this.failureType = Roo.form.Action.CONNECT_FAILURE;
5934         this.form.afterAction(this, false);
5935     },
5936     
5937     handleResponse : function(response){
5938         if(this.form.errorReader){
5939             var rs = this.form.errorReader.read(response);
5940             var errors = [];
5941             if(rs.records){
5942                 for(var i = 0, len = rs.records.length; i < len; i++) {
5943                     var r = rs.records[i];
5944                     errors[i] = r.data;
5945                 }
5946             }
5947             if(errors.length < 1){
5948                 errors = null;
5949             }
5950             return {
5951                 success : rs.success,
5952                 errors : errors
5953             };
5954         }
5955         var ret = false;
5956         try {
5957             ret = Roo.decode(response.responseText);
5958         } catch (e) {
5959             ret = {
5960                 success: false,
5961                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
5962                 errors : []
5963             };
5964         }
5965         return ret;
5966         
5967     }
5968 });
5969
5970
5971 Roo.form.Action.Load = function(form, options){
5972     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
5973     this.reader = this.form.reader;
5974 };
5975
5976 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
5977     type : 'load',
5978
5979     run : function(){
5980         
5981         Roo.Ajax.request(Roo.apply(
5982                 this.createCallback(), {
5983                     method:this.getMethod(),
5984                     url:this.getUrl(false),
5985                     params:this.getParams()
5986         }));
5987     },
5988
5989     success : function(response){
5990         
5991         var result = this.processResponse(response);
5992         if(result === true || !result.success || !result.data){
5993             this.failureType = Roo.form.Action.LOAD_FAILURE;
5994             this.form.afterAction(this, false);
5995             return;
5996         }
5997         this.form.clearInvalid();
5998         this.form.setValues(result.data);
5999         this.form.afterAction(this, true);
6000     },
6001
6002     handleResponse : function(response){
6003         if(this.form.reader){
6004             var rs = this.form.reader.read(response);
6005             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
6006             return {
6007                 success : rs.success,
6008                 data : data
6009             };
6010         }
6011         return Roo.decode(response.responseText);
6012     }
6013 });
6014
6015 Roo.form.Action.ACTION_TYPES = {
6016     'load' : Roo.form.Action.Load,
6017     'submit' : Roo.form.Action.Submit
6018 };/*
6019  * - LGPL
6020  *
6021  * form
6022  * 
6023  */
6024
6025 /**
6026  * @class Roo.bootstrap.Form
6027  * @extends Roo.bootstrap.Component
6028  * Bootstrap Form class
6029  * @cfg {String} method  GET | POST (default POST)
6030  * @cfg {String} labelAlign top | left (default top)
6031   * @cfg {String} align left  | right - for navbars
6032
6033  * 
6034  * @constructor
6035  * Create a new Form
6036  * @param {Object} config The config object
6037  */
6038
6039
6040 Roo.bootstrap.Form = function(config){
6041     Roo.bootstrap.Form.superclass.constructor.call(this, config);
6042     this.addEvents({
6043         /**
6044          * @event clientvalidation
6045          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
6046          * @param {Form} this
6047          * @param {Boolean} valid true if the form has passed client-side validation
6048          */
6049         clientvalidation: true,
6050         /**
6051          * @event beforeaction
6052          * Fires before any action is performed. Return false to cancel the action.
6053          * @param {Form} this
6054          * @param {Action} action The action to be performed
6055          */
6056         beforeaction: true,
6057         /**
6058          * @event actionfailed
6059          * Fires when an action fails.
6060          * @param {Form} this
6061          * @param {Action} action The action that failed
6062          */
6063         actionfailed : true,
6064         /**
6065          * @event actioncomplete
6066          * Fires when an action is completed.
6067          * @param {Form} this
6068          * @param {Action} action The action that completed
6069          */
6070         actioncomplete : true
6071     });
6072     
6073 };
6074
6075 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
6076       
6077      /**
6078      * @cfg {String} method
6079      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
6080      */
6081     method : 'POST',
6082     /**
6083      * @cfg {String} url
6084      * The URL to use for form actions if one isn't supplied in the action options.
6085      */
6086     /**
6087      * @cfg {Boolean} fileUpload
6088      * Set to true if this form is a file upload.
6089      */
6090      
6091     /**
6092      * @cfg {Object} baseParams
6093      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
6094      */
6095       
6096     /**
6097      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
6098      */
6099     timeout: 30,
6100     /**
6101      * @cfg {Sting} align (left|right) for navbar forms
6102      */
6103     align : 'left',
6104
6105     // private
6106     activeAction : null,
6107  
6108     /**
6109      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
6110      * element by passing it or its id or mask the form itself by passing in true.
6111      * @type Mixed
6112      */
6113     waitMsgTarget : false,
6114     
6115      
6116     
6117     /**
6118      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
6119      * element by passing it or its id or mask the form itself by passing in true.
6120      * @type Mixed
6121      */
6122     
6123     getAutoCreate : function(){
6124         
6125         var cfg = {
6126             tag: 'form',
6127             method : this.method || 'POST',
6128             id : this.id || Roo.id(),
6129             cls : ''
6130         }
6131         if (this.parent().xtype.match(/^Nav/)) {
6132             cfg.cls = 'navbar-form navbar-' + this.align;
6133             
6134         }
6135         
6136         if (this.labelAlign == 'left' ) {
6137             cfg.cls += ' form-horizontal';
6138         }
6139         
6140         
6141         return cfg;
6142     },
6143     initEvents : function()
6144     {
6145         this.el.on('submit', this.onSubmit, this);
6146         // this was added as random key presses on the form where triggering form submit.
6147         this.el.on('keypress', function(e) {
6148             if (e.getCharCode() != 13) {
6149                 return true;
6150             }
6151             // we might need to allow it for textareas.. and some other items.
6152             // check e.getTarget().
6153             
6154             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
6155                 return true;
6156             }
6157         
6158             Roo.log("keypress blocked");
6159             
6160             e.preventDefault();
6161             return false;
6162         });
6163         
6164     },
6165     // private
6166     onSubmit : function(e){
6167         e.stopEvent();
6168     },
6169     
6170      /**
6171      * Returns true if client-side validation on the form is successful.
6172      * @return Boolean
6173      */
6174     isValid : function(){
6175         var items = this.getItems();
6176         var valid = true;
6177         items.each(function(f){
6178            if(!f.validate()){
6179                valid = false;
6180                
6181            }
6182         });
6183         return valid;
6184     },
6185     /**
6186      * Returns true if any fields in this form have changed since their original load.
6187      * @return Boolean
6188      */
6189     isDirty : function(){
6190         var dirty = false;
6191         var items = this.getItems();
6192         items.each(function(f){
6193            if(f.isDirty()){
6194                dirty = true;
6195                return false;
6196            }
6197            return true;
6198         });
6199         return dirty;
6200     },
6201      /**
6202      * Performs a predefined action (submit or load) or custom actions you define on this form.
6203      * @param {String} actionName The name of the action type
6204      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
6205      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
6206      * accept other config options):
6207      * <pre>
6208 Property          Type             Description
6209 ----------------  ---------------  ----------------------------------------------------------------------------------
6210 url               String           The url for the action (defaults to the form's url)
6211 method            String           The form method to use (defaults to the form's method, or POST if not defined)
6212 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
6213 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
6214                                    validate the form on the client (defaults to false)
6215      * </pre>
6216      * @return {BasicForm} this
6217      */
6218     doAction : function(action, options){
6219         if(typeof action == 'string'){
6220             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
6221         }
6222         if(this.fireEvent('beforeaction', this, action) !== false){
6223             this.beforeAction(action);
6224             action.run.defer(100, action);
6225         }
6226         return this;
6227     },
6228     
6229     // private
6230     beforeAction : function(action){
6231         var o = action.options;
6232         
6233         // not really supported yet.. ??
6234         
6235         //if(this.waitMsgTarget === true){
6236             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6237         //}else if(this.waitMsgTarget){
6238         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
6239         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
6240         //}else {
6241         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
6242        // }
6243          
6244     },
6245
6246     // private
6247     afterAction : function(action, success){
6248         this.activeAction = null;
6249         var o = action.options;
6250         
6251         //if(this.waitMsgTarget === true){
6252             this.el.unmask();
6253         //}else if(this.waitMsgTarget){
6254         //    this.waitMsgTarget.unmask();
6255         //}else{
6256         //    Roo.MessageBox.updateProgress(1);
6257         //    Roo.MessageBox.hide();
6258        // }
6259         // 
6260         if(success){
6261             if(o.reset){
6262                 this.reset();
6263             }
6264             Roo.callback(o.success, o.scope, [this, action]);
6265             this.fireEvent('actioncomplete', this, action);
6266             
6267         }else{
6268             
6269             // failure condition..
6270             // we have a scenario where updates need confirming.
6271             // eg. if a locking scenario exists..
6272             // we look for { errors : { needs_confirm : true }} in the response.
6273             if (
6274                 (typeof(action.result) != 'undefined')  &&
6275                 (typeof(action.result.errors) != 'undefined')  &&
6276                 (typeof(action.result.errors.needs_confirm) != 'undefined')
6277            ){
6278                 var _t = this;
6279                 Roo.log("not supported yet");
6280                  /*
6281                 
6282                 Roo.MessageBox.confirm(
6283                     "Change requires confirmation",
6284                     action.result.errorMsg,
6285                     function(r) {
6286                         if (r != 'yes') {
6287                             return;
6288                         }
6289                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
6290                     }
6291                     
6292                 );
6293                 */
6294                 
6295                 
6296                 return;
6297             }
6298             
6299             Roo.callback(o.failure, o.scope, [this, action]);
6300             // show an error message if no failed handler is set..
6301             if (!this.hasListener('actionfailed')) {
6302                 Roo.log("need to add dialog support");
6303                 /*
6304                 Roo.MessageBox.alert("Error",
6305                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
6306                         action.result.errorMsg :
6307                         "Saving Failed, please check your entries or try again"
6308                 );
6309                 */
6310             }
6311             
6312             this.fireEvent('actionfailed', this, action);
6313         }
6314         
6315     },
6316     /**
6317      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
6318      * @param {String} id The value to search for
6319      * @return Field
6320      */
6321     findField : function(id){
6322         var items = this.getItems();
6323         var field = items.get(id);
6324         if(!field){
6325              items.each(function(f){
6326                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
6327                     field = f;
6328                     return false;
6329                 }
6330                 return true;
6331             });
6332         }
6333         return field || null;
6334     },
6335      /**
6336      * Mark fields in this form invalid in bulk.
6337      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
6338      * @return {BasicForm} this
6339      */
6340     markInvalid : function(errors){
6341         if(errors instanceof Array){
6342             for(var i = 0, len = errors.length; i < len; i++){
6343                 var fieldError = errors[i];
6344                 var f = this.findField(fieldError.id);
6345                 if(f){
6346                     f.markInvalid(fieldError.msg);
6347                 }
6348             }
6349         }else{
6350             var field, id;
6351             for(id in errors){
6352                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
6353                     field.markInvalid(errors[id]);
6354                 }
6355             }
6356         }
6357         //Roo.each(this.childForms || [], function (f) {
6358         //    f.markInvalid(errors);
6359         //});
6360         
6361         return this;
6362     },
6363
6364     /**
6365      * Set values for fields in this form in bulk.
6366      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
6367      * @return {BasicForm} this
6368      */
6369     setValues : function(values){
6370         if(values instanceof Array){ // array of objects
6371             for(var i = 0, len = values.length; i < len; i++){
6372                 var v = values[i];
6373                 var f = this.findField(v.id);
6374                 if(f){
6375                     f.setValue(v.value);
6376                     if(this.trackResetOnLoad){
6377                         f.originalValue = f.getValue();
6378                     }
6379                 }
6380             }
6381         }else{ // object hash
6382             var field, id;
6383             for(id in values){
6384                 if(typeof values[id] != 'function' && (field = this.findField(id))){
6385                     
6386                     if (field.setFromData && 
6387                         field.valueField && 
6388                         field.displayField &&
6389                         // combos' with local stores can 
6390                         // be queried via setValue()
6391                         // to set their value..
6392                         (field.store && !field.store.isLocal)
6393                         ) {
6394                         // it's a combo
6395                         var sd = { };
6396                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
6397                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
6398                         field.setFromData(sd);
6399                         
6400                     } else {
6401                         field.setValue(values[id]);
6402                     }
6403                     
6404                     
6405                     if(this.trackResetOnLoad){
6406                         field.originalValue = field.getValue();
6407                     }
6408                 }
6409             }
6410         }
6411          
6412         //Roo.each(this.childForms || [], function (f) {
6413         //    f.setValues(values);
6414         //});
6415                 
6416         return this;
6417     },
6418
6419     /**
6420      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
6421      * they are returned as an array.
6422      * @param {Boolean} asString
6423      * @return {Object}
6424      */
6425     getValues : function(asString){
6426         //if (this.childForms) {
6427             // copy values from the child forms
6428         //    Roo.each(this.childForms, function (f) {
6429         //        this.setValues(f.getValues());
6430         //    }, this);
6431         //}
6432         
6433         
6434         
6435         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
6436         if(asString === true){
6437             return fs;
6438         }
6439         return Roo.urlDecode(fs);
6440     },
6441     
6442     /**
6443      * Returns the fields in this form as an object with key/value pairs. 
6444      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
6445      * @return {Object}
6446      */
6447     getFieldValues : function(with_hidden)
6448     {
6449         var items = this.getItems();
6450         var ret = {};
6451         items.each(function(f){
6452             if (!f.getName()) {
6453                 return;
6454             }
6455             var v = f.getValue();
6456             if (f.inputType =='radio') {
6457                 if (typeof(ret[f.getName()]) == 'undefined') {
6458                     ret[f.getName()] = ''; // empty..
6459                 }
6460                 
6461                 if (!f.el.dom.checked) {
6462                     return;
6463                     
6464                 }
6465                 v = f.el.dom.value;
6466                 
6467             }
6468             
6469             // not sure if this supported any more..
6470             if ((typeof(v) == 'object') && f.getRawValue) {
6471                 v = f.getRawValue() ; // dates..
6472             }
6473             // combo boxes where name != hiddenName...
6474             if (f.name != f.getName()) {
6475                 ret[f.name] = f.getRawValue();
6476             }
6477             ret[f.getName()] = v;
6478         });
6479         
6480         return ret;
6481     },
6482
6483     /**
6484      * Clears all invalid messages in this form.
6485      * @return {BasicForm} this
6486      */
6487     clearInvalid : function(){
6488         var items = this.getItems();
6489         
6490         items.each(function(f){
6491            f.clearInvalid();
6492         });
6493         
6494         
6495         
6496         return this;
6497     },
6498
6499     /**
6500      * Resets this form.
6501      * @return {BasicForm} this
6502      */
6503     reset : function(){
6504         var items = this.getItems();
6505         items.each(function(f){
6506             f.reset();
6507         });
6508         
6509         Roo.each(this.childForms || [], function (f) {
6510             f.reset();
6511         });
6512        
6513         
6514         return this;
6515     },
6516     getItems : function()
6517     {
6518         var r=new Roo.util.MixedCollection(false, function(o){
6519             return o.id || (o.id = Roo.id());
6520         });
6521         var iter = function(el) {
6522             if (el.inputEl) {
6523                 r.add(el);
6524             }
6525             if (!el.items) {
6526                 return;
6527             }
6528             Roo.each(el.items,function(e) {
6529                 iter(e);
6530             });
6531             
6532             
6533         };
6534         iter(this);
6535         return r;
6536         
6537         
6538         
6539         
6540     }
6541     
6542 });
6543
6544  
6545 /*
6546  * Based on:
6547  * Ext JS Library 1.1.1
6548  * Copyright(c) 2006-2007, Ext JS, LLC.
6549  *
6550  * Originally Released Under LGPL - original licence link has changed is not relivant.
6551  *
6552  * Fork - LGPL
6553  * <script type="text/javascript">
6554  */
6555 /**
6556  * @class Roo.form.VTypes
6557  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
6558  * @singleton
6559  */
6560 Roo.form.VTypes = function(){
6561     // closure these in so they are only created once.
6562     var alpha = /^[a-zA-Z_]+$/;
6563     var alphanum = /^[a-zA-Z0-9_]+$/;
6564     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
6565     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
6566
6567     // All these messages and functions are configurable
6568     return {
6569         /**
6570          * The function used to validate email addresses
6571          * @param {String} value The email address
6572          */
6573         'email' : function(v){
6574             return email.test(v);
6575         },
6576         /**
6577          * The error text to display when the email validation function returns false
6578          * @type String
6579          */
6580         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
6581         /**
6582          * The keystroke filter mask to be applied on email input
6583          * @type RegExp
6584          */
6585         'emailMask' : /[a-z0-9_\.\-@]/i,
6586
6587         /**
6588          * The function used to validate URLs
6589          * @param {String} value The URL
6590          */
6591         'url' : function(v){
6592             return url.test(v);
6593         },
6594         /**
6595          * The error text to display when the url validation function returns false
6596          * @type String
6597          */
6598         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
6599         
6600         /**
6601          * The function used to validate alpha values
6602          * @param {String} value The value
6603          */
6604         'alpha' : function(v){
6605             return alpha.test(v);
6606         },
6607         /**
6608          * The error text to display when the alpha validation function returns false
6609          * @type String
6610          */
6611         'alphaText' : 'This field should only contain letters and _',
6612         /**
6613          * The keystroke filter mask to be applied on alpha input
6614          * @type RegExp
6615          */
6616         'alphaMask' : /[a-z_]/i,
6617
6618         /**
6619          * The function used to validate alphanumeric values
6620          * @param {String} value The value
6621          */
6622         'alphanum' : function(v){
6623             return alphanum.test(v);
6624         },
6625         /**
6626          * The error text to display when the alphanumeric validation function returns false
6627          * @type String
6628          */
6629         'alphanumText' : 'This field should only contain letters, numbers and _',
6630         /**
6631          * The keystroke filter mask to be applied on alphanumeric input
6632          * @type RegExp
6633          */
6634         'alphanumMask' : /[a-z0-9_]/i
6635     };
6636 }();/*
6637  * - LGPL
6638  *
6639  * Input
6640  * 
6641  */
6642
6643 /**
6644  * @class Roo.bootstrap.Input
6645  * @extends Roo.bootstrap.Component
6646  * Bootstrap Input class
6647  * @cfg {Boolean} disabled is it disabled
6648  * @cfg {String} fieldLabel - the label associated
6649  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
6650  * @cfg {String} name name of the input
6651  * @cfg {string} fieldLabel - the label associated
6652  * @cfg {string}  inputType - input / file submit ...
6653  * @cfg {string} placeholder - placeholder to put in text.
6654  * @cfg {string}  before - input group add on before
6655  * @cfg {string} after - input group add on after
6656  * @cfg {string} size - (lg|sm) or leave empty..
6657  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
6658  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
6659  * @cfg {Number} md colspan out of 12 for computer-sized screens
6660  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
6661  * @cfg {string} value default value of the input
6662  * @cfg {Number} labelWidth set the width of label (0-12)
6663  * @cfg {String} labelAlign (top|left)
6664  * @cfg {Boolean} readOnly Specifies that the field should be read-only
6665  * @cfg {String} align (left|center|right) Default left
6666  * 
6667  * 
6668  * @constructor
6669  * Create a new Input
6670  * @param {Object} config The config object
6671  */
6672
6673 Roo.bootstrap.Input = function(config){
6674     Roo.bootstrap.Input.superclass.constructor.call(this, config);
6675    
6676         this.addEvents({
6677             /**
6678              * @event focus
6679              * Fires when this field receives input focus.
6680              * @param {Roo.form.Field} this
6681              */
6682             focus : true,
6683             /**
6684              * @event blur
6685              * Fires when this field loses input focus.
6686              * @param {Roo.form.Field} this
6687              */
6688             blur : true,
6689             /**
6690              * @event specialkey
6691              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
6692              * {@link Roo.EventObject#getKey} to determine which key was pressed.
6693              * @param {Roo.form.Field} this
6694              * @param {Roo.EventObject} e The event object
6695              */
6696             specialkey : true,
6697             /**
6698              * @event change
6699              * Fires just before the field blurs if the field value has changed.
6700              * @param {Roo.form.Field} this
6701              * @param {Mixed} newValue The new value
6702              * @param {Mixed} oldValue The original value
6703              */
6704             change : true,
6705             /**
6706              * @event invalid
6707              * Fires after the field has been marked as invalid.
6708              * @param {Roo.form.Field} this
6709              * @param {String} msg The validation message
6710              */
6711             invalid : true,
6712             /**
6713              * @event valid
6714              * Fires after the field has been validated with no errors.
6715              * @param {Roo.form.Field} this
6716              */
6717             valid : true,
6718              /**
6719              * @event keyup
6720              * Fires after the key up
6721              * @param {Roo.form.Field} this
6722              * @param {Roo.EventObject}  e The event Object
6723              */
6724             keyup : true
6725         });
6726 };
6727
6728 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
6729      /**
6730      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
6731       automatic validation (defaults to "keyup").
6732      */
6733     validationEvent : "keyup",
6734      /**
6735      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
6736      */
6737     validateOnBlur : true,
6738     /**
6739      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
6740      */
6741     validationDelay : 250,
6742      /**
6743      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
6744      */
6745     focusClass : "x-form-focus",  // not needed???
6746     
6747        
6748     /**
6749      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
6750      */
6751     invalidClass : "has-error",
6752     
6753     /**
6754      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
6755      */
6756     selectOnFocus : false,
6757     
6758      /**
6759      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
6760      */
6761     maskRe : null,
6762        /**
6763      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
6764      */
6765     vtype : null,
6766     
6767       /**
6768      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
6769      */
6770     disableKeyFilter : false,
6771     
6772        /**
6773      * @cfg {Boolean} disabled True to disable the field (defaults to false).
6774      */
6775     disabled : false,
6776      /**
6777      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
6778      */
6779     allowBlank : true,
6780     /**
6781      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
6782      */
6783     blankText : "This field is required",
6784     
6785      /**
6786      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
6787      */
6788     minLength : 0,
6789     /**
6790      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
6791      */
6792     maxLength : Number.MAX_VALUE,
6793     /**
6794      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
6795      */
6796     minLengthText : "The minimum length for this field is {0}",
6797     /**
6798      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
6799      */
6800     maxLengthText : "The maximum length for this field is {0}",
6801   
6802     
6803     /**
6804      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
6805      * If available, this function will be called only after the basic validators all return true, and will be passed the
6806      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
6807      */
6808     validator : null,
6809     /**
6810      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
6811      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
6812      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
6813      */
6814     regex : null,
6815     /**
6816      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
6817      */
6818     regexText : "",
6819     
6820     
6821     
6822     fieldLabel : '',
6823     inputType : 'text',
6824     
6825     name : false,
6826     placeholder: false,
6827     before : false,
6828     after : false,
6829     size : false,
6830     // private
6831     hasFocus : false,
6832     preventMark: false,
6833     isFormField : true,
6834     value : '',
6835     labelWidth : 2,
6836     labelAlign : false,
6837     readOnly : false,
6838     align : false,
6839     formatedValue : false,
6840     
6841     parentLabelAlign : function()
6842     {
6843         var parent = this;
6844         while (parent.parent()) {
6845             parent = parent.parent();
6846             if (typeof(parent.labelAlign) !='undefined') {
6847                 return parent.labelAlign;
6848             }
6849         }
6850         return 'left';
6851         
6852     },
6853     
6854     getAutoCreate : function(){
6855         
6856         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
6857         
6858         var id = Roo.id();
6859         
6860         var cfg = {};
6861         
6862         if(this.inputType != 'hidden'){
6863             cfg.cls = 'form-group' //input-group
6864         }
6865         
6866         var input =  {
6867             tag: 'input',
6868             id : id,
6869             type : this.inputType,
6870             value : this.value,
6871             cls : 'form-control',
6872             placeholder : this.placeholder || ''
6873             
6874         };
6875         
6876         if(this.align){
6877             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
6878         }
6879         
6880         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
6881             input.maxLength = this.maxLength;
6882         }
6883         
6884         if (this.disabled) {
6885             input.disabled=true;
6886         }
6887         
6888         if (this.readOnly) {
6889             input.readonly=true;
6890         }
6891         
6892         if (this.name) {
6893             input.name = this.name;
6894         }
6895         if (this.size) {
6896             input.cls += ' input-' + this.size;
6897         }
6898         var settings=this;
6899         ['xs','sm','md','lg'].map(function(size){
6900             if (settings[size]) {
6901                 cfg.cls += ' col-' + size + '-' + settings[size];
6902             }
6903         });
6904         
6905         var inputblock = input;
6906         
6907         if (this.before || this.after) {
6908             
6909             inputblock = {
6910                 cls : 'input-group',
6911                 cn :  [] 
6912             };
6913             if (this.before && typeof(this.before) == 'string') {
6914                 
6915                 inputblock.cn.push({
6916                     tag :'span',
6917                     cls : 'roo-input-before input-group-addon',
6918                     html : this.before
6919                 });
6920             }
6921             if (this.before && typeof(this.before) == 'object') {
6922                 this.before = Roo.factory(this.before);
6923                 Roo.log(this.before);
6924                 inputblock.cn.push({
6925                     tag :'span',
6926                     cls : 'roo-input-before input-group-' +
6927                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
6928                 });
6929             }
6930             
6931             inputblock.cn.push(input);
6932             
6933             if (this.after && typeof(this.after) == 'string') {
6934                 inputblock.cn.push({
6935                     tag :'span',
6936                     cls : 'roo-input-after input-group-addon',
6937                     html : this.after
6938                 });
6939             }
6940             if (this.after && typeof(this.after) == 'object') {
6941                 this.after = Roo.factory(this.after);
6942                 Roo.log(this.after);
6943                 inputblock.cn.push({
6944                     tag :'span',
6945                     cls : 'roo-input-after input-group-' +
6946                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
6947                 });
6948             }
6949         };
6950         
6951         if (align ==='left' && this.fieldLabel.length) {
6952                 Roo.log("left and has label");
6953                 cfg.cn = [
6954                     
6955                     {
6956                         tag: 'label',
6957                         'for' :  id,
6958                         cls : 'control-label col-sm-' + this.labelWidth,
6959                         html : this.fieldLabel
6960                         
6961                     },
6962                     {
6963                         cls : "col-sm-" + (12 - this.labelWidth), 
6964                         cn: [
6965                             inputblock
6966                         ]
6967                     }
6968                     
6969                 ];
6970         } else if ( this.fieldLabel.length) {
6971                 Roo.log(" label");
6972                  cfg.cn = [
6973                    
6974                     {
6975                         tag: 'label',
6976                         //cls : 'input-group-addon',
6977                         html : this.fieldLabel
6978                         
6979                     },
6980                     
6981                     inputblock
6982                     
6983                 ];
6984
6985         } else {
6986             
6987                 Roo.log(" no label && no align");
6988                 cfg.cn = [
6989                     
6990                         inputblock
6991                     
6992                 ];
6993                 
6994                 
6995         };
6996         Roo.log('input-parentType: ' + this.parentType);
6997         
6998         if (this.parentType === 'Navbar' &&  this.parent().bar) {
6999            cfg.cls += ' navbar-form';
7000            Roo.log(cfg);
7001         }
7002         
7003         return cfg;
7004         
7005     },
7006     /**
7007      * return the real input element.
7008      */
7009     inputEl: function ()
7010     {
7011         return this.el.select('input.form-control',true).first();
7012     },
7013     setDisabled : function(v)
7014     {
7015         var i  = this.inputEl().dom;
7016         if (!v) {
7017             i.removeAttribute('disabled');
7018             return;
7019             
7020         }
7021         i.setAttribute('disabled','true');
7022     },
7023     initEvents : function()
7024     {
7025         
7026         this.inputEl().on("keydown" , this.fireKey,  this);
7027         this.inputEl().on("focus", this.onFocus,  this);
7028         this.inputEl().on("blur", this.onBlur,  this);
7029         
7030         this.inputEl().relayEvent('keyup', this);
7031
7032         // reference to original value for reset
7033         this.originalValue = this.getValue();
7034         //Roo.form.TextField.superclass.initEvents.call(this);
7035         if(this.validationEvent == 'keyup'){
7036             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
7037             this.inputEl().on('keyup', this.filterValidation, this);
7038         }
7039         else if(this.validationEvent !== false){
7040             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
7041         }
7042         
7043         if(this.selectOnFocus){
7044             this.on("focus", this.preFocus, this);
7045             
7046         }
7047         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
7048             this.inputEl().on("keypress", this.filterKeys, this);
7049         }
7050        /* if(this.grow){
7051             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
7052             this.el.on("click", this.autoSize,  this);
7053         }
7054         */
7055         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
7056             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
7057         }
7058         
7059         if (typeof(this.before) == 'object') {
7060             this.before.render(this.el.select('.roo-input-before',true).first());
7061         }
7062         if (typeof(this.after) == 'object') {
7063             this.after.render(this.el.select('.roo-input-after',true).first());
7064         }
7065         
7066         
7067     },
7068     filterValidation : function(e){
7069         if(!e.isNavKeyPress()){
7070             this.validationTask.delay(this.validationDelay);
7071         }
7072     },
7073      /**
7074      * Validates the field value
7075      * @return {Boolean} True if the value is valid, else false
7076      */
7077     validate : function(){
7078         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
7079         if(this.disabled || this.validateValue(this.getRawValue())){
7080             this.clearInvalid();
7081             return true;
7082         }
7083         return false;
7084     },
7085     
7086     
7087     /**
7088      * Validates a value according to the field's validation rules and marks the field as invalid
7089      * if the validation fails
7090      * @param {Mixed} value The value to validate
7091      * @return {Boolean} True if the value is valid, else false
7092      */
7093     validateValue : function(value){
7094         if(value.length < 1)  { // if it's blank
7095              if(this.allowBlank){
7096                 this.clearInvalid();
7097                 return true;
7098              }else{
7099                 this.markInvalid(this.blankText);
7100                 return false;
7101              }
7102         }
7103         if(value.length < this.minLength){
7104             this.markInvalid(String.format(this.minLengthText, this.minLength));
7105             return false;
7106         }
7107         if(value.length > this.maxLength){
7108             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
7109             return false;
7110         }
7111         if(this.vtype){
7112             var vt = Roo.form.VTypes;
7113             if(!vt[this.vtype](value, this)){
7114                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
7115                 return false;
7116             }
7117         }
7118         if(typeof this.validator == "function"){
7119             var msg = this.validator(value);
7120             if(msg !== true){
7121                 this.markInvalid(msg);
7122                 return false;
7123             }
7124         }
7125         if(this.regex && !this.regex.test(value)){
7126             this.markInvalid(this.regexText);
7127             return false;
7128         }
7129         return true;
7130     },
7131
7132     
7133     
7134      // private
7135     fireKey : function(e){
7136         //Roo.log('field ' + e.getKey());
7137         if(e.isNavKeyPress()){
7138             this.fireEvent("specialkey", this, e);
7139         }
7140     },
7141     focus : function (selectText){
7142         if(this.rendered){
7143             this.inputEl().focus();
7144             if(selectText === true){
7145                 this.inputEl().dom.select();
7146             }
7147         }
7148         return this;
7149     } ,
7150     
7151     onFocus : function(){
7152         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7153            // this.el.addClass(this.focusClass);
7154         }
7155         if(!this.hasFocus){
7156             this.hasFocus = true;
7157             this.startValue = this.getValue();
7158             this.fireEvent("focus", this);
7159         }
7160     },
7161     
7162     beforeBlur : Roo.emptyFn,
7163
7164     
7165     // private
7166     onBlur : function(){
7167         this.beforeBlur();
7168         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7169             //this.el.removeClass(this.focusClass);
7170         }
7171         this.hasFocus = false;
7172         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
7173             this.validate();
7174         }
7175         var v = this.getValue();
7176         if(String(v) !== String(this.startValue)){
7177             this.fireEvent('change', this, v, this.startValue);
7178         }
7179         this.fireEvent("blur", this);
7180     },
7181     
7182     /**
7183      * Resets the current field value to the originally loaded value and clears any validation messages
7184      */
7185     reset : function(){
7186         this.setValue(this.originalValue);
7187         this.clearInvalid();
7188     },
7189      /**
7190      * Returns the name of the field
7191      * @return {Mixed} name The name field
7192      */
7193     getName: function(){
7194         return this.name;
7195     },
7196      /**
7197      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
7198      * @return {Mixed} value The field value
7199      */
7200     getValue : function(){
7201         
7202         var v = this.inputEl().getValue();
7203         
7204         return v;
7205     },
7206     /**
7207      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
7208      * @return {Mixed} value The field value
7209      */
7210     getRawValue : function(){
7211         var v = this.inputEl().getValue();
7212         
7213         return v;
7214     },
7215     
7216     /**
7217      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
7218      * @param {Mixed} value The value to set
7219      */
7220     setRawValue : function(v){
7221         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7222     },
7223     
7224     selectText : function(start, end){
7225         var v = this.getRawValue();
7226         if(v.length > 0){
7227             start = start === undefined ? 0 : start;
7228             end = end === undefined ? v.length : end;
7229             var d = this.inputEl().dom;
7230             if(d.setSelectionRange){
7231                 d.setSelectionRange(start, end);
7232             }else if(d.createTextRange){
7233                 var range = d.createTextRange();
7234                 range.moveStart("character", start);
7235                 range.moveEnd("character", v.length-end);
7236                 range.select();
7237             }
7238         }
7239     },
7240     
7241     /**
7242      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
7243      * @param {Mixed} value The value to set
7244      */
7245     setValue : function(v){
7246         this.value = v;
7247         if(this.rendered){
7248             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7249             this.validate();
7250         }
7251     },
7252     
7253     /*
7254     processValue : function(value){
7255         if(this.stripCharsRe){
7256             var newValue = value.replace(this.stripCharsRe, '');
7257             if(newValue !== value){
7258                 this.setRawValue(newValue);
7259                 return newValue;
7260             }
7261         }
7262         return value;
7263     },
7264   */
7265     preFocus : function(){
7266         
7267         if(this.selectOnFocus){
7268             this.inputEl().dom.select();
7269         }
7270     },
7271     filterKeys : function(e){
7272         var k = e.getKey();
7273         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
7274             return;
7275         }
7276         var c = e.getCharCode(), cc = String.fromCharCode(c);
7277         if(Roo.isIE && (e.isSpecialKey() || !cc)){
7278             return;
7279         }
7280         if(!this.maskRe.test(cc)){
7281             e.stopEvent();
7282         }
7283     },
7284      /**
7285      * Clear any invalid styles/messages for this field
7286      */
7287     clearInvalid : function(){
7288         
7289         if(!this.el || this.preventMark){ // not rendered
7290             return;
7291         }
7292         this.el.removeClass(this.invalidClass);
7293         /*
7294         switch(this.msgTarget){
7295             case 'qtip':
7296                 this.el.dom.qtip = '';
7297                 break;
7298             case 'title':
7299                 this.el.dom.title = '';
7300                 break;
7301             case 'under':
7302                 if(this.errorEl){
7303                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
7304                 }
7305                 break;
7306             case 'side':
7307                 if(this.errorIcon){
7308                     this.errorIcon.dom.qtip = '';
7309                     this.errorIcon.hide();
7310                     this.un('resize', this.alignErrorIcon, this);
7311                 }
7312                 break;
7313             default:
7314                 var t = Roo.getDom(this.msgTarget);
7315                 t.innerHTML = '';
7316                 t.style.display = 'none';
7317                 break;
7318         }
7319         */
7320         this.fireEvent('valid', this);
7321     },
7322      /**
7323      * Mark this field as invalid
7324      * @param {String} msg The validation message
7325      */
7326     markInvalid : function(msg){
7327         if(!this.el  || this.preventMark){ // not rendered
7328             return;
7329         }
7330         this.el.addClass(this.invalidClass);
7331         /*
7332         msg = msg || this.invalidText;
7333         switch(this.msgTarget){
7334             case 'qtip':
7335                 this.el.dom.qtip = msg;
7336                 this.el.dom.qclass = 'x-form-invalid-tip';
7337                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
7338                     Roo.QuickTips.enable();
7339                 }
7340                 break;
7341             case 'title':
7342                 this.el.dom.title = msg;
7343                 break;
7344             case 'under':
7345                 if(!this.errorEl){
7346                     var elp = this.el.findParent('.x-form-element', 5, true);
7347                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
7348                     this.errorEl.setWidth(elp.getWidth(true)-20);
7349                 }
7350                 this.errorEl.update(msg);
7351                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
7352                 break;
7353             case 'side':
7354                 if(!this.errorIcon){
7355                     var elp = this.el.findParent('.x-form-element', 5, true);
7356                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
7357                 }
7358                 this.alignErrorIcon();
7359                 this.errorIcon.dom.qtip = msg;
7360                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
7361                 this.errorIcon.show();
7362                 this.on('resize', this.alignErrorIcon, this);
7363                 break;
7364             default:
7365                 var t = Roo.getDom(this.msgTarget);
7366                 t.innerHTML = msg;
7367                 t.style.display = this.msgDisplay;
7368                 break;
7369         }
7370         */
7371         this.fireEvent('invalid', this, msg);
7372     },
7373     // private
7374     SafariOnKeyDown : function(event)
7375     {
7376         // this is a workaround for a password hang bug on chrome/ webkit.
7377         
7378         var isSelectAll = false;
7379         
7380         if(this.inputEl().dom.selectionEnd > 0){
7381             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
7382         }
7383         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
7384             event.preventDefault();
7385             this.setValue('');
7386             return;
7387         }
7388         
7389         if(isSelectAll){ // backspace and delete key
7390             
7391             event.preventDefault();
7392             // this is very hacky as keydown always get's upper case.
7393             //
7394             var cc = String.fromCharCode(event.getCharCode());
7395             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
7396             
7397         }
7398     },
7399     adjustWidth : function(tag, w){
7400         tag = tag.toLowerCase();
7401         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
7402             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
7403                 if(tag == 'input'){
7404                     return w + 2;
7405                 }
7406                 if(tag == 'textarea'){
7407                     return w-2;
7408                 }
7409             }else if(Roo.isOpera){
7410                 if(tag == 'input'){
7411                     return w + 2;
7412                 }
7413                 if(tag == 'textarea'){
7414                     return w-2;
7415                 }
7416             }
7417         }
7418         return w;
7419     }
7420     
7421 });
7422
7423  
7424 /*
7425  * - LGPL
7426  *
7427  * Input
7428  * 
7429  */
7430
7431 /**
7432  * @class Roo.bootstrap.TextArea
7433  * @extends Roo.bootstrap.Input
7434  * Bootstrap TextArea class
7435  * @cfg {Number} cols Specifies the visible width of a text area
7436  * @cfg {Number} rows Specifies the visible number of lines in a text area
7437  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
7438  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
7439  * @cfg {string} html text
7440  * 
7441  * @constructor
7442  * Create a new TextArea
7443  * @param {Object} config The config object
7444  */
7445
7446 Roo.bootstrap.TextArea = function(config){
7447     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
7448    
7449 };
7450
7451 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
7452      
7453     cols : false,
7454     rows : 5,
7455     readOnly : false,
7456     warp : 'soft',
7457     resize : false,
7458     value: false,
7459     html: false,
7460     
7461     getAutoCreate : function(){
7462         
7463         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7464         
7465         var id = Roo.id();
7466         
7467         var cfg = {};
7468         
7469         var input =  {
7470             tag: 'textarea',
7471             id : id,
7472             warp : this.warp,
7473             rows : this.rows,
7474             value : this.value || '',
7475             html: this.html || '',
7476             cls : 'form-control',
7477             placeholder : this.placeholder || '' 
7478             
7479         };
7480         
7481         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7482             input.maxLength = this.maxLength;
7483         }
7484         
7485         if(this.resize){
7486             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
7487         }
7488         
7489         if(this.cols){
7490             input.cols = this.cols;
7491         }
7492         
7493         if (this.readOnly) {
7494             input.readonly = true;
7495         }
7496         
7497         if (this.name) {
7498             input.name = this.name;
7499         }
7500         
7501         if (this.size) {
7502             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
7503         }
7504         
7505         var settings=this;
7506         ['xs','sm','md','lg'].map(function(size){
7507             if (settings[size]) {
7508                 cfg.cls += ' col-' + size + '-' + settings[size];
7509             }
7510         });
7511         
7512         var inputblock = input;
7513         
7514         if (this.before || this.after) {
7515             
7516             inputblock = {
7517                 cls : 'input-group',
7518                 cn :  [] 
7519             };
7520             if (this.before) {
7521                 inputblock.cn.push({
7522                     tag :'span',
7523                     cls : 'input-group-addon',
7524                     html : this.before
7525                 });
7526             }
7527             inputblock.cn.push(input);
7528             if (this.after) {
7529                 inputblock.cn.push({
7530                     tag :'span',
7531                     cls : 'input-group-addon',
7532                     html : this.after
7533                 });
7534             }
7535             
7536         }
7537         
7538         if (align ==='left' && this.fieldLabel.length) {
7539                 Roo.log("left and has label");
7540                 cfg.cn = [
7541                     
7542                     {
7543                         tag: 'label',
7544                         'for' :  id,
7545                         cls : 'control-label col-sm-' + this.labelWidth,
7546                         html : this.fieldLabel
7547                         
7548                     },
7549                     {
7550                         cls : "col-sm-" + (12 - this.labelWidth), 
7551                         cn: [
7552                             inputblock
7553                         ]
7554                     }
7555                     
7556                 ];
7557         } else if ( this.fieldLabel.length) {
7558                 Roo.log(" label");
7559                  cfg.cn = [
7560                    
7561                     {
7562                         tag: 'label',
7563                         //cls : 'input-group-addon',
7564                         html : this.fieldLabel
7565                         
7566                     },
7567                     
7568                     inputblock
7569                     
7570                 ];
7571
7572         } else {
7573             
7574                    Roo.log(" no label && no align");
7575                 cfg.cn = [
7576                     
7577                         inputblock
7578                     
7579                 ];
7580                 
7581                 
7582         }
7583         
7584         if (this.disabled) {
7585             input.disabled=true;
7586         }
7587         
7588         return cfg;
7589         
7590     },
7591     /**
7592      * return the real textarea element.
7593      */
7594     inputEl: function ()
7595     {
7596         return this.el.select('textarea.form-control',true).first();
7597     }
7598 });
7599
7600  
7601 /*
7602  * - LGPL
7603  *
7604  * trigger field - base class for combo..
7605  * 
7606  */
7607  
7608 /**
7609  * @class Roo.bootstrap.TriggerField
7610  * @extends Roo.bootstrap.Input
7611  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
7612  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
7613  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
7614  * for which you can provide a custom implementation.  For example:
7615  * <pre><code>
7616 var trigger = new Roo.bootstrap.TriggerField();
7617 trigger.onTriggerClick = myTriggerFn;
7618 trigger.applyTo('my-field');
7619 </code></pre>
7620  *
7621  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
7622  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
7623  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
7624  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
7625  * @constructor
7626  * Create a new TriggerField.
7627  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
7628  * to the base TextField)
7629  */
7630 Roo.bootstrap.TriggerField = function(config){
7631     this.mimicing = false;
7632     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
7633 };
7634
7635 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
7636     /**
7637      * @cfg {String} triggerClass A CSS class to apply to the trigger
7638      */
7639      /**
7640      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
7641      */
7642     hideTrigger:false,
7643
7644     /** @cfg {Boolean} grow @hide */
7645     /** @cfg {Number} growMin @hide */
7646     /** @cfg {Number} growMax @hide */
7647
7648     /**
7649      * @hide 
7650      * @method
7651      */
7652     autoSize: Roo.emptyFn,
7653     // private
7654     monitorTab : true,
7655     // private
7656     deferHeight : true,
7657
7658     
7659     actionMode : 'wrap',
7660     
7661     
7662     
7663     getAutoCreate : function(){
7664        
7665         var align = this.labelAlign || this.parentLabelAlign();
7666         
7667         var id = Roo.id();
7668         
7669         var cfg = {
7670             cls: 'form-group' //input-group
7671         };
7672         
7673         
7674         var input =  {
7675             tag: 'input',
7676             id : id,
7677             type : this.inputType,
7678             cls : 'form-control',
7679             autocomplete: 'off',
7680             placeholder : this.placeholder || '' 
7681             
7682         };
7683         if (this.name) {
7684             input.name = this.name;
7685         }
7686         if (this.size) {
7687             input.cls += ' input-' + this.size;
7688         }
7689         
7690         if (this.disabled) {
7691             input.disabled=true;
7692         }
7693         
7694         var inputblock = input;
7695         
7696         if (this.before || this.after) {
7697             
7698             inputblock = {
7699                 cls : 'input-group',
7700                 cn :  [] 
7701             };
7702             if (this.before) {
7703                 inputblock.cn.push({
7704                     tag :'span',
7705                     cls : 'input-group-addon',
7706                     html : this.before
7707                 });
7708             }
7709             inputblock.cn.push(input);
7710             if (this.after) {
7711                 inputblock.cn.push({
7712                     tag :'span',
7713                     cls : 'input-group-addon',
7714                     html : this.after
7715                 });
7716             }
7717             
7718         };
7719         
7720         var box = {
7721             tag: 'div',
7722             cn: [
7723                 {
7724                     tag: 'input',
7725                     type : 'hidden',
7726                     cls: 'form-hidden-field'
7727                 },
7728                 inputblock
7729             ]
7730             
7731         };
7732         
7733         if(this.multiple){
7734             Roo.log('multiple');
7735             
7736             box = {
7737                 tag: 'div',
7738                 cn: [
7739                     {
7740                         tag: 'input',
7741                         type : 'hidden',
7742                         cls: 'form-hidden-field'
7743                     },
7744                     {
7745                         tag: 'ul',
7746                         cls: 'select2-choices',
7747                         cn:[
7748                             {
7749                                 tag: 'li',
7750                                 cls: 'select2-search-field',
7751                                 cn: [
7752
7753                                     inputblock
7754                                 ]
7755                             }
7756                         ]
7757                     }
7758                 ]
7759             }
7760         };
7761         
7762         var combobox = {
7763             cls: 'select2-container input-group',
7764             cn: [
7765                 box
7766 //                {
7767 //                    tag: 'ul',
7768 //                    cls: 'typeahead typeahead-long dropdown-menu',
7769 //                    style: 'display:none'
7770 //                }
7771             ]
7772         };
7773         
7774         if(!this.multiple){
7775             combobox.cn.push({
7776                 tag :'span',
7777                 cls : 'input-group-addon btn dropdown-toggle',
7778                 cn : [
7779                     {
7780                         tag: 'span',
7781                         cls: 'caret'
7782                     },
7783                     {
7784                         tag: 'span',
7785                         cls: 'combobox-clear',
7786                         cn  : [
7787                             {
7788                                 tag : 'i',
7789                                 cls: 'icon-remove'
7790                             }
7791                         ]
7792                     }
7793                 ]
7794
7795             })
7796         }
7797         
7798         if(this.multiple){
7799             combobox.cls += ' select2-container-multi';
7800         }
7801         
7802         if (align ==='left' && this.fieldLabel.length) {
7803             
7804                 Roo.log("left and has label");
7805                 cfg.cn = [
7806                     
7807                     {
7808                         tag: 'label',
7809                         'for' :  id,
7810                         cls : 'control-label col-sm-' + this.labelWidth,
7811                         html : this.fieldLabel
7812                         
7813                     },
7814                     {
7815                         cls : "col-sm-" + (12 - this.labelWidth), 
7816                         cn: [
7817                             combobox
7818                         ]
7819                     }
7820                     
7821                 ];
7822         } else if ( this.fieldLabel.length) {
7823                 Roo.log(" label");
7824                  cfg.cn = [
7825                    
7826                     {
7827                         tag: 'label',
7828                         //cls : 'input-group-addon',
7829                         html : this.fieldLabel
7830                         
7831                     },
7832                     
7833                     combobox
7834                     
7835                 ];
7836
7837         } else {
7838             
7839                 Roo.log(" no label && no align");
7840                 cfg = combobox
7841                      
7842                 
7843         }
7844          
7845         var settings=this;
7846         ['xs','sm','md','lg'].map(function(size){
7847             if (settings[size]) {
7848                 cfg.cls += ' col-' + size + '-' + settings[size];
7849             }
7850         });
7851         
7852         return cfg;
7853         
7854     },
7855     
7856     
7857     
7858     // private
7859     onResize : function(w, h){
7860 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
7861 //        if(typeof w == 'number'){
7862 //            var x = w - this.trigger.getWidth();
7863 //            this.inputEl().setWidth(this.adjustWidth('input', x));
7864 //            this.trigger.setStyle('left', x+'px');
7865 //        }
7866     },
7867
7868     // private
7869     adjustSize : Roo.BoxComponent.prototype.adjustSize,
7870
7871     // private
7872     getResizeEl : function(){
7873         return this.inputEl();
7874     },
7875
7876     // private
7877     getPositionEl : function(){
7878         return this.inputEl();
7879     },
7880
7881     // private
7882     alignErrorIcon : function(){
7883         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
7884     },
7885
7886     // private
7887     initEvents : function(){
7888         
7889         this.createList();
7890         
7891         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
7892         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
7893         if(!this.multiple){
7894             this.trigger = this.el.select('span.dropdown-toggle',true).first();
7895             if(this.hideTrigger){
7896                 this.trigger.setDisplayed(false);
7897             }
7898             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
7899         }
7900         
7901         if(this.multiple){
7902             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
7903         }
7904         
7905         //this.trigger.addClassOnOver('x-form-trigger-over');
7906         //this.trigger.addClassOnClick('x-form-trigger-click');
7907         
7908         //if(!this.width){
7909         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
7910         //}
7911     },
7912     
7913     createList : function()
7914     {
7915         this.list = Roo.get(document.body).createChild({
7916             tag: 'ul',
7917             cls: 'typeahead typeahead-long dropdown-menu',
7918             style: 'display:none'
7919         });
7920         
7921         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
7922         
7923     },
7924
7925     // private
7926     initTrigger : function(){
7927        
7928     },
7929
7930     // private
7931     onDestroy : function(){
7932         if(this.trigger){
7933             this.trigger.removeAllListeners();
7934           //  this.trigger.remove();
7935         }
7936         //if(this.wrap){
7937         //    this.wrap.remove();
7938         //}
7939         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
7940     },
7941
7942     // private
7943     onFocus : function(){
7944         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
7945         /*
7946         if(!this.mimicing){
7947             this.wrap.addClass('x-trigger-wrap-focus');
7948             this.mimicing = true;
7949             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
7950             if(this.monitorTab){
7951                 this.el.on("keydown", this.checkTab, this);
7952             }
7953         }
7954         */
7955     },
7956
7957     // private
7958     checkTab : function(e){
7959         if(e.getKey() == e.TAB){
7960             this.triggerBlur();
7961         }
7962     },
7963
7964     // private
7965     onBlur : function(){
7966         // do nothing
7967     },
7968
7969     // private
7970     mimicBlur : function(e, t){
7971         /*
7972         if(!this.wrap.contains(t) && this.validateBlur()){
7973             this.triggerBlur();
7974         }
7975         */
7976     },
7977
7978     // private
7979     triggerBlur : function(){
7980         this.mimicing = false;
7981         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
7982         if(this.monitorTab){
7983             this.el.un("keydown", this.checkTab, this);
7984         }
7985         //this.wrap.removeClass('x-trigger-wrap-focus');
7986         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
7987     },
7988
7989     // private
7990     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
7991     validateBlur : function(e, t){
7992         return true;
7993     },
7994
7995     // private
7996     onDisable : function(){
7997         this.inputEl().dom.disabled = true;
7998         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
7999         //if(this.wrap){
8000         //    this.wrap.addClass('x-item-disabled');
8001         //}
8002     },
8003
8004     // private
8005     onEnable : function(){
8006         this.inputEl().dom.disabled = false;
8007         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
8008         //if(this.wrap){
8009         //    this.el.removeClass('x-item-disabled');
8010         //}
8011     },
8012
8013     // private
8014     onShow : function(){
8015         var ae = this.getActionEl();
8016         
8017         if(ae){
8018             ae.dom.style.display = '';
8019             ae.dom.style.visibility = 'visible';
8020         }
8021     },
8022
8023     // private
8024     
8025     onHide : function(){
8026         var ae = this.getActionEl();
8027         ae.dom.style.display = 'none';
8028     },
8029
8030     /**
8031      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
8032      * by an implementing function.
8033      * @method
8034      * @param {EventObject} e
8035      */
8036     onTriggerClick : Roo.emptyFn
8037 });
8038  /*
8039  * Based on:
8040  * Ext JS Library 1.1.1
8041  * Copyright(c) 2006-2007, Ext JS, LLC.
8042  *
8043  * Originally Released Under LGPL - original licence link has changed is not relivant.
8044  *
8045  * Fork - LGPL
8046  * <script type="text/javascript">
8047  */
8048
8049
8050 /**
8051  * @class Roo.data.SortTypes
8052  * @singleton
8053  * Defines the default sorting (casting?) comparison functions used when sorting data.
8054  */
8055 Roo.data.SortTypes = {
8056     /**
8057      * Default sort that does nothing
8058      * @param {Mixed} s The value being converted
8059      * @return {Mixed} The comparison value
8060      */
8061     none : function(s){
8062         return s;
8063     },
8064     
8065     /**
8066      * The regular expression used to strip tags
8067      * @type {RegExp}
8068      * @property
8069      */
8070     stripTagsRE : /<\/?[^>]+>/gi,
8071     
8072     /**
8073      * Strips all HTML tags to sort on text only
8074      * @param {Mixed} s The value being converted
8075      * @return {String} The comparison value
8076      */
8077     asText : function(s){
8078         return String(s).replace(this.stripTagsRE, "");
8079     },
8080     
8081     /**
8082      * Strips all HTML tags to sort on text only - Case insensitive
8083      * @param {Mixed} s The value being converted
8084      * @return {String} The comparison value
8085      */
8086     asUCText : function(s){
8087         return String(s).toUpperCase().replace(this.stripTagsRE, "");
8088     },
8089     
8090     /**
8091      * Case insensitive string
8092      * @param {Mixed} s The value being converted
8093      * @return {String} The comparison value
8094      */
8095     asUCString : function(s) {
8096         return String(s).toUpperCase();
8097     },
8098     
8099     /**
8100      * Date sorting
8101      * @param {Mixed} s The value being converted
8102      * @return {Number} The comparison value
8103      */
8104     asDate : function(s) {
8105         if(!s){
8106             return 0;
8107         }
8108         if(s instanceof Date){
8109             return s.getTime();
8110         }
8111         return Date.parse(String(s));
8112     },
8113     
8114     /**
8115      * Float sorting
8116      * @param {Mixed} s The value being converted
8117      * @return {Float} The comparison value
8118      */
8119     asFloat : function(s) {
8120         var val = parseFloat(String(s).replace(/,/g, ""));
8121         if(isNaN(val)) val = 0;
8122         return val;
8123     },
8124     
8125     /**
8126      * Integer sorting
8127      * @param {Mixed} s The value being converted
8128      * @return {Number} The comparison value
8129      */
8130     asInt : function(s) {
8131         var val = parseInt(String(s).replace(/,/g, ""));
8132         if(isNaN(val)) val = 0;
8133         return val;
8134     }
8135 };/*
8136  * Based on:
8137  * Ext JS Library 1.1.1
8138  * Copyright(c) 2006-2007, Ext JS, LLC.
8139  *
8140  * Originally Released Under LGPL - original licence link has changed is not relivant.
8141  *
8142  * Fork - LGPL
8143  * <script type="text/javascript">
8144  */
8145
8146 /**
8147 * @class Roo.data.Record
8148  * Instances of this class encapsulate both record <em>definition</em> information, and record
8149  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
8150  * to access Records cached in an {@link Roo.data.Store} object.<br>
8151  * <p>
8152  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
8153  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
8154  * objects.<br>
8155  * <p>
8156  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
8157  * @constructor
8158  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
8159  * {@link #create}. The parameters are the same.
8160  * @param {Array} data An associative Array of data values keyed by the field name.
8161  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
8162  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
8163  * not specified an integer id is generated.
8164  */
8165 Roo.data.Record = function(data, id){
8166     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
8167     this.data = data;
8168 };
8169
8170 /**
8171  * Generate a constructor for a specific record layout.
8172  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
8173  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
8174  * Each field definition object may contain the following properties: <ul>
8175  * <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,
8176  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
8177  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
8178  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
8179  * is being used, then this is a string containing the javascript expression to reference the data relative to 
8180  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
8181  * to the data item relative to the record element. If the mapping expression is the same as the field name,
8182  * this may be omitted.</p></li>
8183  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
8184  * <ul><li>auto (Default, implies no conversion)</li>
8185  * <li>string</li>
8186  * <li>int</li>
8187  * <li>float</li>
8188  * <li>boolean</li>
8189  * <li>date</li></ul></p></li>
8190  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
8191  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
8192  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
8193  * by the Reader into an object that will be stored in the Record. It is passed the
8194  * following parameters:<ul>
8195  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
8196  * </ul></p></li>
8197  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
8198  * </ul>
8199  * <br>usage:<br><pre><code>
8200 var TopicRecord = Roo.data.Record.create(
8201     {name: 'title', mapping: 'topic_title'},
8202     {name: 'author', mapping: 'username'},
8203     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
8204     {name: 'lastPost', mapping: 'post_time', type: 'date'},
8205     {name: 'lastPoster', mapping: 'user2'},
8206     {name: 'excerpt', mapping: 'post_text'}
8207 );
8208
8209 var myNewRecord = new TopicRecord({
8210     title: 'Do my job please',
8211     author: 'noobie',
8212     totalPosts: 1,
8213     lastPost: new Date(),
8214     lastPoster: 'Animal',
8215     excerpt: 'No way dude!'
8216 });
8217 myStore.add(myNewRecord);
8218 </code></pre>
8219  * @method create
8220  * @static
8221  */
8222 Roo.data.Record.create = function(o){
8223     var f = function(){
8224         f.superclass.constructor.apply(this, arguments);
8225     };
8226     Roo.extend(f, Roo.data.Record);
8227     var p = f.prototype;
8228     p.fields = new Roo.util.MixedCollection(false, function(field){
8229         return field.name;
8230     });
8231     for(var i = 0, len = o.length; i < len; i++){
8232         p.fields.add(new Roo.data.Field(o[i]));
8233     }
8234     f.getField = function(name){
8235         return p.fields.get(name);  
8236     };
8237     return f;
8238 };
8239
8240 Roo.data.Record.AUTO_ID = 1000;
8241 Roo.data.Record.EDIT = 'edit';
8242 Roo.data.Record.REJECT = 'reject';
8243 Roo.data.Record.COMMIT = 'commit';
8244
8245 Roo.data.Record.prototype = {
8246     /**
8247      * Readonly flag - true if this record has been modified.
8248      * @type Boolean
8249      */
8250     dirty : false,
8251     editing : false,
8252     error: null,
8253     modified: null,
8254
8255     // private
8256     join : function(store){
8257         this.store = store;
8258     },
8259
8260     /**
8261      * Set the named field to the specified value.
8262      * @param {String} name The name of the field to set.
8263      * @param {Object} value The value to set the field to.
8264      */
8265     set : function(name, value){
8266         if(this.data[name] == value){
8267             return;
8268         }
8269         this.dirty = true;
8270         if(!this.modified){
8271             this.modified = {};
8272         }
8273         if(typeof this.modified[name] == 'undefined'){
8274             this.modified[name] = this.data[name];
8275         }
8276         this.data[name] = value;
8277         if(!this.editing && this.store){
8278             this.store.afterEdit(this);
8279         }       
8280     },
8281
8282     /**
8283      * Get the value of the named field.
8284      * @param {String} name The name of the field to get the value of.
8285      * @return {Object} The value of the field.
8286      */
8287     get : function(name){
8288         return this.data[name]; 
8289     },
8290
8291     // private
8292     beginEdit : function(){
8293         this.editing = true;
8294         this.modified = {}; 
8295     },
8296
8297     // private
8298     cancelEdit : function(){
8299         this.editing = false;
8300         delete this.modified;
8301     },
8302
8303     // private
8304     endEdit : function(){
8305         this.editing = false;
8306         if(this.dirty && this.store){
8307             this.store.afterEdit(this);
8308         }
8309     },
8310
8311     /**
8312      * Usually called by the {@link Roo.data.Store} which owns the Record.
8313      * Rejects all changes made to the Record since either creation, or the last commit operation.
8314      * Modified fields are reverted to their original values.
8315      * <p>
8316      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8317      * of reject operations.
8318      */
8319     reject : function(){
8320         var m = this.modified;
8321         for(var n in m){
8322             if(typeof m[n] != "function"){
8323                 this.data[n] = m[n];
8324             }
8325         }
8326         this.dirty = false;
8327         delete this.modified;
8328         this.editing = false;
8329         if(this.store){
8330             this.store.afterReject(this);
8331         }
8332     },
8333
8334     /**
8335      * Usually called by the {@link Roo.data.Store} which owns the Record.
8336      * Commits all changes made to the Record since either creation, or the last commit operation.
8337      * <p>
8338      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8339      * of commit operations.
8340      */
8341     commit : function(){
8342         this.dirty = false;
8343         delete this.modified;
8344         this.editing = false;
8345         if(this.store){
8346             this.store.afterCommit(this);
8347         }
8348     },
8349
8350     // private
8351     hasError : function(){
8352         return this.error != null;
8353     },
8354
8355     // private
8356     clearError : function(){
8357         this.error = null;
8358     },
8359
8360     /**
8361      * Creates a copy of this record.
8362      * @param {String} id (optional) A new record id if you don't want to use this record's id
8363      * @return {Record}
8364      */
8365     copy : function(newId) {
8366         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
8367     }
8368 };/*
8369  * Based on:
8370  * Ext JS Library 1.1.1
8371  * Copyright(c) 2006-2007, Ext JS, LLC.
8372  *
8373  * Originally Released Under LGPL - original licence link has changed is not relivant.
8374  *
8375  * Fork - LGPL
8376  * <script type="text/javascript">
8377  */
8378
8379
8380
8381 /**
8382  * @class Roo.data.Store
8383  * @extends Roo.util.Observable
8384  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
8385  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
8386  * <p>
8387  * 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
8388  * has no knowledge of the format of the data returned by the Proxy.<br>
8389  * <p>
8390  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
8391  * instances from the data object. These records are cached and made available through accessor functions.
8392  * @constructor
8393  * Creates a new Store.
8394  * @param {Object} config A config object containing the objects needed for the Store to access data,
8395  * and read the data into Records.
8396  */
8397 Roo.data.Store = function(config){
8398     this.data = new Roo.util.MixedCollection(false);
8399     this.data.getKey = function(o){
8400         return o.id;
8401     };
8402     this.baseParams = {};
8403     // private
8404     this.paramNames = {
8405         "start" : "start",
8406         "limit" : "limit",
8407         "sort" : "sort",
8408         "dir" : "dir",
8409         "multisort" : "_multisort"
8410     };
8411
8412     if(config && config.data){
8413         this.inlineData = config.data;
8414         delete config.data;
8415     }
8416
8417     Roo.apply(this, config);
8418     
8419     if(this.reader){ // reader passed
8420         this.reader = Roo.factory(this.reader, Roo.data);
8421         this.reader.xmodule = this.xmodule || false;
8422         if(!this.recordType){
8423             this.recordType = this.reader.recordType;
8424         }
8425         if(this.reader.onMetaChange){
8426             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
8427         }
8428     }
8429
8430     if(this.recordType){
8431         this.fields = this.recordType.prototype.fields;
8432     }
8433     this.modified = [];
8434
8435     this.addEvents({
8436         /**
8437          * @event datachanged
8438          * Fires when the data cache has changed, and a widget which is using this Store
8439          * as a Record cache should refresh its view.
8440          * @param {Store} this
8441          */
8442         datachanged : true,
8443         /**
8444          * @event metachange
8445          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
8446          * @param {Store} this
8447          * @param {Object} meta The JSON metadata
8448          */
8449         metachange : true,
8450         /**
8451          * @event add
8452          * Fires when Records have been added to the Store
8453          * @param {Store} this
8454          * @param {Roo.data.Record[]} records The array of Records added
8455          * @param {Number} index The index at which the record(s) were added
8456          */
8457         add : true,
8458         /**
8459          * @event remove
8460          * Fires when a Record has been removed from the Store
8461          * @param {Store} this
8462          * @param {Roo.data.Record} record The Record that was removed
8463          * @param {Number} index The index at which the record was removed
8464          */
8465         remove : true,
8466         /**
8467          * @event update
8468          * Fires when a Record has been updated
8469          * @param {Store} this
8470          * @param {Roo.data.Record} record The Record that was updated
8471          * @param {String} operation The update operation being performed.  Value may be one of:
8472          * <pre><code>
8473  Roo.data.Record.EDIT
8474  Roo.data.Record.REJECT
8475  Roo.data.Record.COMMIT
8476          * </code></pre>
8477          */
8478         update : true,
8479         /**
8480          * @event clear
8481          * Fires when the data cache has been cleared.
8482          * @param {Store} this
8483          */
8484         clear : true,
8485         /**
8486          * @event beforeload
8487          * Fires before a request is made for a new data object.  If the beforeload handler returns false
8488          * the load action will be canceled.
8489          * @param {Store} this
8490          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8491          */
8492         beforeload : true,
8493         /**
8494          * @event beforeloadadd
8495          * Fires after a new set of Records has been loaded.
8496          * @param {Store} this
8497          * @param {Roo.data.Record[]} records The Records that were loaded
8498          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8499          */
8500         beforeloadadd : true,
8501         /**
8502          * @event load
8503          * Fires after a new set of Records has been loaded, before they are added to the store.
8504          * @param {Store} this
8505          * @param {Roo.data.Record[]} records The Records that were loaded
8506          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8507          * @params {Object} return from reader
8508          */
8509         load : true,
8510         /**
8511          * @event loadexception
8512          * Fires if an exception occurs in the Proxy during loading.
8513          * Called with the signature of the Proxy's "loadexception" event.
8514          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
8515          * 
8516          * @param {Proxy} 
8517          * @param {Object} return from JsonData.reader() - success, totalRecords, records
8518          * @param {Object} load options 
8519          * @param {Object} jsonData from your request (normally this contains the Exception)
8520          */
8521         loadexception : true
8522     });
8523     
8524     if(this.proxy){
8525         this.proxy = Roo.factory(this.proxy, Roo.data);
8526         this.proxy.xmodule = this.xmodule || false;
8527         this.relayEvents(this.proxy,  ["loadexception"]);
8528     }
8529     this.sortToggle = {};
8530     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
8531
8532     Roo.data.Store.superclass.constructor.call(this);
8533
8534     if(this.inlineData){
8535         this.loadData(this.inlineData);
8536         delete this.inlineData;
8537     }
8538 };
8539
8540 Roo.extend(Roo.data.Store, Roo.util.Observable, {
8541      /**
8542     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
8543     * without a remote query - used by combo/forms at present.
8544     */
8545     
8546     /**
8547     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
8548     */
8549     /**
8550     * @cfg {Array} data Inline data to be loaded when the store is initialized.
8551     */
8552     /**
8553     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
8554     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
8555     */
8556     /**
8557     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
8558     * on any HTTP request
8559     */
8560     /**
8561     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
8562     */
8563     /**
8564     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
8565     */
8566     multiSort: false,
8567     /**
8568     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
8569     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
8570     */
8571     remoteSort : false,
8572
8573     /**
8574     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
8575      * loaded or when a record is removed. (defaults to false).
8576     */
8577     pruneModifiedRecords : false,
8578
8579     // private
8580     lastOptions : null,
8581
8582     /**
8583      * Add Records to the Store and fires the add event.
8584      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8585      */
8586     add : function(records){
8587         records = [].concat(records);
8588         for(var i = 0, len = records.length; i < len; i++){
8589             records[i].join(this);
8590         }
8591         var index = this.data.length;
8592         this.data.addAll(records);
8593         this.fireEvent("add", this, records, index);
8594     },
8595
8596     /**
8597      * Remove a Record from the Store and fires the remove event.
8598      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
8599      */
8600     remove : function(record){
8601         var index = this.data.indexOf(record);
8602         this.data.removeAt(index);
8603         if(this.pruneModifiedRecords){
8604             this.modified.remove(record);
8605         }
8606         this.fireEvent("remove", this, record, index);
8607     },
8608
8609     /**
8610      * Remove all Records from the Store and fires the clear event.
8611      */
8612     removeAll : function(){
8613         this.data.clear();
8614         if(this.pruneModifiedRecords){
8615             this.modified = [];
8616         }
8617         this.fireEvent("clear", this);
8618     },
8619
8620     /**
8621      * Inserts Records to the Store at the given index and fires the add event.
8622      * @param {Number} index The start index at which to insert the passed Records.
8623      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8624      */
8625     insert : function(index, records){
8626         records = [].concat(records);
8627         for(var i = 0, len = records.length; i < len; i++){
8628             this.data.insert(index, records[i]);
8629             records[i].join(this);
8630         }
8631         this.fireEvent("add", this, records, index);
8632     },
8633
8634     /**
8635      * Get the index within the cache of the passed Record.
8636      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
8637      * @return {Number} The index of the passed Record. Returns -1 if not found.
8638      */
8639     indexOf : function(record){
8640         return this.data.indexOf(record);
8641     },
8642
8643     /**
8644      * Get the index within the cache of the Record with the passed id.
8645      * @param {String} id The id of the Record to find.
8646      * @return {Number} The index of the Record. Returns -1 if not found.
8647      */
8648     indexOfId : function(id){
8649         return this.data.indexOfKey(id);
8650     },
8651
8652     /**
8653      * Get the Record with the specified id.
8654      * @param {String} id The id of the Record to find.
8655      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
8656      */
8657     getById : function(id){
8658         return this.data.key(id);
8659     },
8660
8661     /**
8662      * Get the Record at the specified index.
8663      * @param {Number} index The index of the Record to find.
8664      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
8665      */
8666     getAt : function(index){
8667         return this.data.itemAt(index);
8668     },
8669
8670     /**
8671      * Returns a range of Records between specified indices.
8672      * @param {Number} startIndex (optional) The starting index (defaults to 0)
8673      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
8674      * @return {Roo.data.Record[]} An array of Records
8675      */
8676     getRange : function(start, end){
8677         return this.data.getRange(start, end);
8678     },
8679
8680     // private
8681     storeOptions : function(o){
8682         o = Roo.apply({}, o);
8683         delete o.callback;
8684         delete o.scope;
8685         this.lastOptions = o;
8686     },
8687
8688     /**
8689      * Loads the Record cache from the configured Proxy using the configured Reader.
8690      * <p>
8691      * If using remote paging, then the first load call must specify the <em>start</em>
8692      * and <em>limit</em> properties in the options.params property to establish the initial
8693      * position within the dataset, and the number of Records to cache on each read from the Proxy.
8694      * <p>
8695      * <strong>It is important to note that for remote data sources, loading is asynchronous,
8696      * and this call will return before the new data has been loaded. Perform any post-processing
8697      * in a callback function, or in a "load" event handler.</strong>
8698      * <p>
8699      * @param {Object} options An object containing properties which control loading options:<ul>
8700      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
8701      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
8702      * passed the following arguments:<ul>
8703      * <li>r : Roo.data.Record[]</li>
8704      * <li>options: Options object from the load call</li>
8705      * <li>success: Boolean success indicator</li></ul></li>
8706      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
8707      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
8708      * </ul>
8709      */
8710     load : function(options){
8711         options = options || {};
8712         if(this.fireEvent("beforeload", this, options) !== false){
8713             this.storeOptions(options);
8714             var p = Roo.apply(options.params || {}, this.baseParams);
8715             // if meta was not loaded from remote source.. try requesting it.
8716             if (!this.reader.metaFromRemote) {
8717                 p._requestMeta = 1;
8718             }
8719             if(this.sortInfo && this.remoteSort){
8720                 var pn = this.paramNames;
8721                 p[pn["sort"]] = this.sortInfo.field;
8722                 p[pn["dir"]] = this.sortInfo.direction;
8723             }
8724             if (this.multiSort) {
8725                 var pn = this.paramNames;
8726                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
8727             }
8728             
8729             this.proxy.load(p, this.reader, this.loadRecords, this, options);
8730         }
8731     },
8732
8733     /**
8734      * Reloads the Record cache from the configured Proxy using the configured Reader and
8735      * the options from the last load operation performed.
8736      * @param {Object} options (optional) An object containing properties which may override the options
8737      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
8738      * the most recently used options are reused).
8739      */
8740     reload : function(options){
8741         this.load(Roo.applyIf(options||{}, this.lastOptions));
8742     },
8743
8744     // private
8745     // Called as a callback by the Reader during a load operation.
8746     loadRecords : function(o, options, success){
8747         if(!o || success === false){
8748             if(success !== false){
8749                 this.fireEvent("load", this, [], options, o);
8750             }
8751             if(options.callback){
8752                 options.callback.call(options.scope || this, [], options, false);
8753             }
8754             return;
8755         }
8756         // if data returned failure - throw an exception.
8757         if (o.success === false) {
8758             // show a message if no listener is registered.
8759             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
8760                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
8761             }
8762             // loadmask wil be hooked into this..
8763             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
8764             return;
8765         }
8766         var r = o.records, t = o.totalRecords || r.length;
8767         
8768         this.fireEvent("beforeloadadd", this, r, options, o);
8769         
8770         if(!options || options.add !== true){
8771             if(this.pruneModifiedRecords){
8772                 this.modified = [];
8773             }
8774             for(var i = 0, len = r.length; i < len; i++){
8775                 r[i].join(this);
8776             }
8777             if(this.snapshot){
8778                 this.data = this.snapshot;
8779                 delete this.snapshot;
8780             }
8781             this.data.clear();
8782             this.data.addAll(r);
8783             this.totalLength = t;
8784             this.applySort();
8785             this.fireEvent("datachanged", this);
8786         }else{
8787             this.totalLength = Math.max(t, this.data.length+r.length);
8788             this.add(r);
8789         }
8790         this.fireEvent("load", this, r, options, o);
8791         if(options.callback){
8792             options.callback.call(options.scope || this, r, options, true);
8793         }
8794     },
8795
8796
8797     /**
8798      * Loads data from a passed data block. A Reader which understands the format of the data
8799      * must have been configured in the constructor.
8800      * @param {Object} data The data block from which to read the Records.  The format of the data expected
8801      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
8802      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
8803      */
8804     loadData : function(o, append){
8805         var r = this.reader.readRecords(o);
8806         this.loadRecords(r, {add: append}, true);
8807     },
8808
8809     /**
8810      * Gets the number of cached records.
8811      * <p>
8812      * <em>If using paging, this may not be the total size of the dataset. If the data object
8813      * used by the Reader contains the dataset size, then the getTotalCount() function returns
8814      * the data set size</em>
8815      */
8816     getCount : function(){
8817         return this.data.length || 0;
8818     },
8819
8820     /**
8821      * Gets the total number of records in the dataset as returned by the server.
8822      * <p>
8823      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
8824      * the dataset size</em>
8825      */
8826     getTotalCount : function(){
8827         return this.totalLength || 0;
8828     },
8829
8830     /**
8831      * Returns the sort state of the Store as an object with two properties:
8832      * <pre><code>
8833  field {String} The name of the field by which the Records are sorted
8834  direction {String} The sort order, "ASC" or "DESC"
8835      * </code></pre>
8836      */
8837     getSortState : function(){
8838         return this.sortInfo;
8839     },
8840
8841     // private
8842     applySort : function(){
8843         if(this.sortInfo && !this.remoteSort){
8844             var s = this.sortInfo, f = s.field;
8845             var st = this.fields.get(f).sortType;
8846             var fn = function(r1, r2){
8847                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
8848                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
8849             };
8850             this.data.sort(s.direction, fn);
8851             if(this.snapshot && this.snapshot != this.data){
8852                 this.snapshot.sort(s.direction, fn);
8853             }
8854         }
8855     },
8856
8857     /**
8858      * Sets the default sort column and order to be used by the next load operation.
8859      * @param {String} fieldName The name of the field to sort by.
8860      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
8861      */
8862     setDefaultSort : function(field, dir){
8863         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
8864     },
8865
8866     /**
8867      * Sort the Records.
8868      * If remote sorting is used, the sort is performed on the server, and the cache is
8869      * reloaded. If local sorting is used, the cache is sorted internally.
8870      * @param {String} fieldName The name of the field to sort by.
8871      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
8872      */
8873     sort : function(fieldName, dir){
8874         var f = this.fields.get(fieldName);
8875         if(!dir){
8876             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
8877             
8878             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
8879                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
8880             }else{
8881                 dir = f.sortDir;
8882             }
8883         }
8884         this.sortToggle[f.name] = dir;
8885         this.sortInfo = {field: f.name, direction: dir};
8886         if(!this.remoteSort){
8887             this.applySort();
8888             this.fireEvent("datachanged", this);
8889         }else{
8890             this.load(this.lastOptions);
8891         }
8892     },
8893
8894     /**
8895      * Calls the specified function for each of the Records in the cache.
8896      * @param {Function} fn The function to call. The Record is passed as the first parameter.
8897      * Returning <em>false</em> aborts and exits the iteration.
8898      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
8899      */
8900     each : function(fn, scope){
8901         this.data.each(fn, scope);
8902     },
8903
8904     /**
8905      * Gets all records modified since the last commit.  Modified records are persisted across load operations
8906      * (e.g., during paging).
8907      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
8908      */
8909     getModifiedRecords : function(){
8910         return this.modified;
8911     },
8912
8913     // private
8914     createFilterFn : function(property, value, anyMatch){
8915         if(!value.exec){ // not a regex
8916             value = String(value);
8917             if(value.length == 0){
8918                 return false;
8919             }
8920             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
8921         }
8922         return function(r){
8923             return value.test(r.data[property]);
8924         };
8925     },
8926
8927     /**
8928      * Sums the value of <i>property</i> for each record between start and end and returns the result.
8929      * @param {String} property A field on your records
8930      * @param {Number} start The record index to start at (defaults to 0)
8931      * @param {Number} end The last record index to include (defaults to length - 1)
8932      * @return {Number} The sum
8933      */
8934     sum : function(property, start, end){
8935         var rs = this.data.items, v = 0;
8936         start = start || 0;
8937         end = (end || end === 0) ? end : rs.length-1;
8938
8939         for(var i = start; i <= end; i++){
8940             v += (rs[i].data[property] || 0);
8941         }
8942         return v;
8943     },
8944
8945     /**
8946      * Filter the records by a specified property.
8947      * @param {String} field A field on your records
8948      * @param {String/RegExp} value Either a string that the field
8949      * should start with or a RegExp to test against the field
8950      * @param {Boolean} anyMatch True to match any part not just the beginning
8951      */
8952     filter : function(property, value, anyMatch){
8953         var fn = this.createFilterFn(property, value, anyMatch);
8954         return fn ? this.filterBy(fn) : this.clearFilter();
8955     },
8956
8957     /**
8958      * Filter by a function. The specified function will be called with each
8959      * record in this data source. If the function returns true the record is included,
8960      * otherwise it is filtered.
8961      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
8962      * @param {Object} scope (optional) The scope of the function (defaults to this)
8963      */
8964     filterBy : function(fn, scope){
8965         this.snapshot = this.snapshot || this.data;
8966         this.data = this.queryBy(fn, scope||this);
8967         this.fireEvent("datachanged", this);
8968     },
8969
8970     /**
8971      * Query the records by a specified property.
8972      * @param {String} field A field on your records
8973      * @param {String/RegExp} value Either a string that the field
8974      * should start with or a RegExp to test against the field
8975      * @param {Boolean} anyMatch True to match any part not just the beginning
8976      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
8977      */
8978     query : function(property, value, anyMatch){
8979         var fn = this.createFilterFn(property, value, anyMatch);
8980         return fn ? this.queryBy(fn) : this.data.clone();
8981     },
8982
8983     /**
8984      * Query by a function. The specified function will be called with each
8985      * record in this data source. If the function returns true the record is included
8986      * in the results.
8987      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
8988      * @param {Object} scope (optional) The scope of the function (defaults to this)
8989       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
8990      **/
8991     queryBy : function(fn, scope){
8992         var data = this.snapshot || this.data;
8993         return data.filterBy(fn, scope||this);
8994     },
8995
8996     /**
8997      * Collects unique values for a particular dataIndex from this store.
8998      * @param {String} dataIndex The property to collect
8999      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
9000      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
9001      * @return {Array} An array of the unique values
9002      **/
9003     collect : function(dataIndex, allowNull, bypassFilter){
9004         var d = (bypassFilter === true && this.snapshot) ?
9005                 this.snapshot.items : this.data.items;
9006         var v, sv, r = [], l = {};
9007         for(var i = 0, len = d.length; i < len; i++){
9008             v = d[i].data[dataIndex];
9009             sv = String(v);
9010             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
9011                 l[sv] = true;
9012                 r[r.length] = v;
9013             }
9014         }
9015         return r;
9016     },
9017
9018     /**
9019      * Revert to a view of the Record cache with no filtering applied.
9020      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
9021      */
9022     clearFilter : function(suppressEvent){
9023         if(this.snapshot && this.snapshot != this.data){
9024             this.data = this.snapshot;
9025             delete this.snapshot;
9026             if(suppressEvent !== true){
9027                 this.fireEvent("datachanged", this);
9028             }
9029         }
9030     },
9031
9032     // private
9033     afterEdit : function(record){
9034         if(this.modified.indexOf(record) == -1){
9035             this.modified.push(record);
9036         }
9037         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
9038     },
9039     
9040     // private
9041     afterReject : function(record){
9042         this.modified.remove(record);
9043         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
9044     },
9045
9046     // private
9047     afterCommit : function(record){
9048         this.modified.remove(record);
9049         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
9050     },
9051
9052     /**
9053      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
9054      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
9055      */
9056     commitChanges : function(){
9057         var m = this.modified.slice(0);
9058         this.modified = [];
9059         for(var i = 0, len = m.length; i < len; i++){
9060             m[i].commit();
9061         }
9062     },
9063
9064     /**
9065      * Cancel outstanding changes on all changed records.
9066      */
9067     rejectChanges : function(){
9068         var m = this.modified.slice(0);
9069         this.modified = [];
9070         for(var i = 0, len = m.length; i < len; i++){
9071             m[i].reject();
9072         }
9073     },
9074
9075     onMetaChange : function(meta, rtype, o){
9076         this.recordType = rtype;
9077         this.fields = rtype.prototype.fields;
9078         delete this.snapshot;
9079         this.sortInfo = meta.sortInfo || this.sortInfo;
9080         this.modified = [];
9081         this.fireEvent('metachange', this, this.reader.meta);
9082     },
9083     
9084     moveIndex : function(data, type)
9085     {
9086         var index = this.indexOf(data);
9087         
9088         var newIndex = index + type;
9089         
9090         this.remove(data);
9091         
9092         this.insert(newIndex, data);
9093         
9094     }
9095 });/*
9096  * Based on:
9097  * Ext JS Library 1.1.1
9098  * Copyright(c) 2006-2007, Ext JS, LLC.
9099  *
9100  * Originally Released Under LGPL - original licence link has changed is not relivant.
9101  *
9102  * Fork - LGPL
9103  * <script type="text/javascript">
9104  */
9105
9106 /**
9107  * @class Roo.data.SimpleStore
9108  * @extends Roo.data.Store
9109  * Small helper class to make creating Stores from Array data easier.
9110  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
9111  * @cfg {Array} fields An array of field definition objects, or field name strings.
9112  * @cfg {Array} data The multi-dimensional array of data
9113  * @constructor
9114  * @param {Object} config
9115  */
9116 Roo.data.SimpleStore = function(config){
9117     Roo.data.SimpleStore.superclass.constructor.call(this, {
9118         isLocal : true,
9119         reader: new Roo.data.ArrayReader({
9120                 id: config.id
9121             },
9122             Roo.data.Record.create(config.fields)
9123         ),
9124         proxy : new Roo.data.MemoryProxy(config.data)
9125     });
9126     this.load();
9127 };
9128 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
9129  * Based on:
9130  * Ext JS Library 1.1.1
9131  * Copyright(c) 2006-2007, Ext JS, LLC.
9132  *
9133  * Originally Released Under LGPL - original licence link has changed is not relivant.
9134  *
9135  * Fork - LGPL
9136  * <script type="text/javascript">
9137  */
9138
9139 /**
9140 /**
9141  * @extends Roo.data.Store
9142  * @class Roo.data.JsonStore
9143  * Small helper class to make creating Stores for JSON data easier. <br/>
9144 <pre><code>
9145 var store = new Roo.data.JsonStore({
9146     url: 'get-images.php',
9147     root: 'images',
9148     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
9149 });
9150 </code></pre>
9151  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
9152  * JsonReader and HttpProxy (unless inline data is provided).</b>
9153  * @cfg {Array} fields An array of field definition objects, or field name strings.
9154  * @constructor
9155  * @param {Object} config
9156  */
9157 Roo.data.JsonStore = function(c){
9158     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
9159         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
9160         reader: new Roo.data.JsonReader(c, c.fields)
9161     }));
9162 };
9163 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
9164  * Based on:
9165  * Ext JS Library 1.1.1
9166  * Copyright(c) 2006-2007, Ext JS, LLC.
9167  *
9168  * Originally Released Under LGPL - original licence link has changed is not relivant.
9169  *
9170  * Fork - LGPL
9171  * <script type="text/javascript">
9172  */
9173
9174  
9175 Roo.data.Field = function(config){
9176     if(typeof config == "string"){
9177         config = {name: config};
9178     }
9179     Roo.apply(this, config);
9180     
9181     if(!this.type){
9182         this.type = "auto";
9183     }
9184     
9185     var st = Roo.data.SortTypes;
9186     // named sortTypes are supported, here we look them up
9187     if(typeof this.sortType == "string"){
9188         this.sortType = st[this.sortType];
9189     }
9190     
9191     // set default sortType for strings and dates
9192     if(!this.sortType){
9193         switch(this.type){
9194             case "string":
9195                 this.sortType = st.asUCString;
9196                 break;
9197             case "date":
9198                 this.sortType = st.asDate;
9199                 break;
9200             default:
9201                 this.sortType = st.none;
9202         }
9203     }
9204
9205     // define once
9206     var stripRe = /[\$,%]/g;
9207
9208     // prebuilt conversion function for this field, instead of
9209     // switching every time we're reading a value
9210     if(!this.convert){
9211         var cv, dateFormat = this.dateFormat;
9212         switch(this.type){
9213             case "":
9214             case "auto":
9215             case undefined:
9216                 cv = function(v){ return v; };
9217                 break;
9218             case "string":
9219                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
9220                 break;
9221             case "int":
9222                 cv = function(v){
9223                     return v !== undefined && v !== null && v !== '' ?
9224                            parseInt(String(v).replace(stripRe, ""), 10) : '';
9225                     };
9226                 break;
9227             case "float":
9228                 cv = function(v){
9229                     return v !== undefined && v !== null && v !== '' ?
9230                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
9231                     };
9232                 break;
9233             case "bool":
9234             case "boolean":
9235                 cv = function(v){ return v === true || v === "true" || v == 1; };
9236                 break;
9237             case "date":
9238                 cv = function(v){
9239                     if(!v){
9240                         return '';
9241                     }
9242                     if(v instanceof Date){
9243                         return v;
9244                     }
9245                     if(dateFormat){
9246                         if(dateFormat == "timestamp"){
9247                             return new Date(v*1000);
9248                         }
9249                         return Date.parseDate(v, dateFormat);
9250                     }
9251                     var parsed = Date.parse(v);
9252                     return parsed ? new Date(parsed) : null;
9253                 };
9254              break;
9255             
9256         }
9257         this.convert = cv;
9258     }
9259 };
9260
9261 Roo.data.Field.prototype = {
9262     dateFormat: null,
9263     defaultValue: "",
9264     mapping: null,
9265     sortType : null,
9266     sortDir : "ASC"
9267 };/*
9268  * Based on:
9269  * Ext JS Library 1.1.1
9270  * Copyright(c) 2006-2007, Ext JS, LLC.
9271  *
9272  * Originally Released Under LGPL - original licence link has changed is not relivant.
9273  *
9274  * Fork - LGPL
9275  * <script type="text/javascript">
9276  */
9277  
9278 // Base class for reading structured data from a data source.  This class is intended to be
9279 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
9280
9281 /**
9282  * @class Roo.data.DataReader
9283  * Base class for reading structured data from a data source.  This class is intended to be
9284  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
9285  */
9286
9287 Roo.data.DataReader = function(meta, recordType){
9288     
9289     this.meta = meta;
9290     
9291     this.recordType = recordType instanceof Array ? 
9292         Roo.data.Record.create(recordType) : recordType;
9293 };
9294
9295 Roo.data.DataReader.prototype = {
9296      /**
9297      * Create an empty record
9298      * @param {Object} data (optional) - overlay some values
9299      * @return {Roo.data.Record} record created.
9300      */
9301     newRow :  function(d) {
9302         var da =  {};
9303         this.recordType.prototype.fields.each(function(c) {
9304             switch( c.type) {
9305                 case 'int' : da[c.name] = 0; break;
9306                 case 'date' : da[c.name] = new Date(); break;
9307                 case 'float' : da[c.name] = 0.0; break;
9308                 case 'boolean' : da[c.name] = false; break;
9309                 default : da[c.name] = ""; break;
9310             }
9311             
9312         });
9313         return new this.recordType(Roo.apply(da, d));
9314     }
9315     
9316 };/*
9317  * Based on:
9318  * Ext JS Library 1.1.1
9319  * Copyright(c) 2006-2007, Ext JS, LLC.
9320  *
9321  * Originally Released Under LGPL - original licence link has changed is not relivant.
9322  *
9323  * Fork - LGPL
9324  * <script type="text/javascript">
9325  */
9326
9327 /**
9328  * @class Roo.data.DataProxy
9329  * @extends Roo.data.Observable
9330  * This class is an abstract base class for implementations which provide retrieval of
9331  * unformatted data objects.<br>
9332  * <p>
9333  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
9334  * (of the appropriate type which knows how to parse the data object) to provide a block of
9335  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
9336  * <p>
9337  * Custom implementations must implement the load method as described in
9338  * {@link Roo.data.HttpProxy#load}.
9339  */
9340 Roo.data.DataProxy = function(){
9341     this.addEvents({
9342         /**
9343          * @event beforeload
9344          * Fires before a network request is made to retrieve a data object.
9345          * @param {Object} This DataProxy object.
9346          * @param {Object} params The params parameter to the load function.
9347          */
9348         beforeload : true,
9349         /**
9350          * @event load
9351          * Fires before the load method's callback is called.
9352          * @param {Object} This DataProxy object.
9353          * @param {Object} o The data object.
9354          * @param {Object} arg The callback argument object passed to the load function.
9355          */
9356         load : true,
9357         /**
9358          * @event loadexception
9359          * Fires if an Exception occurs during data retrieval.
9360          * @param {Object} This DataProxy object.
9361          * @param {Object} o The data object.
9362          * @param {Object} arg The callback argument object passed to the load function.
9363          * @param {Object} e The Exception.
9364          */
9365         loadexception : true
9366     });
9367     Roo.data.DataProxy.superclass.constructor.call(this);
9368 };
9369
9370 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
9371
9372     /**
9373      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
9374      */
9375 /*
9376  * Based on:
9377  * Ext JS Library 1.1.1
9378  * Copyright(c) 2006-2007, Ext JS, LLC.
9379  *
9380  * Originally Released Under LGPL - original licence link has changed is not relivant.
9381  *
9382  * Fork - LGPL
9383  * <script type="text/javascript">
9384  */
9385 /**
9386  * @class Roo.data.MemoryProxy
9387  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
9388  * to the Reader when its load method is called.
9389  * @constructor
9390  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
9391  */
9392 Roo.data.MemoryProxy = function(data){
9393     if (data.data) {
9394         data = data.data;
9395     }
9396     Roo.data.MemoryProxy.superclass.constructor.call(this);
9397     this.data = data;
9398 };
9399
9400 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
9401     /**
9402      * Load data from the requested source (in this case an in-memory
9403      * data object passed to the constructor), read the data object into
9404      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9405      * process that block using the passed callback.
9406      * @param {Object} params This parameter is not used by the MemoryProxy class.
9407      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9408      * object into a block of Roo.data.Records.
9409      * @param {Function} callback The function into which to pass the block of Roo.data.records.
9410      * The function must be passed <ul>
9411      * <li>The Record block object</li>
9412      * <li>The "arg" argument from the load function</li>
9413      * <li>A boolean success indicator</li>
9414      * </ul>
9415      * @param {Object} scope The scope in which to call the callback
9416      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9417      */
9418     load : function(params, reader, callback, scope, arg){
9419         params = params || {};
9420         var result;
9421         try {
9422             result = reader.readRecords(this.data);
9423         }catch(e){
9424             this.fireEvent("loadexception", this, arg, null, e);
9425             callback.call(scope, null, arg, false);
9426             return;
9427         }
9428         callback.call(scope, result, arg, true);
9429     },
9430     
9431     // private
9432     update : function(params, records){
9433         
9434     }
9435 });/*
9436  * Based on:
9437  * Ext JS Library 1.1.1
9438  * Copyright(c) 2006-2007, Ext JS, LLC.
9439  *
9440  * Originally Released Under LGPL - original licence link has changed is not relivant.
9441  *
9442  * Fork - LGPL
9443  * <script type="text/javascript">
9444  */
9445 /**
9446  * @class Roo.data.HttpProxy
9447  * @extends Roo.data.DataProxy
9448  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
9449  * configured to reference a certain URL.<br><br>
9450  * <p>
9451  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
9452  * from which the running page was served.<br><br>
9453  * <p>
9454  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
9455  * <p>
9456  * Be aware that to enable the browser to parse an XML document, the server must set
9457  * the Content-Type header in the HTTP response to "text/xml".
9458  * @constructor
9459  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
9460  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
9461  * will be used to make the request.
9462  */
9463 Roo.data.HttpProxy = function(conn){
9464     Roo.data.HttpProxy.superclass.constructor.call(this);
9465     // is conn a conn config or a real conn?
9466     this.conn = conn;
9467     this.useAjax = !conn || !conn.events;
9468   
9469 };
9470
9471 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
9472     // thse are take from connection...
9473     
9474     /**
9475      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
9476      */
9477     /**
9478      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
9479      * extra parameters to each request made by this object. (defaults to undefined)
9480      */
9481     /**
9482      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
9483      *  to each request made by this object. (defaults to undefined)
9484      */
9485     /**
9486      * @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)
9487      */
9488     /**
9489      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
9490      */
9491      /**
9492      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
9493      * @type Boolean
9494      */
9495   
9496
9497     /**
9498      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
9499      * @type Boolean
9500      */
9501     /**
9502      * Return the {@link Roo.data.Connection} object being used by this Proxy.
9503      * @return {Connection} The Connection object. This object may be used to subscribe to events on
9504      * a finer-grained basis than the DataProxy events.
9505      */
9506     getConnection : function(){
9507         return this.useAjax ? Roo.Ajax : this.conn;
9508     },
9509
9510     /**
9511      * Load data from the configured {@link Roo.data.Connection}, read the data object into
9512      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
9513      * process that block using the passed callback.
9514      * @param {Object} params An object containing properties which are to be used as HTTP parameters
9515      * for the request to the remote server.
9516      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9517      * object into a block of Roo.data.Records.
9518      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9519      * The function must be passed <ul>
9520      * <li>The Record block object</li>
9521      * <li>The "arg" argument from the load function</li>
9522      * <li>A boolean success indicator</li>
9523      * </ul>
9524      * @param {Object} scope The scope in which to call the callback
9525      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9526      */
9527     load : function(params, reader, callback, scope, arg){
9528         if(this.fireEvent("beforeload", this, params) !== false){
9529             var  o = {
9530                 params : params || {},
9531                 request: {
9532                     callback : callback,
9533                     scope : scope,
9534                     arg : arg
9535                 },
9536                 reader: reader,
9537                 callback : this.loadResponse,
9538                 scope: this
9539             };
9540             if(this.useAjax){
9541                 Roo.applyIf(o, this.conn);
9542                 if(this.activeRequest){
9543                     Roo.Ajax.abort(this.activeRequest);
9544                 }
9545                 this.activeRequest = Roo.Ajax.request(o);
9546             }else{
9547                 this.conn.request(o);
9548             }
9549         }else{
9550             callback.call(scope||this, null, arg, false);
9551         }
9552     },
9553
9554     // private
9555     loadResponse : function(o, success, response){
9556         delete this.activeRequest;
9557         if(!success){
9558             this.fireEvent("loadexception", this, o, response);
9559             o.request.callback.call(o.request.scope, null, o.request.arg, false);
9560             return;
9561         }
9562         var result;
9563         try {
9564             result = o.reader.read(response);
9565         }catch(e){
9566             this.fireEvent("loadexception", this, o, response, e);
9567             o.request.callback.call(o.request.scope, null, o.request.arg, false);
9568             return;
9569         }
9570         
9571         this.fireEvent("load", this, o, o.request.arg);
9572         o.request.callback.call(o.request.scope, result, o.request.arg, true);
9573     },
9574
9575     // private
9576     update : function(dataSet){
9577
9578     },
9579
9580     // private
9581     updateResponse : function(dataSet){
9582
9583     }
9584 });/*
9585  * Based on:
9586  * Ext JS Library 1.1.1
9587  * Copyright(c) 2006-2007, Ext JS, LLC.
9588  *
9589  * Originally Released Under LGPL - original licence link has changed is not relivant.
9590  *
9591  * Fork - LGPL
9592  * <script type="text/javascript">
9593  */
9594
9595 /**
9596  * @class Roo.data.ScriptTagProxy
9597  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
9598  * other than the originating domain of the running page.<br><br>
9599  * <p>
9600  * <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
9601  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
9602  * <p>
9603  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
9604  * source code that is used as the source inside a &lt;script> tag.<br><br>
9605  * <p>
9606  * In order for the browser to process the returned data, the server must wrap the data object
9607  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
9608  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
9609  * depending on whether the callback name was passed:
9610  * <p>
9611  * <pre><code>
9612 boolean scriptTag = false;
9613 String cb = request.getParameter("callback");
9614 if (cb != null) {
9615     scriptTag = true;
9616     response.setContentType("text/javascript");
9617 } else {
9618     response.setContentType("application/x-json");
9619 }
9620 Writer out = response.getWriter();
9621 if (scriptTag) {
9622     out.write(cb + "(");
9623 }
9624 out.print(dataBlock.toJsonString());
9625 if (scriptTag) {
9626     out.write(");");
9627 }
9628 </pre></code>
9629  *
9630  * @constructor
9631  * @param {Object} config A configuration object.
9632  */
9633 Roo.data.ScriptTagProxy = function(config){
9634     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
9635     Roo.apply(this, config);
9636     this.head = document.getElementsByTagName("head")[0];
9637 };
9638
9639 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
9640
9641 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
9642     /**
9643      * @cfg {String} url The URL from which to request the data object.
9644      */
9645     /**
9646      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
9647      */
9648     timeout : 30000,
9649     /**
9650      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
9651      * the server the name of the callback function set up by the load call to process the returned data object.
9652      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
9653      * javascript output which calls this named function passing the data object as its only parameter.
9654      */
9655     callbackParam : "callback",
9656     /**
9657      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
9658      * name to the request.
9659      */
9660     nocache : true,
9661
9662     /**
9663      * Load data from the configured URL, read the data object into
9664      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9665      * process that block using the passed callback.
9666      * @param {Object} params An object containing properties which are to be used as HTTP parameters
9667      * for the request to the remote server.
9668      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9669      * object into a block of Roo.data.Records.
9670      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9671      * The function must be passed <ul>
9672      * <li>The Record block object</li>
9673      * <li>The "arg" argument from the load function</li>
9674      * <li>A boolean success indicator</li>
9675      * </ul>
9676      * @param {Object} scope The scope in which to call the callback
9677      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9678      */
9679     load : function(params, reader, callback, scope, arg){
9680         if(this.fireEvent("beforeload", this, params) !== false){
9681
9682             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
9683
9684             var url = this.url;
9685             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
9686             if(this.nocache){
9687                 url += "&_dc=" + (new Date().getTime());
9688             }
9689             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
9690             var trans = {
9691                 id : transId,
9692                 cb : "stcCallback"+transId,
9693                 scriptId : "stcScript"+transId,
9694                 params : params,
9695                 arg : arg,
9696                 url : url,
9697                 callback : callback,
9698                 scope : scope,
9699                 reader : reader
9700             };
9701             var conn = this;
9702
9703             window[trans.cb] = function(o){
9704                 conn.handleResponse(o, trans);
9705             };
9706
9707             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
9708
9709             if(this.autoAbort !== false){
9710                 this.abort();
9711             }
9712
9713             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
9714
9715             var script = document.createElement("script");
9716             script.setAttribute("src", url);
9717             script.setAttribute("type", "text/javascript");
9718             script.setAttribute("id", trans.scriptId);
9719             this.head.appendChild(script);
9720
9721             this.trans = trans;
9722         }else{
9723             callback.call(scope||this, null, arg, false);
9724         }
9725     },
9726
9727     // private
9728     isLoading : function(){
9729         return this.trans ? true : false;
9730     },
9731
9732     /**
9733      * Abort the current server request.
9734      */
9735     abort : function(){
9736         if(this.isLoading()){
9737             this.destroyTrans(this.trans);
9738         }
9739     },
9740
9741     // private
9742     destroyTrans : function(trans, isLoaded){
9743         this.head.removeChild(document.getElementById(trans.scriptId));
9744         clearTimeout(trans.timeoutId);
9745         if(isLoaded){
9746             window[trans.cb] = undefined;
9747             try{
9748                 delete window[trans.cb];
9749             }catch(e){}
9750         }else{
9751             // if hasn't been loaded, wait for load to remove it to prevent script error
9752             window[trans.cb] = function(){
9753                 window[trans.cb] = undefined;
9754                 try{
9755                     delete window[trans.cb];
9756                 }catch(e){}
9757             };
9758         }
9759     },
9760
9761     // private
9762     handleResponse : function(o, trans){
9763         this.trans = false;
9764         this.destroyTrans(trans, true);
9765         var result;
9766         try {
9767             result = trans.reader.readRecords(o);
9768         }catch(e){
9769             this.fireEvent("loadexception", this, o, trans.arg, e);
9770             trans.callback.call(trans.scope||window, null, trans.arg, false);
9771             return;
9772         }
9773         this.fireEvent("load", this, o, trans.arg);
9774         trans.callback.call(trans.scope||window, result, trans.arg, true);
9775     },
9776
9777     // private
9778     handleFailure : function(trans){
9779         this.trans = false;
9780         this.destroyTrans(trans, false);
9781         this.fireEvent("loadexception", this, null, trans.arg);
9782         trans.callback.call(trans.scope||window, null, trans.arg, false);
9783     }
9784 });/*
9785  * Based on:
9786  * Ext JS Library 1.1.1
9787  * Copyright(c) 2006-2007, Ext JS, LLC.
9788  *
9789  * Originally Released Under LGPL - original licence link has changed is not relivant.
9790  *
9791  * Fork - LGPL
9792  * <script type="text/javascript">
9793  */
9794
9795 /**
9796  * @class Roo.data.JsonReader
9797  * @extends Roo.data.DataReader
9798  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
9799  * based on mappings in a provided Roo.data.Record constructor.
9800  * 
9801  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
9802  * in the reply previously. 
9803  * 
9804  * <p>
9805  * Example code:
9806  * <pre><code>
9807 var RecordDef = Roo.data.Record.create([
9808     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
9809     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
9810 ]);
9811 var myReader = new Roo.data.JsonReader({
9812     totalProperty: "results",    // The property which contains the total dataset size (optional)
9813     root: "rows",                // The property which contains an Array of row objects
9814     id: "id"                     // The property within each row object that provides an ID for the record (optional)
9815 }, RecordDef);
9816 </code></pre>
9817  * <p>
9818  * This would consume a JSON file like this:
9819  * <pre><code>
9820 { 'results': 2, 'rows': [
9821     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
9822     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
9823 }
9824 </code></pre>
9825  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
9826  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
9827  * paged from the remote server.
9828  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
9829  * @cfg {String} root name of the property which contains the Array of row objects.
9830  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
9831  * @constructor
9832  * Create a new JsonReader
9833  * @param {Object} meta Metadata configuration options
9834  * @param {Object} recordType Either an Array of field definition objects,
9835  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
9836  */
9837 Roo.data.JsonReader = function(meta, recordType){
9838     
9839     meta = meta || {};
9840     // set some defaults:
9841     Roo.applyIf(meta, {
9842         totalProperty: 'total',
9843         successProperty : 'success',
9844         root : 'data',
9845         id : 'id'
9846     });
9847     
9848     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
9849 };
9850 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
9851     
9852     /**
9853      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
9854      * Used by Store query builder to append _requestMeta to params.
9855      * 
9856      */
9857     metaFromRemote : false,
9858     /**
9859      * This method is only used by a DataProxy which has retrieved data from a remote server.
9860      * @param {Object} response The XHR object which contains the JSON data in its responseText.
9861      * @return {Object} data A data block which is used by an Roo.data.Store object as
9862      * a cache of Roo.data.Records.
9863      */
9864     read : function(response){
9865         var json = response.responseText;
9866        
9867         var o = /* eval:var:o */ eval("("+json+")");
9868         if(!o) {
9869             throw {message: "JsonReader.read: Json object not found"};
9870         }
9871         
9872         if(o.metaData){
9873             
9874             delete this.ef;
9875             this.metaFromRemote = true;
9876             this.meta = o.metaData;
9877             this.recordType = Roo.data.Record.create(o.metaData.fields);
9878             this.onMetaChange(this.meta, this.recordType, o);
9879         }
9880         return this.readRecords(o);
9881     },
9882
9883     // private function a store will implement
9884     onMetaChange : function(meta, recordType, o){
9885
9886     },
9887
9888     /**
9889          * @ignore
9890          */
9891     simpleAccess: function(obj, subsc) {
9892         return obj[subsc];
9893     },
9894
9895         /**
9896          * @ignore
9897          */
9898     getJsonAccessor: function(){
9899         var re = /[\[\.]/;
9900         return function(expr) {
9901             try {
9902                 return(re.test(expr))
9903                     ? new Function("obj", "return obj." + expr)
9904                     : function(obj){
9905                         return obj[expr];
9906                     };
9907             } catch(e){}
9908             return Roo.emptyFn;
9909         };
9910     }(),
9911
9912     /**
9913      * Create a data block containing Roo.data.Records from an XML document.
9914      * @param {Object} o An object which contains an Array of row objects in the property specified
9915      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
9916      * which contains the total size of the dataset.
9917      * @return {Object} data A data block which is used by an Roo.data.Store object as
9918      * a cache of Roo.data.Records.
9919      */
9920     readRecords : function(o){
9921         /**
9922          * After any data loads, the raw JSON data is available for further custom processing.
9923          * @type Object
9924          */
9925         this.o = o;
9926         var s = this.meta, Record = this.recordType,
9927             f = Record.prototype.fields, fi = f.items, fl = f.length;
9928
9929 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
9930         if (!this.ef) {
9931             if(s.totalProperty) {
9932                     this.getTotal = this.getJsonAccessor(s.totalProperty);
9933                 }
9934                 if(s.successProperty) {
9935                     this.getSuccess = this.getJsonAccessor(s.successProperty);
9936                 }
9937                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
9938                 if (s.id) {
9939                         var g = this.getJsonAccessor(s.id);
9940                         this.getId = function(rec) {
9941                                 var r = g(rec);
9942                                 return (r === undefined || r === "") ? null : r;
9943                         };
9944                 } else {
9945                         this.getId = function(){return null;};
9946                 }
9947             this.ef = [];
9948             for(var jj = 0; jj < fl; jj++){
9949                 f = fi[jj];
9950                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
9951                 this.ef[jj] = this.getJsonAccessor(map);
9952             }
9953         }
9954
9955         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
9956         if(s.totalProperty){
9957             var vt = parseInt(this.getTotal(o), 10);
9958             if(!isNaN(vt)){
9959                 totalRecords = vt;
9960             }
9961         }
9962         if(s.successProperty){
9963             var vs = this.getSuccess(o);
9964             if(vs === false || vs === 'false'){
9965                 success = false;
9966             }
9967         }
9968         var records = [];
9969             for(var i = 0; i < c; i++){
9970                     var n = root[i];
9971                 var values = {};
9972                 var id = this.getId(n);
9973                 for(var j = 0; j < fl; j++){
9974                     f = fi[j];
9975                 var v = this.ef[j](n);
9976                 if (!f.convert) {
9977                     Roo.log('missing convert for ' + f.name);
9978                     Roo.log(f);
9979                     continue;
9980                 }
9981                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
9982                 }
9983                 var record = new Record(values, id);
9984                 record.json = n;
9985                 records[i] = record;
9986             }
9987             return {
9988             raw : o,
9989                 success : success,
9990                 records : records,
9991                 totalRecords : totalRecords
9992             };
9993     }
9994 });/*
9995  * Based on:
9996  * Ext JS Library 1.1.1
9997  * Copyright(c) 2006-2007, Ext JS, LLC.
9998  *
9999  * Originally Released Under LGPL - original licence link has changed is not relivant.
10000  *
10001  * Fork - LGPL
10002  * <script type="text/javascript">
10003  */
10004
10005 /**
10006  * @class Roo.data.ArrayReader
10007  * @extends Roo.data.DataReader
10008  * Data reader class to create an Array of Roo.data.Record objects from an Array.
10009  * Each element of that Array represents a row of data fields. The
10010  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
10011  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
10012  * <p>
10013  * Example code:.
10014  * <pre><code>
10015 var RecordDef = Roo.data.Record.create([
10016     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
10017     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
10018 ]);
10019 var myReader = new Roo.data.ArrayReader({
10020     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
10021 }, RecordDef);
10022 </code></pre>
10023  * <p>
10024  * This would consume an Array like this:
10025  * <pre><code>
10026 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
10027   </code></pre>
10028  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
10029  * @constructor
10030  * Create a new JsonReader
10031  * @param {Object} meta Metadata configuration options.
10032  * @param {Object} recordType Either an Array of field definition objects
10033  * as specified to {@link Roo.data.Record#create},
10034  * or an {@link Roo.data.Record} object
10035  * created using {@link Roo.data.Record#create}.
10036  */
10037 Roo.data.ArrayReader = function(meta, recordType){
10038     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
10039 };
10040
10041 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
10042     /**
10043      * Create a data block containing Roo.data.Records from an XML document.
10044      * @param {Object} o An Array of row objects which represents the dataset.
10045      * @return {Object} data A data block which is used by an Roo.data.Store object as
10046      * a cache of Roo.data.Records.
10047      */
10048     readRecords : function(o){
10049         var sid = this.meta ? this.meta.id : null;
10050         var recordType = this.recordType, fields = recordType.prototype.fields;
10051         var records = [];
10052         var root = o;
10053             for(var i = 0; i < root.length; i++){
10054                     var n = root[i];
10055                 var values = {};
10056                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
10057                 for(var j = 0, jlen = fields.length; j < jlen; j++){
10058                 var f = fields.items[j];
10059                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
10060                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
10061                 v = f.convert(v);
10062                 values[f.name] = v;
10063             }
10064                 var record = new recordType(values, id);
10065                 record.json = n;
10066                 records[records.length] = record;
10067             }
10068             return {
10069                 records : records,
10070                 totalRecords : records.length
10071             };
10072     }
10073 });/*
10074  * - LGPL
10075  * * 
10076  */
10077
10078 /**
10079  * @class Roo.bootstrap.ComboBox
10080  * @extends Roo.bootstrap.TriggerField
10081  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
10082  * @cfg {Boolean} append (true|false) default false
10083  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
10084  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
10085  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
10086  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
10087  * @constructor
10088  * Create a new ComboBox.
10089  * @param {Object} config Configuration options
10090  */
10091 Roo.bootstrap.ComboBox = function(config){
10092     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
10093     this.addEvents({
10094         /**
10095          * @event expand
10096          * Fires when the dropdown list is expanded
10097              * @param {Roo.bootstrap.ComboBox} combo This combo box
10098              */
10099         'expand' : true,
10100         /**
10101          * @event collapse
10102          * Fires when the dropdown list is collapsed
10103              * @param {Roo.bootstrap.ComboBox} combo This combo box
10104              */
10105         'collapse' : true,
10106         /**
10107          * @event beforeselect
10108          * Fires before a list item is selected. Return false to cancel the selection.
10109              * @param {Roo.bootstrap.ComboBox} combo This combo box
10110              * @param {Roo.data.Record} record The data record returned from the underlying store
10111              * @param {Number} index The index of the selected item in the dropdown list
10112              */
10113         'beforeselect' : true,
10114         /**
10115          * @event select
10116          * Fires when a list item is selected
10117              * @param {Roo.bootstrap.ComboBox} combo This combo box
10118              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
10119              * @param {Number} index The index of the selected item in the dropdown list
10120              */
10121         'select' : true,
10122         /**
10123          * @event beforequery
10124          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
10125          * The event object passed has these properties:
10126              * @param {Roo.bootstrap.ComboBox} combo This combo box
10127              * @param {String} query The query
10128              * @param {Boolean} forceAll true to force "all" query
10129              * @param {Boolean} cancel true to cancel the query
10130              * @param {Object} e The query event object
10131              */
10132         'beforequery': true,
10133          /**
10134          * @event add
10135          * Fires when the 'add' icon is pressed (add a listener to enable add button)
10136              * @param {Roo.bootstrap.ComboBox} combo This combo box
10137              */
10138         'add' : true,
10139         /**
10140          * @event edit
10141          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
10142              * @param {Roo.bootstrap.ComboBox} combo This combo box
10143              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
10144              */
10145         'edit' : true,
10146         /**
10147          * @event remove
10148          * Fires when the remove value from the combobox array
10149              * @param {Roo.bootstrap.ComboBox} combo This combo box
10150              */
10151         'remove' : true
10152         
10153     });
10154     
10155     this.item = [];
10156     this.tickItems = [];
10157     
10158     this.selectedIndex = -1;
10159     if(this.mode == 'local'){
10160         if(config.queryDelay === undefined){
10161             this.queryDelay = 10;
10162         }
10163         if(config.minChars === undefined){
10164             this.minChars = 0;
10165         }
10166     }
10167 };
10168
10169 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
10170      
10171     /**
10172      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
10173      * rendering into an Roo.Editor, defaults to false)
10174      */
10175     /**
10176      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
10177      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
10178      */
10179     /**
10180      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
10181      */
10182     /**
10183      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
10184      * the dropdown list (defaults to undefined, with no header element)
10185      */
10186
10187      /**
10188      * @cfg {String/Roo.Template} tpl The template to use to render the output
10189      */
10190      
10191      /**
10192      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
10193      */
10194     listWidth: undefined,
10195     /**
10196      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
10197      * mode = 'remote' or 'text' if mode = 'local')
10198      */
10199     displayField: undefined,
10200     /**
10201      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
10202      * mode = 'remote' or 'value' if mode = 'local'). 
10203      * Note: use of a valueField requires the user make a selection
10204      * in order for a value to be mapped.
10205      */
10206     valueField: undefined,
10207     
10208     
10209     /**
10210      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
10211      * field's data value (defaults to the underlying DOM element's name)
10212      */
10213     hiddenName: undefined,
10214     /**
10215      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
10216      */
10217     listClass: '',
10218     /**
10219      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
10220      */
10221     selectedClass: 'active',
10222     
10223     /**
10224      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
10225      */
10226     shadow:'sides',
10227     /**
10228      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
10229      * anchor positions (defaults to 'tl-bl')
10230      */
10231     listAlign: 'tl-bl?',
10232     /**
10233      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
10234      */
10235     maxHeight: 300,
10236     /**
10237      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
10238      * query specified by the allQuery config option (defaults to 'query')
10239      */
10240     triggerAction: 'query',
10241     /**
10242      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
10243      * (defaults to 4, does not apply if editable = false)
10244      */
10245     minChars : 4,
10246     /**
10247      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
10248      * delay (typeAheadDelay) if it matches a known value (defaults to false)
10249      */
10250     typeAhead: false,
10251     /**
10252      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
10253      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
10254      */
10255     queryDelay: 500,
10256     /**
10257      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
10258      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
10259      */
10260     pageSize: 0,
10261     /**
10262      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
10263      * when editable = true (defaults to false)
10264      */
10265     selectOnFocus:false,
10266     /**
10267      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
10268      */
10269     queryParam: 'query',
10270     /**
10271      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
10272      * when mode = 'remote' (defaults to 'Loading...')
10273      */
10274     loadingText: 'Loading...',
10275     /**
10276      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
10277      */
10278     resizable: false,
10279     /**
10280      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
10281      */
10282     handleHeight : 8,
10283     /**
10284      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
10285      * traditional select (defaults to true)
10286      */
10287     editable: true,
10288     /**
10289      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
10290      */
10291     allQuery: '',
10292     /**
10293      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
10294      */
10295     mode: 'remote',
10296     /**
10297      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
10298      * listWidth has a higher value)
10299      */
10300     minListWidth : 70,
10301     /**
10302      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
10303      * allow the user to set arbitrary text into the field (defaults to false)
10304      */
10305     forceSelection:false,
10306     /**
10307      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
10308      * if typeAhead = true (defaults to 250)
10309      */
10310     typeAheadDelay : 250,
10311     /**
10312      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
10313      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
10314      */
10315     valueNotFoundText : undefined,
10316     /**
10317      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
10318      */
10319     blockFocus : false,
10320     
10321     /**
10322      * @cfg {Boolean} disableClear Disable showing of clear button.
10323      */
10324     disableClear : false,
10325     /**
10326      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
10327      */
10328     alwaysQuery : false,
10329     
10330     /**
10331      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
10332      */
10333     multiple : false,
10334     
10335     //private
10336     addicon : false,
10337     editicon: false,
10338     
10339     page: 0,
10340     hasQuery: false,
10341     append: false,
10342     loadNext: false,
10343     autoFocus : true,
10344     tickable : false,
10345     btnPosition : 'right',
10346     triggerList : true,
10347     // element that contains real text value.. (when hidden is used..)
10348     
10349     getAutoCreate : function()
10350     {
10351         var cfg = false;
10352         
10353         /*
10354          *  Normal ComboBox
10355          */
10356         if(!this.tickable){
10357             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
10358             return cfg;
10359         }
10360         
10361         /*
10362          *  ComboBox with tickable selections
10363          */
10364              
10365         var align = this.labelAlign || this.parentLabelAlign();
10366         
10367         cfg = {
10368             cls : 'form-group roo-combobox-tickable' //input-group
10369         };
10370         
10371         
10372         var buttons = {
10373             tag : 'div',
10374             cls : 'tickable-buttons',
10375             cn : [
10376                 {
10377                     tag : 'button',
10378                     type : 'button',
10379                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
10380                     html : 'Edit'
10381                 },
10382                 {
10383                     tag : 'button',
10384                     type : 'button',
10385                     name : 'ok',
10386                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
10387                     html : 'Done'
10388                 },
10389                 {
10390                     tag : 'button',
10391                     type : 'button',
10392                     name : 'cancel',
10393                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
10394                     html : 'Cancel'
10395                 }
10396             ]
10397         };
10398         
10399         var _this = this;
10400         Roo.each(buttons.cn, function(c){
10401             if (_this.size) {
10402                 c.cls += ' btn-' + _this.size;
10403             }
10404
10405             if (_this.disabled) {
10406                 c.disabled = true;
10407             }
10408         });
10409         
10410         var box = {
10411             tag: 'div',
10412             cn: [
10413                 {
10414                     tag: 'input',
10415                     type : 'hidden',
10416                     cls: 'form-hidden-field'
10417                 },
10418                 {
10419                     tag: 'ul',
10420                     cls: 'select2-choices',
10421                     cn:[
10422                         {
10423                             tag: 'li',
10424                             cls: 'select2-search-field',
10425                             cn: [
10426
10427                                 buttons
10428                             ]
10429                         }
10430                     ]
10431                 }
10432             ]
10433         }
10434         
10435         var combobox = {
10436             cls: 'select2-container input-group select2-container-multi',
10437             cn: [
10438                 box
10439 //                {
10440 //                    tag: 'ul',
10441 //                    cls: 'typeahead typeahead-long dropdown-menu',
10442 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
10443 //                }
10444             ]
10445         };
10446         
10447         if (align ==='left' && this.fieldLabel.length) {
10448             
10449                 Roo.log("left and has label");
10450                 cfg.cn = [
10451                     
10452                     {
10453                         tag: 'label',
10454                         'for' :  id,
10455                         cls : 'control-label col-sm-' + this.labelWidth,
10456                         html : this.fieldLabel
10457                         
10458                     },
10459                     {
10460                         cls : "col-sm-" + (12 - this.labelWidth), 
10461                         cn: [
10462                             combobox
10463                         ]
10464                     }
10465                     
10466                 ];
10467         } else if ( this.fieldLabel.length) {
10468                 Roo.log(" label");
10469                  cfg.cn = [
10470                    
10471                     {
10472                         tag: 'label',
10473                         //cls : 'input-group-addon',
10474                         html : this.fieldLabel
10475                         
10476                     },
10477                     
10478                     combobox
10479                     
10480                 ];
10481
10482         } else {
10483             
10484                 Roo.log(" no label && no align");
10485                 cfg = combobox
10486                      
10487                 
10488         }
10489          
10490         var settings=this;
10491         ['xs','sm','md','lg'].map(function(size){
10492             if (settings[size]) {
10493                 cfg.cls += ' col-' + size + '-' + settings[size];
10494             }
10495         });
10496         
10497         return cfg;
10498         
10499     },
10500     
10501     // private
10502     initEvents: function()
10503     {
10504         
10505         if (!this.store) {
10506             throw "can not find store for combo";
10507         }
10508         this.store = Roo.factory(this.store, Roo.data);
10509         
10510         if(this.tickable){
10511             this.initTickableEvents();
10512             return;
10513         }
10514         
10515         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
10516         
10517         if(this.hiddenName){
10518             
10519             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10520             
10521             this.hiddenField.dom.value =
10522                 this.hiddenValue !== undefined ? this.hiddenValue :
10523                 this.value !== undefined ? this.value : '';
10524
10525             // prevent input submission
10526             this.el.dom.removeAttribute('name');
10527             this.hiddenField.dom.setAttribute('name', this.hiddenName);
10528              
10529              
10530         }
10531         //if(Roo.isGecko){
10532         //    this.el.dom.setAttribute('autocomplete', 'off');
10533         //}
10534         
10535         var cls = 'x-combo-list';
10536         
10537         //this.list = new Roo.Layer({
10538         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
10539         //});
10540         
10541         var _this = this;
10542         
10543         (function(){
10544             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
10545             _this.list.setWidth(lw);
10546         }).defer(100);
10547         
10548         this.list.on('mouseover', this.onViewOver, this);
10549         this.list.on('mousemove', this.onViewMove, this);
10550         
10551         this.list.on('scroll', this.onViewScroll, this);
10552         
10553         /*
10554         this.list.swallowEvent('mousewheel');
10555         this.assetHeight = 0;
10556
10557         if(this.title){
10558             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
10559             this.assetHeight += this.header.getHeight();
10560         }
10561
10562         this.innerList = this.list.createChild({cls:cls+'-inner'});
10563         this.innerList.on('mouseover', this.onViewOver, this);
10564         this.innerList.on('mousemove', this.onViewMove, this);
10565         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
10566         
10567         if(this.allowBlank && !this.pageSize && !this.disableClear){
10568             this.footer = this.list.createChild({cls:cls+'-ft'});
10569             this.pageTb = new Roo.Toolbar(this.footer);
10570            
10571         }
10572         if(this.pageSize){
10573             this.footer = this.list.createChild({cls:cls+'-ft'});
10574             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
10575                     {pageSize: this.pageSize});
10576             
10577         }
10578         
10579         if (this.pageTb && this.allowBlank && !this.disableClear) {
10580             var _this = this;
10581             this.pageTb.add(new Roo.Toolbar.Fill(), {
10582                 cls: 'x-btn-icon x-btn-clear',
10583                 text: '&#160;',
10584                 handler: function()
10585                 {
10586                     _this.collapse();
10587                     _this.clearValue();
10588                     _this.onSelect(false, -1);
10589                 }
10590             });
10591         }
10592         if (this.footer) {
10593             this.assetHeight += this.footer.getHeight();
10594         }
10595         */
10596             
10597         if(!this.tpl){
10598             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
10599         }
10600
10601         this.view = new Roo.View(this.list, this.tpl, {
10602             singleSelect:true, store: this.store, selectedClass: this.selectedClass
10603         });
10604         //this.view.wrapEl.setDisplayed(false);
10605         this.view.on('click', this.onViewClick, this);
10606         
10607         
10608         
10609         this.store.on('beforeload', this.onBeforeLoad, this);
10610         this.store.on('load', this.onLoad, this);
10611         this.store.on('loadexception', this.onLoadException, this);
10612         /*
10613         if(this.resizable){
10614             this.resizer = new Roo.Resizable(this.list,  {
10615                pinned:true, handles:'se'
10616             });
10617             this.resizer.on('resize', function(r, w, h){
10618                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
10619                 this.listWidth = w;
10620                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
10621                 this.restrictHeight();
10622             }, this);
10623             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
10624         }
10625         */
10626         if(!this.editable){
10627             this.editable = true;
10628             this.setEditable(false);
10629         }
10630         
10631         /*
10632         
10633         if (typeof(this.events.add.listeners) != 'undefined') {
10634             
10635             this.addicon = this.wrap.createChild(
10636                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
10637        
10638             this.addicon.on('click', function(e) {
10639                 this.fireEvent('add', this);
10640             }, this);
10641         }
10642         if (typeof(this.events.edit.listeners) != 'undefined') {
10643             
10644             this.editicon = this.wrap.createChild(
10645                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
10646             if (this.addicon) {
10647                 this.editicon.setStyle('margin-left', '40px');
10648             }
10649             this.editicon.on('click', function(e) {
10650                 
10651                 // we fire even  if inothing is selected..
10652                 this.fireEvent('edit', this, this.lastData );
10653                 
10654             }, this);
10655         }
10656         */
10657         
10658         this.keyNav = new Roo.KeyNav(this.inputEl(), {
10659             "up" : function(e){
10660                 this.inKeyMode = true;
10661                 this.selectPrev();
10662             },
10663
10664             "down" : function(e){
10665                 if(!this.isExpanded()){
10666                     this.onTriggerClick();
10667                 }else{
10668                     this.inKeyMode = true;
10669                     this.selectNext();
10670                 }
10671             },
10672
10673             "enter" : function(e){
10674 //                this.onViewClick();
10675                 //return true;
10676                 this.collapse();
10677                 
10678                 if(this.fireEvent("specialkey", this, e)){
10679                     this.onViewClick(false);
10680                 }
10681                 
10682                 return true;
10683             },
10684
10685             "esc" : function(e){
10686                 this.collapse();
10687             },
10688
10689             "tab" : function(e){
10690                 this.collapse();
10691                 
10692                 if(this.fireEvent("specialkey", this, e)){
10693                     this.onViewClick(false);
10694                 }
10695                 
10696                 return true;
10697             },
10698
10699             scope : this,
10700
10701             doRelay : function(foo, bar, hname){
10702                 if(hname == 'down' || this.scope.isExpanded()){
10703                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
10704                 }
10705                 return true;
10706             },
10707
10708             forceKeyDown: true
10709         });
10710         
10711         
10712         this.queryDelay = Math.max(this.queryDelay || 10,
10713                 this.mode == 'local' ? 10 : 250);
10714         
10715         
10716         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
10717         
10718         if(this.typeAhead){
10719             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
10720         }
10721         if(this.editable !== false){
10722             this.inputEl().on("keyup", this.onKeyUp, this);
10723         }
10724         if(this.forceSelection){
10725             this.inputEl().on('blur', this.doForce, this);
10726         }
10727         
10728         if(this.multiple){
10729             this.choices = this.el.select('ul.select2-choices', true).first();
10730             this.searchField = this.el.select('ul li.select2-search-field', true).first();
10731         }
10732     },
10733     
10734     initTickableEvents: function()
10735     {   
10736         this.createList();
10737         
10738         if(this.hiddenName){
10739             
10740             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10741             
10742             this.hiddenField.dom.value =
10743                 this.hiddenValue !== undefined ? this.hiddenValue :
10744                 this.value !== undefined ? this.value : '';
10745
10746             // prevent input submission
10747             this.el.dom.removeAttribute('name');
10748             this.hiddenField.dom.setAttribute('name', this.hiddenName);
10749              
10750              
10751         }
10752         
10753 //        this.list = this.el.select('ul.dropdown-menu',true).first();
10754         
10755         this.choices = this.el.select('ul.select2-choices', true).first();
10756         this.searchField = this.el.select('ul li.select2-search-field', true).first();
10757         if(this.triggerList){
10758             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
10759         }
10760          
10761         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
10762         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
10763         
10764         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
10765         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
10766         
10767         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
10768         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
10769         
10770         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
10771         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
10772         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
10773         
10774         this.okBtn.hide();
10775         this.cancelBtn.hide();
10776         
10777         var _this = this;
10778         
10779         (function(){
10780             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
10781             _this.list.setWidth(lw);
10782         }).defer(100);
10783         
10784         this.list.on('mouseover', this.onViewOver, this);
10785         this.list.on('mousemove', this.onViewMove, this);
10786         
10787         this.list.on('scroll', this.onViewScroll, this);
10788         
10789         if(!this.tpl){
10790             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>';
10791         }
10792
10793         this.view = new Roo.View(this.list, this.tpl, {
10794             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
10795         });
10796         
10797         //this.view.wrapEl.setDisplayed(false);
10798         this.view.on('click', this.onViewClick, this);
10799         
10800         
10801         
10802         this.store.on('beforeload', this.onBeforeLoad, this);
10803         this.store.on('load', this.onLoad, this);
10804         this.store.on('loadexception', this.onLoadException, this);
10805         
10806 //        this.keyNav = new Roo.KeyNav(this.inputEl(), {
10807 //            "up" : function(e){
10808 //                this.inKeyMode = true;
10809 //                this.selectPrev();
10810 //            },
10811 //
10812 //            "down" : function(e){
10813 //                if(!this.isExpanded()){
10814 //                    this.onTriggerClick();
10815 //                }else{
10816 //                    this.inKeyMode = true;
10817 //                    this.selectNext();
10818 //                }
10819 //            },
10820 //
10821 //            "enter" : function(e){
10822 ////                this.onViewClick();
10823 //                //return true;
10824 //                this.collapse();
10825 //                
10826 //                if(this.fireEvent("specialkey", this, e)){
10827 //                    this.onViewClick(false);
10828 //                }
10829 //                
10830 //                return true;
10831 //            },
10832 //
10833 //            "esc" : function(e){
10834 //                this.collapse();
10835 //            },
10836 //
10837 //            "tab" : function(e){
10838 //                this.collapse();
10839 //                
10840 //                if(this.fireEvent("specialkey", this, e)){
10841 //                    this.onViewClick(false);
10842 //                }
10843 //                
10844 //                return true;
10845 //            },
10846 //
10847 //            scope : this,
10848 //
10849 //            doRelay : function(foo, bar, hname){
10850 //                if(hname == 'down' || this.scope.isExpanded()){
10851 //                   return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
10852 //                }
10853 //                return true;
10854 //            },
10855 //
10856 //            forceKeyDown: true
10857 //        });
10858         
10859         
10860         this.queryDelay = Math.max(this.queryDelay || 10,
10861                 this.mode == 'local' ? 10 : 250);
10862         
10863         
10864         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
10865         
10866         if(this.typeAhead){
10867             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
10868         }
10869     },
10870
10871     onDestroy : function(){
10872         if(this.view){
10873             this.view.setStore(null);
10874             this.view.el.removeAllListeners();
10875             this.view.el.remove();
10876             this.view.purgeListeners();
10877         }
10878         if(this.list){
10879             this.list.dom.innerHTML  = '';
10880         }
10881         
10882         if(this.store){
10883             this.store.un('beforeload', this.onBeforeLoad, this);
10884             this.store.un('load', this.onLoad, this);
10885             this.store.un('loadexception', this.onLoadException, this);
10886         }
10887         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
10888     },
10889
10890     // private
10891     fireKey : function(e){
10892         if(e.isNavKeyPress() && !this.list.isVisible()){
10893             this.fireEvent("specialkey", this, e);
10894         }
10895     },
10896
10897     // private
10898     onResize: function(w, h){
10899 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
10900 //        
10901 //        if(typeof w != 'number'){
10902 //            // we do not handle it!?!?
10903 //            return;
10904 //        }
10905 //        var tw = this.trigger.getWidth();
10906 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
10907 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
10908 //        var x = w - tw;
10909 //        this.inputEl().setWidth( this.adjustWidth('input', x));
10910 //            
10911 //        //this.trigger.setStyle('left', x+'px');
10912 //        
10913 //        if(this.list && this.listWidth === undefined){
10914 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
10915 //            this.list.setWidth(lw);
10916 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
10917 //        }
10918         
10919     
10920         
10921     },
10922
10923     /**
10924      * Allow or prevent the user from directly editing the field text.  If false is passed,
10925      * the user will only be able to select from the items defined in the dropdown list.  This method
10926      * is the runtime equivalent of setting the 'editable' config option at config time.
10927      * @param {Boolean} value True to allow the user to directly edit the field text
10928      */
10929     setEditable : function(value){
10930         if(value == this.editable){
10931             return;
10932         }
10933         this.editable = value;
10934         if(!value){
10935             this.inputEl().dom.setAttribute('readOnly', true);
10936             this.inputEl().on('mousedown', this.onTriggerClick,  this);
10937             this.inputEl().addClass('x-combo-noedit');
10938         }else{
10939             this.inputEl().dom.setAttribute('readOnly', false);
10940             this.inputEl().un('mousedown', this.onTriggerClick,  this);
10941             this.inputEl().removeClass('x-combo-noedit');
10942         }
10943     },
10944
10945     // private
10946     
10947     onBeforeLoad : function(combo,opts){
10948         if(!this.hasFocus){
10949             return;
10950         }
10951          if (!opts.add) {
10952             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
10953          }
10954 //        this.restrictHeight();
10955         this.selectedIndex = -1;
10956     },
10957
10958     // private
10959     onLoad : function(){
10960         
10961         this.hasQuery = false;
10962         
10963         if(!this.hasFocus){
10964             return;
10965         }
10966         
10967         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
10968             this.loading.hide();
10969         }
10970         
10971         if(this.store.getCount() > 0){
10972             this.expand();
10973 //            this.restrictHeight();
10974             if(this.lastQuery == this.allQuery){
10975                 if(this.editable && !this.tickable){
10976                     this.inputEl().dom.select();
10977                 }
10978                 if(!this.selectByValue(this.value, true) && this.autoFocus){
10979                     this.select(0, true);
10980                 }
10981             }else{
10982                 if(this.autoFocus){
10983                     this.selectNext();
10984                 }
10985                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
10986                     this.taTask.delay(this.typeAheadDelay);
10987                 }
10988             }
10989         }else{
10990             this.onEmptyResults();
10991         }
10992         
10993         //this.el.focus();
10994     },
10995     // private
10996     onLoadException : function()
10997     {
10998         this.hasQuery = false;
10999         
11000         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11001             this.loading.hide();
11002         }
11003         
11004         this.collapse();
11005         Roo.log(this.store.reader.jsonData);
11006         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
11007             // fixme
11008             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
11009         }
11010         
11011         
11012     },
11013     // private
11014     onTypeAhead : function(){
11015         if(this.store.getCount() > 0){
11016             var r = this.store.getAt(0);
11017             var newValue = r.data[this.displayField];
11018             var len = newValue.length;
11019             var selStart = this.getRawValue().length;
11020             
11021             if(selStart != len){
11022                 this.setRawValue(newValue);
11023                 this.selectText(selStart, newValue.length);
11024             }
11025         }
11026     },
11027
11028     // private
11029     onSelect : function(record, index){
11030         
11031         if(this.fireEvent('beforeselect', this, record, index) !== false){
11032         
11033             this.setFromData(index > -1 ? record.data : false);
11034             
11035             this.collapse();
11036             this.fireEvent('select', this, record, index);
11037         }
11038     },
11039
11040     /**
11041      * Returns the currently selected field value or empty string if no value is set.
11042      * @return {String} value The selected value
11043      */
11044     getValue : function(){
11045         
11046         if(this.multiple){
11047             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
11048         }
11049         
11050         if(this.valueField){
11051             return typeof this.value != 'undefined' ? this.value : '';
11052         }else{
11053             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
11054         }
11055     },
11056
11057     /**
11058      * Clears any text/value currently set in the field
11059      */
11060     clearValue : function(){
11061         if(this.hiddenField){
11062             this.hiddenField.dom.value = '';
11063         }
11064         this.value = '';
11065         this.setRawValue('');
11066         this.lastSelectionText = '';
11067         
11068     },
11069
11070     /**
11071      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
11072      * will be displayed in the field.  If the value does not match the data value of an existing item,
11073      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
11074      * Otherwise the field will be blank (although the value will still be set).
11075      * @param {String} value The value to match
11076      */
11077     setValue : function(v){
11078         if(this.multiple){
11079             this.syncValue();
11080             return;
11081         }
11082         
11083         var text = v;
11084         if(this.valueField){
11085             var r = this.findRecord(this.valueField, v);
11086             if(r){
11087                 text = r.data[this.displayField];
11088             }else if(this.valueNotFoundText !== undefined){
11089                 text = this.valueNotFoundText;
11090             }
11091         }
11092         this.lastSelectionText = text;
11093         if(this.hiddenField){
11094             this.hiddenField.dom.value = v;
11095         }
11096         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
11097         this.value = v;
11098     },
11099     /**
11100      * @property {Object} the last set data for the element
11101      */
11102     
11103     lastData : false,
11104     /**
11105      * Sets the value of the field based on a object which is related to the record format for the store.
11106      * @param {Object} value the value to set as. or false on reset?
11107      */
11108     setFromData : function(o){
11109         
11110         if(this.multiple){
11111             if(typeof o.display_name !== 'string'){
11112                 for(var i=0;i<o.display_name.length;i++){
11113                     this.addItem({'id':o.id[i],'display_name':o.display_name[i]});
11114                 }
11115                 return;
11116             }
11117             this.addItem(o);
11118             return;
11119         }
11120             
11121         var dv = ''; // display value
11122         var vv = ''; // value value..
11123         this.lastData = o;
11124         if (this.displayField) {
11125             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11126         } else {
11127             // this is an error condition!!!
11128             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
11129         }
11130         
11131         if(this.valueField){
11132             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
11133         }
11134         
11135         if(this.hiddenField){
11136             this.hiddenField.dom.value = vv;
11137             
11138             this.lastSelectionText = dv;
11139             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11140             this.value = vv;
11141             return;
11142         }
11143         // no hidden field.. - we store the value in 'value', but still display
11144         // display field!!!!
11145         this.lastSelectionText = dv;
11146         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11147         this.value = vv;
11148         
11149         
11150     },
11151     // private
11152     reset : function(){
11153         // overridden so that last data is reset..
11154         this.setValue(this.originalValue);
11155         this.clearInvalid();
11156         this.lastData = false;
11157         if (this.view) {
11158             this.view.clearSelections();
11159         }
11160     },
11161     // private
11162     findRecord : function(prop, value){
11163         var record;
11164         if(this.store.getCount() > 0){
11165             this.store.each(function(r){
11166                 if(r.data[prop] == value){
11167                     record = r;
11168                     return false;
11169                 }
11170                 return true;
11171             });
11172         }
11173         return record;
11174     },
11175     
11176     getName: function()
11177     {
11178         // returns hidden if it's set..
11179         if (!this.rendered) {return ''};
11180         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
11181         
11182     },
11183     // private
11184     onViewMove : function(e, t){
11185         this.inKeyMode = false;
11186     },
11187
11188     // private
11189     onViewOver : function(e, t){
11190         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
11191             return;
11192         }
11193         var item = this.view.findItemFromChild(t);
11194         
11195         if(item){
11196             var index = this.view.indexOf(item);
11197             this.select(index, false);
11198         }
11199     },
11200
11201     // private
11202     onViewClick : function(view, doFocus, el, e)
11203     {
11204         var index = this.view.getSelectedIndexes()[0];
11205         
11206         var r = this.store.getAt(index);
11207         
11208         if(this.tickable){
11209             
11210             if(e.getTarget().nodeName.toLowerCase() != 'input'){
11211                 return;
11212             }
11213             
11214             var rm = false;
11215             var _this = this;
11216             
11217             Roo.each(this.tickItems, function(v,k){
11218                 
11219                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
11220                     _this.tickItems.splice(k, 1);
11221                     rm = true;
11222                     return;
11223                 }
11224             })
11225             
11226             if(rm){
11227                 return;
11228             }
11229             
11230             this.tickItems.push(r.data);
11231             return;
11232         }
11233         
11234         if(r){
11235             this.onSelect(r, index);
11236         }
11237         if(doFocus !== false && !this.blockFocus){
11238             this.inputEl().focus();
11239         }
11240     },
11241
11242     // private
11243     restrictHeight : function(){
11244         //this.innerList.dom.style.height = '';
11245         //var inner = this.innerList.dom;
11246         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
11247         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
11248         //this.list.beginUpdate();
11249         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
11250         this.list.alignTo(this.inputEl(), this.listAlign);
11251         this.list.alignTo(this.inputEl(), this.listAlign);
11252         //this.list.endUpdate();
11253     },
11254
11255     // private
11256     onEmptyResults : function(){
11257         this.collapse();
11258     },
11259
11260     /**
11261      * Returns true if the dropdown list is expanded, else false.
11262      */
11263     isExpanded : function(){
11264         return this.list.isVisible();
11265     },
11266
11267     /**
11268      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
11269      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11270      * @param {String} value The data value of the item to select
11271      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11272      * selected item if it is not currently in view (defaults to true)
11273      * @return {Boolean} True if the value matched an item in the list, else false
11274      */
11275     selectByValue : function(v, scrollIntoView){
11276         if(v !== undefined && v !== null){
11277             var r = this.findRecord(this.valueField || this.displayField, v);
11278             if(r){
11279                 this.select(this.store.indexOf(r), scrollIntoView);
11280                 return true;
11281             }
11282         }
11283         return false;
11284     },
11285
11286     /**
11287      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
11288      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11289      * @param {Number} index The zero-based index of the list item to select
11290      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11291      * selected item if it is not currently in view (defaults to true)
11292      */
11293     select : function(index, scrollIntoView){
11294         this.selectedIndex = index;
11295         this.view.select(index);
11296         if(scrollIntoView !== false){
11297             var el = this.view.getNode(index);
11298             if(el && !this.multiple && !this.tickable){
11299                 this.list.scrollChildIntoView(el, false);
11300             }
11301         }
11302     },
11303
11304     // private
11305     selectNext : function(){
11306         var ct = this.store.getCount();
11307         if(ct > 0){
11308             if(this.selectedIndex == -1){
11309                 this.select(0);
11310             }else if(this.selectedIndex < ct-1){
11311                 this.select(this.selectedIndex+1);
11312             }
11313         }
11314     },
11315
11316     // private
11317     selectPrev : function(){
11318         var ct = this.store.getCount();
11319         if(ct > 0){
11320             if(this.selectedIndex == -1){
11321                 this.select(0);
11322             }else if(this.selectedIndex != 0){
11323                 this.select(this.selectedIndex-1);
11324             }
11325         }
11326     },
11327
11328     // private
11329     onKeyUp : function(e){
11330         if(this.editable !== false && !e.isSpecialKey()){
11331             this.lastKey = e.getKey();
11332             this.dqTask.delay(this.queryDelay);
11333         }
11334     },
11335
11336     // private
11337     validateBlur : function(){
11338         return !this.list || !this.list.isVisible();   
11339     },
11340
11341     // private
11342     initQuery : function(){
11343         this.doQuery(this.getRawValue());
11344     },
11345
11346     // private
11347     doForce : function(){
11348         if(this.inputEl().dom.value.length > 0){
11349             this.inputEl().dom.value =
11350                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
11351              
11352         }
11353     },
11354
11355     /**
11356      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
11357      * query allowing the query action to be canceled if needed.
11358      * @param {String} query The SQL query to execute
11359      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
11360      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
11361      * saved in the current store (defaults to false)
11362      */
11363     doQuery : function(q, forceAll){
11364         
11365         if(q === undefined || q === null){
11366             q = '';
11367         }
11368         var qe = {
11369             query: q,
11370             forceAll: forceAll,
11371             combo: this,
11372             cancel:false
11373         };
11374         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
11375             return false;
11376         }
11377         q = qe.query;
11378         
11379         forceAll = qe.forceAll;
11380         if(forceAll === true || (q.length >= this.minChars)){
11381             
11382             this.hasQuery = true;
11383             
11384             if(this.lastQuery != q || this.alwaysQuery){
11385                 this.lastQuery = q;
11386                 if(this.mode == 'local'){
11387                     this.selectedIndex = -1;
11388                     if(forceAll){
11389                         this.store.clearFilter();
11390                     }else{
11391                         this.store.filter(this.displayField, q);
11392                     }
11393                     this.onLoad();
11394                 }else{
11395                     this.store.baseParams[this.queryParam] = q;
11396                     
11397                     var options = {params : this.getParams(q)};
11398                     
11399                     if(this.loadNext){
11400                         options.add = true;
11401                         options.params.start = this.page * this.pageSize;
11402                     }
11403                     
11404                     this.store.load(options);
11405                     /*
11406                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
11407                      *  we should expand the list on onLoad
11408                      *  so command out it
11409                      */
11410 //                    this.expand();
11411                 }
11412             }else{
11413                 this.selectedIndex = -1;
11414                 this.onLoad();   
11415             }
11416         }
11417         
11418         this.loadNext = false;
11419     },
11420
11421     // private
11422     getParams : function(q){
11423         var p = {};
11424         //p[this.queryParam] = q;
11425         
11426         if(this.pageSize){
11427             p.start = 0;
11428             p.limit = this.pageSize;
11429         }
11430         return p;
11431     },
11432
11433     /**
11434      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
11435      */
11436     collapse : function(){
11437         if(!this.isExpanded()){
11438             return;
11439         }
11440         
11441         this.hasFocus = false;
11442         
11443         this.list.hide();
11444         
11445         if(this.tickable){
11446             this.okBtn.hide();
11447             this.cancelBtn.hide();
11448             this.trigger.show();
11449         }
11450         
11451         Roo.get(document).un('mousedown', this.collapseIf, this);
11452         Roo.get(document).un('mousewheel', this.collapseIf, this);
11453         if (!this.editable) {
11454             Roo.get(document).un('keydown', this.listKeyPress, this);
11455         }
11456         this.fireEvent('collapse', this);
11457     },
11458
11459     // private
11460     collapseIf : function(e){
11461         var in_combo  = e.within(this.el);
11462         var in_list =  e.within(this.list);
11463         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
11464         
11465         if (in_combo || in_list || is_list) {
11466             //e.stopPropagation();
11467             return;
11468         }
11469         
11470         if(this.tickable){
11471             this.onTickableFooterButtonClick(e, false, false);
11472         }
11473
11474         this.collapse();
11475         
11476     },
11477
11478     /**
11479      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
11480      */
11481     expand : function(){
11482        
11483         if(this.isExpanded() || !this.hasFocus){
11484             return;
11485         }
11486          Roo.log('expand');
11487         
11488         this.list.show();
11489         
11490         this.restrictHeight();
11491         
11492         if(this.tickable){
11493             
11494             this.tickItems = Roo.apply([], this.item);
11495             
11496             this.okBtn.show();
11497             this.cancelBtn.show();
11498             this.trigger.hide();
11499             
11500         }
11501         
11502         Roo.get(document).on('mousedown', this.collapseIf, this);
11503         Roo.get(document).on('mousewheel', this.collapseIf, this);
11504         if (!this.editable) {
11505             Roo.get(document).on('keydown', this.listKeyPress, this);
11506         }
11507         
11508         this.fireEvent('expand', this);
11509     },
11510
11511     // private
11512     // Implements the default empty TriggerField.onTriggerClick function
11513     onTriggerClick : function(e)
11514     {
11515         Roo.log('trigger click');
11516         
11517         if(this.disabled || !this.triggerList){
11518             return;
11519         }
11520         
11521         this.page = 0;
11522         this.loadNext = false;
11523         
11524         if(this.isExpanded()){
11525             this.collapse();
11526             if (!this.blockFocus) {
11527                 this.inputEl().focus();
11528             }
11529             
11530         }else {
11531             this.hasFocus = true;
11532             if(this.triggerAction == 'all') {
11533                 this.doQuery(this.allQuery, true);
11534             } else {
11535                 this.doQuery(this.getRawValue());
11536             }
11537             if (!this.blockFocus) {
11538                 this.inputEl().focus();
11539             }
11540         }
11541     },
11542     
11543     onTickableTriggerClick : function(e)
11544     {
11545         if(this.disabled){
11546             return;
11547         }
11548         
11549         this.page = 0;
11550         this.loadNext = false;
11551         this.hasFocus = true;
11552         
11553         if(this.triggerAction == 'all') {
11554             this.doQuery(this.allQuery, true);
11555         } else {
11556             this.doQuery(this.getRawValue());
11557         }
11558     },
11559     
11560     onSearchFieldClick : function(e)
11561     {
11562         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
11563             return;
11564         }
11565         
11566         this.page = 0;
11567         this.loadNext = false;
11568         this.hasFocus = true;
11569         
11570         if(this.triggerAction == 'all') {
11571             this.doQuery(this.allQuery, true);
11572         } else {
11573             this.doQuery(this.getRawValue());
11574         }
11575     },
11576     
11577     listKeyPress : function(e)
11578     {
11579         //Roo.log('listkeypress');
11580         // scroll to first matching element based on key pres..
11581         if (e.isSpecialKey()) {
11582             return false;
11583         }
11584         var k = String.fromCharCode(e.getKey()).toUpperCase();
11585         //Roo.log(k);
11586         var match  = false;
11587         var csel = this.view.getSelectedNodes();
11588         var cselitem = false;
11589         if (csel.length) {
11590             var ix = this.view.indexOf(csel[0]);
11591             cselitem  = this.store.getAt(ix);
11592             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
11593                 cselitem = false;
11594             }
11595             
11596         }
11597         
11598         this.store.each(function(v) { 
11599             if (cselitem) {
11600                 // start at existing selection.
11601                 if (cselitem.id == v.id) {
11602                     cselitem = false;
11603                 }
11604                 return true;
11605             }
11606                 
11607             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
11608                 match = this.store.indexOf(v);
11609                 return false;
11610             }
11611             return true;
11612         }, this);
11613         
11614         if (match === false) {
11615             return true; // no more action?
11616         }
11617         // scroll to?
11618         this.view.select(match);
11619         var sn = Roo.get(this.view.getSelectedNodes()[0])
11620         //sn.scrollIntoView(sn.dom.parentNode, false);
11621     },
11622     
11623     onViewScroll : function(e, t){
11624         
11625         if(this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
11626             return;
11627         }
11628         
11629         this.hasQuery = true;
11630         
11631         this.loading = this.list.select('.loading', true).first();
11632         
11633         if(this.loading === null){
11634             this.list.createChild({
11635                 tag: 'div',
11636                 cls: 'loading select2-more-results select2-active',
11637                 html: 'Loading more results...'
11638             })
11639             
11640             this.loading = this.list.select('.loading', true).first();
11641             
11642             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
11643             
11644             this.loading.hide();
11645         }
11646         
11647         this.loading.show();
11648         
11649         var _combo = this;
11650         
11651         this.page++;
11652         this.loadNext = true;
11653         
11654         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
11655         
11656         return;
11657     },
11658     
11659     addItem : function(o)
11660     {   
11661         var dv = ''; // display value
11662         
11663         if (this.displayField) {
11664             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11665         } else {
11666             // this is an error condition!!!
11667             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
11668         }
11669         
11670         if(!dv.length){
11671             return;
11672         }
11673         
11674         var choice = this.choices.createChild({
11675             tag: 'li',
11676             cls: 'select2-search-choice',
11677             cn: [
11678                 {
11679                     tag: 'div',
11680                     html: dv
11681                 },
11682                 {
11683                     tag: 'a',
11684                     href: '#',
11685                     cls: 'select2-search-choice-close',
11686                     tabindex: '-1'
11687                 }
11688             ]
11689             
11690         }, this.searchField);
11691         
11692         var close = choice.select('a.select2-search-choice-close', true).first()
11693         
11694         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
11695         
11696         this.item.push(o);
11697         
11698         this.lastData = o;
11699         
11700         this.syncValue();
11701         
11702         this.inputEl().dom.value = '';
11703         
11704     },
11705     
11706     onRemoveItem : function(e, _self, o)
11707     {
11708         e.preventDefault();
11709         var index = this.item.indexOf(o.data) * 1;
11710         
11711         if( index < 0){
11712             Roo.log('not this item?!');
11713             return;
11714         }
11715         
11716         this.item.splice(index, 1);
11717         o.item.remove();
11718         
11719         this.syncValue();
11720         
11721         this.fireEvent('remove', this, e);
11722         
11723     },
11724     
11725     syncValue : function()
11726     {
11727         if(!this.item.length){
11728             this.clearValue();
11729             return;
11730         }
11731             
11732         var value = [];
11733         var _this = this;
11734         Roo.each(this.item, function(i){
11735             if(_this.valueField){
11736                 value.push(i[_this.valueField]);
11737                 return;
11738             }
11739
11740             value.push(i);
11741         });
11742
11743         this.value = value.join(',');
11744
11745         if(this.hiddenField){
11746             this.hiddenField.dom.value = this.value;
11747         }
11748     },
11749     
11750     clearItem : function()
11751     {
11752         if(!this.multiple){
11753             return;
11754         }
11755         
11756         this.item = [];
11757         
11758         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
11759            c.remove();
11760         });
11761         
11762         this.syncValue();
11763     },
11764     
11765     inputEl: function ()
11766     {
11767         if(this.tickable){
11768             return this.searchField;
11769         }
11770         return this.el.select('input.form-control',true).first();
11771     },
11772     
11773     
11774     onTickableFooterButtonClick : function(e, btn, el)
11775     {
11776         e.preventDefault();
11777         
11778         if(btn && btn.name == 'cancel'){
11779             this.tickItems = Roo.apply([], this.item);
11780             this.collapse();
11781             return;
11782         }
11783         
11784         this.clearItem();
11785         
11786         var _this = this;
11787         
11788         Roo.each(this.tickItems, function(o){
11789             _this.addItem(o);
11790         });
11791         
11792         this.collapse();
11793         
11794     }
11795     
11796     
11797
11798     /** 
11799     * @cfg {Boolean} grow 
11800     * @hide 
11801     */
11802     /** 
11803     * @cfg {Number} growMin 
11804     * @hide 
11805     */
11806     /** 
11807     * @cfg {Number} growMax 
11808     * @hide 
11809     */
11810     /**
11811      * @hide
11812      * @method autoSize
11813      */
11814 });
11815 /*
11816  * Based on:
11817  * Ext JS Library 1.1.1
11818  * Copyright(c) 2006-2007, Ext JS, LLC.
11819  *
11820  * Originally Released Under LGPL - original licence link has changed is not relivant.
11821  *
11822  * Fork - LGPL
11823  * <script type="text/javascript">
11824  */
11825
11826 /**
11827  * @class Roo.View
11828  * @extends Roo.util.Observable
11829  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
11830  * This class also supports single and multi selection modes. <br>
11831  * Create a data model bound view:
11832  <pre><code>
11833  var store = new Roo.data.Store(...);
11834
11835  var view = new Roo.View({
11836     el : "my-element",
11837     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
11838  
11839     singleSelect: true,
11840     selectedClass: "ydataview-selected",
11841     store: store
11842  });
11843
11844  // listen for node click?
11845  view.on("click", function(vw, index, node, e){
11846  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
11847  });
11848
11849  // load XML data
11850  dataModel.load("foobar.xml");
11851  </code></pre>
11852  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
11853  * <br><br>
11854  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
11855  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
11856  * 
11857  * Note: old style constructor is still suported (container, template, config)
11858  * 
11859  * @constructor
11860  * Create a new View
11861  * @param {Object} config The config object
11862  * 
11863  */
11864 Roo.View = function(config, depreciated_tpl, depreciated_config){
11865     
11866     this.parent = false;
11867     
11868     if (typeof(depreciated_tpl) == 'undefined') {
11869         // new way.. - universal constructor.
11870         Roo.apply(this, config);
11871         this.el  = Roo.get(this.el);
11872     } else {
11873         // old format..
11874         this.el  = Roo.get(config);
11875         this.tpl = depreciated_tpl;
11876         Roo.apply(this, depreciated_config);
11877     }
11878     this.wrapEl  = this.el.wrap().wrap();
11879     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
11880     
11881     
11882     if(typeof(this.tpl) == "string"){
11883         this.tpl = new Roo.Template(this.tpl);
11884     } else {
11885         // support xtype ctors..
11886         this.tpl = new Roo.factory(this.tpl, Roo);
11887     }
11888     
11889     
11890     this.tpl.compile();
11891     
11892     /** @private */
11893     this.addEvents({
11894         /**
11895          * @event beforeclick
11896          * Fires before a click is processed. Returns false to cancel the default action.
11897          * @param {Roo.View} this
11898          * @param {Number} index The index of the target node
11899          * @param {HTMLElement} node The target node
11900          * @param {Roo.EventObject} e The raw event object
11901          */
11902             "beforeclick" : true,
11903         /**
11904          * @event click
11905          * Fires when a template node is clicked.
11906          * @param {Roo.View} this
11907          * @param {Number} index The index of the target node
11908          * @param {HTMLElement} node The target node
11909          * @param {Roo.EventObject} e The raw event object
11910          */
11911             "click" : true,
11912         /**
11913          * @event dblclick
11914          * Fires when a template node is double clicked.
11915          * @param {Roo.View} this
11916          * @param {Number} index The index of the target node
11917          * @param {HTMLElement} node The target node
11918          * @param {Roo.EventObject} e The raw event object
11919          */
11920             "dblclick" : true,
11921         /**
11922          * @event contextmenu
11923          * Fires when a template node is right clicked.
11924          * @param {Roo.View} this
11925          * @param {Number} index The index of the target node
11926          * @param {HTMLElement} node The target node
11927          * @param {Roo.EventObject} e The raw event object
11928          */
11929             "contextmenu" : true,
11930         /**
11931          * @event selectionchange
11932          * Fires when the selected nodes change.
11933          * @param {Roo.View} this
11934          * @param {Array} selections Array of the selected nodes
11935          */
11936             "selectionchange" : true,
11937     
11938         /**
11939          * @event beforeselect
11940          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
11941          * @param {Roo.View} this
11942          * @param {HTMLElement} node The node to be selected
11943          * @param {Array} selections Array of currently selected nodes
11944          */
11945             "beforeselect" : true,
11946         /**
11947          * @event preparedata
11948          * Fires on every row to render, to allow you to change the data.
11949          * @param {Roo.View} this
11950          * @param {Object} data to be rendered (change this)
11951          */
11952           "preparedata" : true
11953           
11954           
11955         });
11956
11957
11958
11959     this.el.on({
11960         "click": this.onClick,
11961         "dblclick": this.onDblClick,
11962         "contextmenu": this.onContextMenu,
11963         scope:this
11964     });
11965
11966     this.selections = [];
11967     this.nodes = [];
11968     this.cmp = new Roo.CompositeElementLite([]);
11969     if(this.store){
11970         this.store = Roo.factory(this.store, Roo.data);
11971         this.setStore(this.store, true);
11972     }
11973     
11974     if ( this.footer && this.footer.xtype) {
11975            
11976          var fctr = this.wrapEl.appendChild(document.createElement("div"));
11977         
11978         this.footer.dataSource = this.store
11979         this.footer.container = fctr;
11980         this.footer = Roo.factory(this.footer, Roo);
11981         fctr.insertFirst(this.el);
11982         
11983         // this is a bit insane - as the paging toolbar seems to detach the el..
11984 //        dom.parentNode.parentNode.parentNode
11985          // they get detached?
11986     }
11987     
11988     
11989     Roo.View.superclass.constructor.call(this);
11990     
11991     
11992 };
11993
11994 Roo.extend(Roo.View, Roo.util.Observable, {
11995     
11996      /**
11997      * @cfg {Roo.data.Store} store Data store to load data from.
11998      */
11999     store : false,
12000     
12001     /**
12002      * @cfg {String|Roo.Element} el The container element.
12003      */
12004     el : '',
12005     
12006     /**
12007      * @cfg {String|Roo.Template} tpl The template used by this View 
12008      */
12009     tpl : false,
12010     /**
12011      * @cfg {String} dataName the named area of the template to use as the data area
12012      *                          Works with domtemplates roo-name="name"
12013      */
12014     dataName: false,
12015     /**
12016      * @cfg {String} selectedClass The css class to add to selected nodes
12017      */
12018     selectedClass : "x-view-selected",
12019      /**
12020      * @cfg {String} emptyText The empty text to show when nothing is loaded.
12021      */
12022     emptyText : "",
12023     
12024     /**
12025      * @cfg {String} text to display on mask (default Loading)
12026      */
12027     mask : false,
12028     /**
12029      * @cfg {Boolean} multiSelect Allow multiple selection
12030      */
12031     multiSelect : false,
12032     /**
12033      * @cfg {Boolean} singleSelect Allow single selection
12034      */
12035     singleSelect:  false,
12036     
12037     /**
12038      * @cfg {Boolean} toggleSelect - selecting 
12039      */
12040     toggleSelect : false,
12041     
12042     /**
12043      * @cfg {Boolean} tickable - selecting 
12044      */
12045     tickable : false,
12046     
12047     /**
12048      * Returns the element this view is bound to.
12049      * @return {Roo.Element}
12050      */
12051     getEl : function(){
12052         return this.wrapEl;
12053     },
12054     
12055     
12056
12057     /**
12058      * Refreshes the view. - called by datachanged on the store. - do not call directly.
12059      */
12060     refresh : function(){
12061         Roo.log('refresh');
12062         var t = this.tpl;
12063         
12064         // if we are using something like 'domtemplate', then
12065         // the what gets used is:
12066         // t.applySubtemplate(NAME, data, wrapping data..)
12067         // the outer template then get' applied with
12068         //     the store 'extra data'
12069         // and the body get's added to the
12070         //      roo-name="data" node?
12071         //      <span class='roo-tpl-{name}'></span> ?????
12072         
12073         
12074         
12075         this.clearSelections();
12076         this.el.update("");
12077         var html = [];
12078         var records = this.store.getRange();
12079         if(records.length < 1) {
12080             
12081             // is this valid??  = should it render a template??
12082             
12083             this.el.update(this.emptyText);
12084             return;
12085         }
12086         var el = this.el;
12087         if (this.dataName) {
12088             this.el.update(t.apply(this.store.meta)); //????
12089             el = this.el.child('.roo-tpl-' + this.dataName);
12090         }
12091         
12092         for(var i = 0, len = records.length; i < len; i++){
12093             var data = this.prepareData(records[i].data, i, records[i]);
12094             this.fireEvent("preparedata", this, data, i, records[i]);
12095             
12096             var d = Roo.apply({}, data);
12097             
12098             if(this.tickable){
12099                 Roo.apply(d, {'roo-id' : Roo.id()});
12100                 
12101                 var _this = this;
12102             
12103                 Roo.each(this.parent.item, function(item){
12104                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
12105                         return;
12106                     }
12107                     Roo.apply(d, {'roo-data-checked' : 'checked'});
12108                 });
12109             }
12110             
12111             html[html.length] = Roo.util.Format.trim(
12112                 this.dataName ?
12113                     t.applySubtemplate(this.dataName, d, this.store.meta) :
12114                     t.apply(d)
12115             );
12116         }
12117         
12118         
12119         
12120         el.update(html.join(""));
12121         this.nodes = el.dom.childNodes;
12122         this.updateIndexes(0);
12123     },
12124     
12125
12126     /**
12127      * Function to override to reformat the data that is sent to
12128      * the template for each node.
12129      * DEPRICATED - use the preparedata event handler.
12130      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
12131      * a JSON object for an UpdateManager bound view).
12132      */
12133     prepareData : function(data, index, record)
12134     {
12135         this.fireEvent("preparedata", this, data, index, record);
12136         return data;
12137     },
12138
12139     onUpdate : function(ds, record){
12140          Roo.log('on update');   
12141         this.clearSelections();
12142         var index = this.store.indexOf(record);
12143         var n = this.nodes[index];
12144         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
12145         n.parentNode.removeChild(n);
12146         this.updateIndexes(index, index);
12147     },
12148
12149     
12150     
12151 // --------- FIXME     
12152     onAdd : function(ds, records, index)
12153     {
12154         Roo.log(['on Add', ds, records, index] );        
12155         this.clearSelections();
12156         if(this.nodes.length == 0){
12157             this.refresh();
12158             return;
12159         }
12160         var n = this.nodes[index];
12161         for(var i = 0, len = records.length; i < len; i++){
12162             var d = this.prepareData(records[i].data, i, records[i]);
12163             if(n){
12164                 this.tpl.insertBefore(n, d);
12165             }else{
12166                 
12167                 this.tpl.append(this.el, d);
12168             }
12169         }
12170         this.updateIndexes(index);
12171     },
12172
12173     onRemove : function(ds, record, index){
12174         Roo.log('onRemove');
12175         this.clearSelections();
12176         var el = this.dataName  ?
12177             this.el.child('.roo-tpl-' + this.dataName) :
12178             this.el; 
12179         
12180         el.dom.removeChild(this.nodes[index]);
12181         this.updateIndexes(index);
12182     },
12183
12184     /**
12185      * Refresh an individual node.
12186      * @param {Number} index
12187      */
12188     refreshNode : function(index){
12189         this.onUpdate(this.store, this.store.getAt(index));
12190     },
12191
12192     updateIndexes : function(startIndex, endIndex){
12193         var ns = this.nodes;
12194         startIndex = startIndex || 0;
12195         endIndex = endIndex || ns.length - 1;
12196         for(var i = startIndex; i <= endIndex; i++){
12197             ns[i].nodeIndex = i;
12198         }
12199     },
12200
12201     /**
12202      * Changes the data store this view uses and refresh the view.
12203      * @param {Store} store
12204      */
12205     setStore : function(store, initial){
12206         if(!initial && this.store){
12207             this.store.un("datachanged", this.refresh);
12208             this.store.un("add", this.onAdd);
12209             this.store.un("remove", this.onRemove);
12210             this.store.un("update", this.onUpdate);
12211             this.store.un("clear", this.refresh);
12212             this.store.un("beforeload", this.onBeforeLoad);
12213             this.store.un("load", this.onLoad);
12214             this.store.un("loadexception", this.onLoad);
12215         }
12216         if(store){
12217           
12218             store.on("datachanged", this.refresh, this);
12219             store.on("add", this.onAdd, this);
12220             store.on("remove", this.onRemove, this);
12221             store.on("update", this.onUpdate, this);
12222             store.on("clear", this.refresh, this);
12223             store.on("beforeload", this.onBeforeLoad, this);
12224             store.on("load", this.onLoad, this);
12225             store.on("loadexception", this.onLoad, this);
12226         }
12227         
12228         if(store){
12229             this.refresh();
12230         }
12231     },
12232     /**
12233      * onbeforeLoad - masks the loading area.
12234      *
12235      */
12236     onBeforeLoad : function(store,opts)
12237     {
12238          Roo.log('onBeforeLoad');   
12239         if (!opts.add) {
12240             this.el.update("");
12241         }
12242         this.el.mask(this.mask ? this.mask : "Loading" ); 
12243     },
12244     onLoad : function ()
12245     {
12246         this.el.unmask();
12247     },
12248     
12249
12250     /**
12251      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
12252      * @param {HTMLElement} node
12253      * @return {HTMLElement} The template node
12254      */
12255     findItemFromChild : function(node){
12256         var el = this.dataName  ?
12257             this.el.child('.roo-tpl-' + this.dataName,true) :
12258             this.el.dom; 
12259         
12260         if(!node || node.parentNode == el){
12261                     return node;
12262             }
12263             var p = node.parentNode;
12264             while(p && p != el){
12265             if(p.parentNode == el){
12266                 return p;
12267             }
12268             p = p.parentNode;
12269         }
12270             return null;
12271     },
12272
12273     /** @ignore */
12274     onClick : function(e){
12275         var item = this.findItemFromChild(e.getTarget());
12276         if(item){
12277             var index = this.indexOf(item);
12278             if(this.onItemClick(item, index, e) !== false){
12279                 this.fireEvent("click", this, index, item, e);
12280             }
12281         }else{
12282             this.clearSelections();
12283         }
12284     },
12285
12286     /** @ignore */
12287     onContextMenu : function(e){
12288         var item = this.findItemFromChild(e.getTarget());
12289         if(item){
12290             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
12291         }
12292     },
12293
12294     /** @ignore */
12295     onDblClick : function(e){
12296         var item = this.findItemFromChild(e.getTarget());
12297         if(item){
12298             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
12299         }
12300     },
12301
12302     onItemClick : function(item, index, e)
12303     {
12304         if(this.fireEvent("beforeclick", this, index, item, e) === false){
12305             return false;
12306         }
12307         if (this.toggleSelect) {
12308             var m = this.isSelected(item) ? 'unselect' : 'select';
12309             Roo.log(m);
12310             var _t = this;
12311             _t[m](item, true, false);
12312             return true;
12313         }
12314         if(this.multiSelect || this.singleSelect){
12315             if(this.multiSelect && e.shiftKey && this.lastSelection){
12316                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
12317             }else{
12318                 this.select(item, this.multiSelect && e.ctrlKey);
12319                 this.lastSelection = item;
12320             }
12321             
12322             if(!this.tickable){
12323                 e.preventDefault();
12324             }
12325             
12326         }
12327         return true;
12328     },
12329
12330     /**
12331      * Get the number of selected nodes.
12332      * @return {Number}
12333      */
12334     getSelectionCount : function(){
12335         return this.selections.length;
12336     },
12337
12338     /**
12339      * Get the currently selected nodes.
12340      * @return {Array} An array of HTMLElements
12341      */
12342     getSelectedNodes : function(){
12343         return this.selections;
12344     },
12345
12346     /**
12347      * Get the indexes of the selected nodes.
12348      * @return {Array}
12349      */
12350     getSelectedIndexes : function(){
12351         var indexes = [], s = this.selections;
12352         for(var i = 0, len = s.length; i < len; i++){
12353             indexes.push(s[i].nodeIndex);
12354         }
12355         return indexes;
12356     },
12357
12358     /**
12359      * Clear all selections
12360      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
12361      */
12362     clearSelections : function(suppressEvent){
12363         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
12364             this.cmp.elements = this.selections;
12365             this.cmp.removeClass(this.selectedClass);
12366             this.selections = [];
12367             if(!suppressEvent){
12368                 this.fireEvent("selectionchange", this, this.selections);
12369             }
12370         }
12371     },
12372
12373     /**
12374      * Returns true if the passed node is selected
12375      * @param {HTMLElement/Number} node The node or node index
12376      * @return {Boolean}
12377      */
12378     isSelected : function(node){
12379         var s = this.selections;
12380         if(s.length < 1){
12381             return false;
12382         }
12383         node = this.getNode(node);
12384         return s.indexOf(node) !== -1;
12385     },
12386
12387     /**
12388      * Selects nodes.
12389      * @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
12390      * @param {Boolean} keepExisting (optional) true to keep existing selections
12391      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12392      */
12393     select : function(nodeInfo, keepExisting, suppressEvent){
12394         if(nodeInfo instanceof Array){
12395             if(!keepExisting){
12396                 this.clearSelections(true);
12397             }
12398             for(var i = 0, len = nodeInfo.length; i < len; i++){
12399                 this.select(nodeInfo[i], true, true);
12400             }
12401             return;
12402         } 
12403         var node = this.getNode(nodeInfo);
12404         if(!node || this.isSelected(node)){
12405             return; // already selected.
12406         }
12407         if(!keepExisting){
12408             this.clearSelections(true);
12409         }
12410         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
12411             Roo.fly(node).addClass(this.selectedClass);
12412             this.selections.push(node);
12413             if(!suppressEvent){
12414                 this.fireEvent("selectionchange", this, this.selections);
12415             }
12416         }
12417         
12418         
12419     },
12420       /**
12421      * Unselects nodes.
12422      * @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
12423      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
12424      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12425      */
12426     unselect : function(nodeInfo, keepExisting, suppressEvent)
12427     {
12428         if(nodeInfo instanceof Array){
12429             Roo.each(this.selections, function(s) {
12430                 this.unselect(s, nodeInfo);
12431             }, this);
12432             return;
12433         }
12434         var node = this.getNode(nodeInfo);
12435         if(!node || !this.isSelected(node)){
12436             Roo.log("not selected");
12437             return; // not selected.
12438         }
12439         // fireevent???
12440         var ns = [];
12441         Roo.each(this.selections, function(s) {
12442             if (s == node ) {
12443                 Roo.fly(node).removeClass(this.selectedClass);
12444
12445                 return;
12446             }
12447             ns.push(s);
12448         },this);
12449         
12450         this.selections= ns;
12451         this.fireEvent("selectionchange", this, this.selections);
12452     },
12453
12454     /**
12455      * Gets a template node.
12456      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12457      * @return {HTMLElement} The node or null if it wasn't found
12458      */
12459     getNode : function(nodeInfo){
12460         if(typeof nodeInfo == "string"){
12461             return document.getElementById(nodeInfo);
12462         }else if(typeof nodeInfo == "number"){
12463             return this.nodes[nodeInfo];
12464         }
12465         return nodeInfo;
12466     },
12467
12468     /**
12469      * Gets a range template nodes.
12470      * @param {Number} startIndex
12471      * @param {Number} endIndex
12472      * @return {Array} An array of nodes
12473      */
12474     getNodes : function(start, end){
12475         var ns = this.nodes;
12476         start = start || 0;
12477         end = typeof end == "undefined" ? ns.length - 1 : end;
12478         var nodes = [];
12479         if(start <= end){
12480             for(var i = start; i <= end; i++){
12481                 nodes.push(ns[i]);
12482             }
12483         } else{
12484             for(var i = start; i >= end; i--){
12485                 nodes.push(ns[i]);
12486             }
12487         }
12488         return nodes;
12489     },
12490
12491     /**
12492      * Finds the index of the passed node
12493      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12494      * @return {Number} The index of the node or -1
12495      */
12496     indexOf : function(node){
12497         node = this.getNode(node);
12498         if(typeof node.nodeIndex == "number"){
12499             return node.nodeIndex;
12500         }
12501         var ns = this.nodes;
12502         for(var i = 0, len = ns.length; i < len; i++){
12503             if(ns[i] == node){
12504                 return i;
12505             }
12506         }
12507         return -1;
12508     }
12509 });
12510 /*
12511  * - LGPL
12512  *
12513  * based on jquery fullcalendar
12514  * 
12515  */
12516
12517 Roo.bootstrap = Roo.bootstrap || {};
12518 /**
12519  * @class Roo.bootstrap.Calendar
12520  * @extends Roo.bootstrap.Component
12521  * Bootstrap Calendar class
12522  * @cfg {Boolean} loadMask (true|false) default false
12523  * @cfg {Object} header generate the user specific header of the calendar, default false
12524
12525  * @constructor
12526  * Create a new Container
12527  * @param {Object} config The config object
12528  */
12529
12530
12531
12532 Roo.bootstrap.Calendar = function(config){
12533     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
12534      this.addEvents({
12535         /**
12536              * @event select
12537              * Fires when a date is selected
12538              * @param {DatePicker} this
12539              * @param {Date} date The selected date
12540              */
12541         'select': true,
12542         /**
12543              * @event monthchange
12544              * Fires when the displayed month changes 
12545              * @param {DatePicker} this
12546              * @param {Date} date The selected month
12547              */
12548         'monthchange': true,
12549         /**
12550              * @event evententer
12551              * Fires when mouse over an event
12552              * @param {Calendar} this
12553              * @param {event} Event
12554              */
12555         'evententer': true,
12556         /**
12557              * @event eventleave
12558              * Fires when the mouse leaves an
12559              * @param {Calendar} this
12560              * @param {event}
12561              */
12562         'eventleave': true,
12563         /**
12564              * @event eventclick
12565              * Fires when the mouse click an
12566              * @param {Calendar} this
12567              * @param {event}
12568              */
12569         'eventclick': true
12570         
12571     });
12572
12573 };
12574
12575 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
12576     
12577      /**
12578      * @cfg {Number} startDay
12579      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
12580      */
12581     startDay : 0,
12582     
12583     loadMask : false,
12584     
12585     header : false,
12586       
12587     getAutoCreate : function(){
12588         
12589         
12590         var fc_button = function(name, corner, style, content ) {
12591             return Roo.apply({},{
12592                 tag : 'span',
12593                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
12594                          (corner.length ?
12595                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
12596                             ''
12597                         ),
12598                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
12599                 unselectable: 'on'
12600             });
12601         };
12602         
12603         var header = {};
12604         
12605         if(!this.header){
12606             header = {
12607                 tag : 'table',
12608                 cls : 'fc-header',
12609                 style : 'width:100%',
12610                 cn : [
12611                     {
12612                         tag: 'tr',
12613                         cn : [
12614                             {
12615                                 tag : 'td',
12616                                 cls : 'fc-header-left',
12617                                 cn : [
12618                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
12619                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
12620                                     { tag: 'span', cls: 'fc-header-space' },
12621                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
12622
12623
12624                                 ]
12625                             },
12626
12627                             {
12628                                 tag : 'td',
12629                                 cls : 'fc-header-center',
12630                                 cn : [
12631                                     {
12632                                         tag: 'span',
12633                                         cls: 'fc-header-title',
12634                                         cn : {
12635                                             tag: 'H2',
12636                                             html : 'month / year'
12637                                         }
12638                                     }
12639
12640                                 ]
12641                             },
12642                             {
12643                                 tag : 'td',
12644                                 cls : 'fc-header-right',
12645                                 cn : [
12646                               /*      fc_button('month', 'left', '', 'month' ),
12647                                     fc_button('week', '', '', 'week' ),
12648                                     fc_button('day', 'right', '', 'day' )
12649                                 */    
12650
12651                                 ]
12652                             }
12653
12654                         ]
12655                     }
12656                 ]
12657             };
12658         }
12659         
12660         header = this.header;
12661         
12662        
12663         var cal_heads = function() {
12664             var ret = [];
12665             // fixme - handle this.
12666             
12667             for (var i =0; i < Date.dayNames.length; i++) {
12668                 var d = Date.dayNames[i];
12669                 ret.push({
12670                     tag: 'th',
12671                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
12672                     html : d.substring(0,3)
12673                 });
12674                 
12675             }
12676             ret[0].cls += ' fc-first';
12677             ret[6].cls += ' fc-last';
12678             return ret;
12679         };
12680         var cal_cell = function(n) {
12681             return  {
12682                 tag: 'td',
12683                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
12684                 cn : [
12685                     {
12686                         cn : [
12687                             {
12688                                 cls: 'fc-day-number',
12689                                 html: 'D'
12690                             },
12691                             {
12692                                 cls: 'fc-day-content',
12693                              
12694                                 cn : [
12695                                      {
12696                                         style: 'position: relative;' // height: 17px;
12697                                     }
12698                                 ]
12699                             }
12700                             
12701                             
12702                         ]
12703                     }
12704                 ]
12705                 
12706             }
12707         };
12708         var cal_rows = function() {
12709             
12710             var ret = []
12711             for (var r = 0; r < 6; r++) {
12712                 var row= {
12713                     tag : 'tr',
12714                     cls : 'fc-week',
12715                     cn : []
12716                 };
12717                 
12718                 for (var i =0; i < Date.dayNames.length; i++) {
12719                     var d = Date.dayNames[i];
12720                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
12721
12722                 }
12723                 row.cn[0].cls+=' fc-first';
12724                 row.cn[0].cn[0].style = 'min-height:90px';
12725                 row.cn[6].cls+=' fc-last';
12726                 ret.push(row);
12727                 
12728             }
12729             ret[0].cls += ' fc-first';
12730             ret[4].cls += ' fc-prev-last';
12731             ret[5].cls += ' fc-last';
12732             return ret;
12733             
12734         };
12735         
12736         var cal_table = {
12737             tag: 'table',
12738             cls: 'fc-border-separate',
12739             style : 'width:100%',
12740             cellspacing  : 0,
12741             cn : [
12742                 { 
12743                     tag: 'thead',
12744                     cn : [
12745                         { 
12746                             tag: 'tr',
12747                             cls : 'fc-first fc-last',
12748                             cn : cal_heads()
12749                         }
12750                     ]
12751                 },
12752                 { 
12753                     tag: 'tbody',
12754                     cn : cal_rows()
12755                 }
12756                   
12757             ]
12758         };
12759          
12760          var cfg = {
12761             cls : 'fc fc-ltr',
12762             cn : [
12763                 header,
12764                 {
12765                     cls : 'fc-content',
12766                     style : "position: relative;",
12767                     cn : [
12768                         {
12769                             cls : 'fc-view fc-view-month fc-grid',
12770                             style : 'position: relative',
12771                             unselectable : 'on',
12772                             cn : [
12773                                 {
12774                                     cls : 'fc-event-container',
12775                                     style : 'position:absolute;z-index:8;top:0;left:0;'
12776                                 },
12777                                 cal_table
12778                             ]
12779                         }
12780                     ]
12781     
12782                 }
12783            ] 
12784             
12785         };
12786         
12787          
12788         
12789         return cfg;
12790     },
12791     
12792     
12793     initEvents : function()
12794     {
12795         if(!this.store){
12796             throw "can not find store for calendar";
12797         }
12798         
12799         var mark = {
12800             tag: "div",
12801             cls:"x-dlg-mask",
12802             style: "text-align:center",
12803             cn: [
12804                 {
12805                     tag: "div",
12806                     style: "background-color:white;width:50%;margin:250 auto",
12807                     cn: [
12808                         {
12809                             tag: "img",
12810                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
12811                         },
12812                         {
12813                             tag: "span",
12814                             html: "Loading"
12815                         }
12816                         
12817                     ]
12818                 }
12819             ]
12820         }
12821         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
12822         
12823         var size = this.el.select('.fc-content', true).first().getSize();
12824         this.maskEl.setSize(size.width, size.height);
12825         this.maskEl.enableDisplayMode("block");
12826         if(!this.loadMask){
12827             this.maskEl.hide();
12828         }
12829         
12830         this.store = Roo.factory(this.store, Roo.data);
12831         this.store.on('load', this.onLoad, this);
12832         this.store.on('beforeload', this.onBeforeLoad, this);
12833         
12834         this.resize();
12835         
12836         this.cells = this.el.select('.fc-day',true);
12837         //Roo.log(this.cells);
12838         this.textNodes = this.el.query('.fc-day-number');
12839         this.cells.addClassOnOver('fc-state-hover');
12840         
12841         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
12842         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
12843         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
12844         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
12845         
12846         this.on('monthchange', this.onMonthChange, this);
12847         
12848         this.update(new Date().clearTime());
12849     },
12850     
12851     resize : function() {
12852         var sz  = this.el.getSize();
12853         
12854         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
12855         this.el.select('.fc-day-content div',true).setHeight(34);
12856     },
12857     
12858     
12859     // private
12860     showPrevMonth : function(e){
12861         this.update(this.activeDate.add("mo", -1));
12862     },
12863     showToday : function(e){
12864         this.update(new Date().clearTime());
12865     },
12866     // private
12867     showNextMonth : function(e){
12868         this.update(this.activeDate.add("mo", 1));
12869     },
12870
12871     // private
12872     showPrevYear : function(){
12873         this.update(this.activeDate.add("y", -1));
12874     },
12875
12876     // private
12877     showNextYear : function(){
12878         this.update(this.activeDate.add("y", 1));
12879     },
12880
12881     
12882    // private
12883     update : function(date)
12884     {
12885         var vd = this.activeDate;
12886         this.activeDate = date;
12887 //        if(vd && this.el){
12888 //            var t = date.getTime();
12889 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
12890 //                Roo.log('using add remove');
12891 //                
12892 //                this.fireEvent('monthchange', this, date);
12893 //                
12894 //                this.cells.removeClass("fc-state-highlight");
12895 //                this.cells.each(function(c){
12896 //                   if(c.dateValue == t){
12897 //                       c.addClass("fc-state-highlight");
12898 //                       setTimeout(function(){
12899 //                            try{c.dom.firstChild.focus();}catch(e){}
12900 //                       }, 50);
12901 //                       return false;
12902 //                   }
12903 //                   return true;
12904 //                });
12905 //                return;
12906 //            }
12907 //        }
12908         
12909         var days = date.getDaysInMonth();
12910         
12911         var firstOfMonth = date.getFirstDateOfMonth();
12912         var startingPos = firstOfMonth.getDay()-this.startDay;
12913         
12914         if(startingPos < this.startDay){
12915             startingPos += 7;
12916         }
12917         
12918         var pm = date.add(Date.MONTH, -1);
12919         var prevStart = pm.getDaysInMonth()-startingPos;
12920 //        
12921         this.cells = this.el.select('.fc-day',true);
12922         this.textNodes = this.el.query('.fc-day-number');
12923         this.cells.addClassOnOver('fc-state-hover');
12924         
12925         var cells = this.cells.elements;
12926         var textEls = this.textNodes;
12927         
12928         Roo.each(cells, function(cell){
12929             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
12930         });
12931         
12932         days += startingPos;
12933
12934         // convert everything to numbers so it's fast
12935         var day = 86400000;
12936         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
12937         //Roo.log(d);
12938         //Roo.log(pm);
12939         //Roo.log(prevStart);
12940         
12941         var today = new Date().clearTime().getTime();
12942         var sel = date.clearTime().getTime();
12943         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
12944         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
12945         var ddMatch = this.disabledDatesRE;
12946         var ddText = this.disabledDatesText;
12947         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
12948         var ddaysText = this.disabledDaysText;
12949         var format = this.format;
12950         
12951         var setCellClass = function(cal, cell){
12952             cell.row = 0;
12953             cell.events = [];
12954             cell.more = [];
12955             //Roo.log('set Cell Class');
12956             cell.title = "";
12957             var t = d.getTime();
12958             
12959             //Roo.log(d);
12960             
12961             cell.dateValue = t;
12962             if(t == today){
12963                 cell.className += " fc-today";
12964                 cell.className += " fc-state-highlight";
12965                 cell.title = cal.todayText;
12966             }
12967             if(t == sel){
12968                 // disable highlight in other month..
12969                 //cell.className += " fc-state-highlight";
12970                 
12971             }
12972             // disabling
12973             if(t < min) {
12974                 cell.className = " fc-state-disabled";
12975                 cell.title = cal.minText;
12976                 return;
12977             }
12978             if(t > max) {
12979                 cell.className = " fc-state-disabled";
12980                 cell.title = cal.maxText;
12981                 return;
12982             }
12983             if(ddays){
12984                 if(ddays.indexOf(d.getDay()) != -1){
12985                     cell.title = ddaysText;
12986                     cell.className = " fc-state-disabled";
12987                 }
12988             }
12989             if(ddMatch && format){
12990                 var fvalue = d.dateFormat(format);
12991                 if(ddMatch.test(fvalue)){
12992                     cell.title = ddText.replace("%0", fvalue);
12993                     cell.className = " fc-state-disabled";
12994                 }
12995             }
12996             
12997             if (!cell.initialClassName) {
12998                 cell.initialClassName = cell.dom.className;
12999             }
13000             
13001             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
13002         };
13003
13004         var i = 0;
13005         
13006         for(; i < startingPos; i++) {
13007             textEls[i].innerHTML = (++prevStart);
13008             d.setDate(d.getDate()+1);
13009             
13010             cells[i].className = "fc-past fc-other-month";
13011             setCellClass(this, cells[i]);
13012         }
13013         
13014         var intDay = 0;
13015         
13016         for(; i < days; i++){
13017             intDay = i - startingPos + 1;
13018             textEls[i].innerHTML = (intDay);
13019             d.setDate(d.getDate()+1);
13020             
13021             cells[i].className = ''; // "x-date-active";
13022             setCellClass(this, cells[i]);
13023         }
13024         var extraDays = 0;
13025         
13026         for(; i < 42; i++) {
13027             textEls[i].innerHTML = (++extraDays);
13028             d.setDate(d.getDate()+1);
13029             
13030             cells[i].className = "fc-future fc-other-month";
13031             setCellClass(this, cells[i]);
13032         }
13033         
13034         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
13035         
13036         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
13037         
13038         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
13039         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
13040         
13041         if(totalRows != 6){
13042             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
13043             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
13044         }
13045         
13046         this.fireEvent('monthchange', this, date);
13047         
13048         
13049         /*
13050         if(!this.internalRender){
13051             var main = this.el.dom.firstChild;
13052             var w = main.offsetWidth;
13053             this.el.setWidth(w + this.el.getBorderWidth("lr"));
13054             Roo.fly(main).setWidth(w);
13055             this.internalRender = true;
13056             // opera does not respect the auto grow header center column
13057             // then, after it gets a width opera refuses to recalculate
13058             // without a second pass
13059             if(Roo.isOpera && !this.secondPass){
13060                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
13061                 this.secondPass = true;
13062                 this.update.defer(10, this, [date]);
13063             }
13064         }
13065         */
13066         
13067     },
13068     
13069     findCell : function(dt) {
13070         dt = dt.clearTime().getTime();
13071         var ret = false;
13072         this.cells.each(function(c){
13073             //Roo.log("check " +c.dateValue + '?=' + dt);
13074             if(c.dateValue == dt){
13075                 ret = c;
13076                 return false;
13077             }
13078             return true;
13079         });
13080         
13081         return ret;
13082     },
13083     
13084     findCells : function(ev) {
13085         var s = ev.start.clone().clearTime().getTime();
13086        // Roo.log(s);
13087         var e= ev.end.clone().clearTime().getTime();
13088        // Roo.log(e);
13089         var ret = [];
13090         this.cells.each(function(c){
13091              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
13092             
13093             if(c.dateValue > e){
13094                 return ;
13095             }
13096             if(c.dateValue < s){
13097                 return ;
13098             }
13099             ret.push(c);
13100         });
13101         
13102         return ret;    
13103     },
13104     
13105 //    findBestRow: function(cells)
13106 //    {
13107 //        var ret = 0;
13108 //        
13109 //        for (var i =0 ; i < cells.length;i++) {
13110 //            ret  = Math.max(cells[i].rows || 0,ret);
13111 //        }
13112 //        return ret;
13113 //        
13114 //    },
13115     
13116     
13117     addItem : function(ev)
13118     {
13119         // look for vertical location slot in
13120         var cells = this.findCells(ev);
13121         
13122 //        ev.row = this.findBestRow(cells);
13123         
13124         // work out the location.
13125         
13126         var crow = false;
13127         var rows = [];
13128         for(var i =0; i < cells.length; i++) {
13129             
13130             cells[i].row = cells[0].row;
13131             
13132             if(i == 0){
13133                 cells[i].row = cells[i].row + 1;
13134             }
13135             
13136             if (!crow) {
13137                 crow = {
13138                     start : cells[i],
13139                     end :  cells[i]
13140                 };
13141                 continue;
13142             }
13143             if (crow.start.getY() == cells[i].getY()) {
13144                 // on same row.
13145                 crow.end = cells[i];
13146                 continue;
13147             }
13148             // different row.
13149             rows.push(crow);
13150             crow = {
13151                 start: cells[i],
13152                 end : cells[i]
13153             };
13154             
13155         }
13156         
13157         rows.push(crow);
13158         ev.els = [];
13159         ev.rows = rows;
13160         ev.cells = cells;
13161         
13162         cells[0].events.push(ev);
13163         
13164         this.calevents.push(ev);
13165     },
13166     
13167     clearEvents: function() {
13168         
13169         if(!this.calevents){
13170             return;
13171         }
13172         
13173         Roo.each(this.cells.elements, function(c){
13174             c.row = 0;
13175             c.events = [];
13176             c.more = [];
13177         });
13178         
13179         Roo.each(this.calevents, function(e) {
13180             Roo.each(e.els, function(el) {
13181                 el.un('mouseenter' ,this.onEventEnter, this);
13182                 el.un('mouseleave' ,this.onEventLeave, this);
13183                 el.remove();
13184             },this);
13185         },this);
13186         
13187         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
13188             e.remove();
13189         });
13190         
13191     },
13192     
13193     renderEvents: function()
13194     {   
13195         var _this = this;
13196         
13197         this.cells.each(function(c) {
13198             
13199             if(c.row < 5){
13200                 return;
13201             }
13202             
13203             var ev = c.events;
13204             
13205             var r = 4;
13206             if(c.row != c.events.length){
13207                 r = 4 - (4 - (c.row - c.events.length));
13208             }
13209             
13210             c.events = ev.slice(0, r);
13211             c.more = ev.slice(r);
13212             
13213             if(c.more.length && c.more.length == 1){
13214                 c.events.push(c.more.pop());
13215             }
13216             
13217             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
13218             
13219         });
13220             
13221         this.cells.each(function(c) {
13222             
13223             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
13224             
13225             
13226             for (var e = 0; e < c.events.length; e++){
13227                 var ev = c.events[e];
13228                 var rows = ev.rows;
13229                 
13230                 for(var i = 0; i < rows.length; i++) {
13231                 
13232                     // how many rows should it span..
13233
13234                     var  cfg = {
13235                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
13236                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
13237
13238                         unselectable : "on",
13239                         cn : [
13240                             {
13241                                 cls: 'fc-event-inner',
13242                                 cn : [
13243     //                                {
13244     //                                  tag:'span',
13245     //                                  cls: 'fc-event-time',
13246     //                                  html : cells.length > 1 ? '' : ev.time
13247     //                                },
13248                                     {
13249                                       tag:'span',
13250                                       cls: 'fc-event-title',
13251                                       html : String.format('{0}', ev.title)
13252                                     }
13253
13254
13255                                 ]
13256                             },
13257                             {
13258                                 cls: 'ui-resizable-handle ui-resizable-e',
13259                                 html : '&nbsp;&nbsp;&nbsp'
13260                             }
13261
13262                         ]
13263                     };
13264
13265                     if (i == 0) {
13266                         cfg.cls += ' fc-event-start';
13267                     }
13268                     if ((i+1) == rows.length) {
13269                         cfg.cls += ' fc-event-end';
13270                     }
13271
13272                     var ctr = _this.el.select('.fc-event-container',true).first();
13273                     var cg = ctr.createChild(cfg);
13274
13275                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
13276                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
13277
13278                     var r = (c.more.length) ? 1 : 0;
13279                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
13280                     cg.setWidth(ebox.right - sbox.x -2);
13281
13282                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
13283                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
13284                     cg.on('click', _this.onEventClick, _this, ev);
13285
13286                     ev.els.push(cg);
13287                     
13288                 }
13289                 
13290             }
13291             
13292             
13293             if(c.more.length){
13294                 var  cfg = {
13295                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
13296                     style : 'position: absolute',
13297                     unselectable : "on",
13298                     cn : [
13299                         {
13300                             cls: 'fc-event-inner',
13301                             cn : [
13302                                 {
13303                                   tag:'span',
13304                                   cls: 'fc-event-title',
13305                                   html : 'More'
13306                                 }
13307
13308
13309                             ]
13310                         },
13311                         {
13312                             cls: 'ui-resizable-handle ui-resizable-e',
13313                             html : '&nbsp;&nbsp;&nbsp'
13314                         }
13315
13316                     ]
13317                 };
13318
13319                 var ctr = _this.el.select('.fc-event-container',true).first();
13320                 var cg = ctr.createChild(cfg);
13321
13322                 var sbox = c.select('.fc-day-content',true).first().getBox();
13323                 var ebox = c.select('.fc-day-content',true).first().getBox();
13324                 //Roo.log(cg);
13325                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
13326                 cg.setWidth(ebox.right - sbox.x -2);
13327
13328                 cg.on('click', _this.onMoreEventClick, _this, c.more);
13329                 
13330             }
13331             
13332         });
13333         
13334         
13335         
13336     },
13337     
13338     onEventEnter: function (e, el,event,d) {
13339         this.fireEvent('evententer', this, el, event);
13340     },
13341     
13342     onEventLeave: function (e, el,event,d) {
13343         this.fireEvent('eventleave', this, el, event);
13344     },
13345     
13346     onEventClick: function (e, el,event,d) {
13347         this.fireEvent('eventclick', this, el, event);
13348     },
13349     
13350     onMonthChange: function () {
13351         this.store.load();
13352     },
13353     
13354     onMoreEventClick: function(e, el, more)
13355     {
13356         var _this = this;
13357         
13358         this.calpopover.placement = 'right';
13359         this.calpopover.setTitle('More');
13360         
13361         this.calpopover.setContent('');
13362         
13363         var ctr = this.calpopover.el.select('.popover-content', true).first();
13364         
13365         Roo.each(more, function(m){
13366             var cfg = {
13367                 cls : 'fc-event-hori fc-event-draggable',
13368                 html : m.title
13369             }
13370             var cg = ctr.createChild(cfg);
13371             
13372             cg.on('click', _this.onEventClick, _this, m);
13373         });
13374         
13375         this.calpopover.show(el);
13376         
13377         
13378     },
13379     
13380     onLoad: function () 
13381     {   
13382         this.calevents = [];
13383         var cal = this;
13384         
13385         if(this.store.getCount() > 0){
13386             this.store.data.each(function(d){
13387                cal.addItem({
13388                     id : d.data.id,
13389                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
13390                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
13391                     time : d.data.start_time,
13392                     title : d.data.title,
13393                     description : d.data.description,
13394                     venue : d.data.venue
13395                 });
13396             });
13397         }
13398         
13399         this.renderEvents();
13400         
13401         if(this.calevents.length && this.loadMask){
13402             this.maskEl.hide();
13403         }
13404     },
13405     
13406     onBeforeLoad: function()
13407     {
13408         this.clearEvents();
13409         if(this.loadMask){
13410             this.maskEl.show();
13411         }
13412     }
13413 });
13414
13415  
13416  /*
13417  * - LGPL
13418  *
13419  * element
13420  * 
13421  */
13422
13423 /**
13424  * @class Roo.bootstrap.Popover
13425  * @extends Roo.bootstrap.Component
13426  * Bootstrap Popover class
13427  * @cfg {String} html contents of the popover   (or false to use children..)
13428  * @cfg {String} title of popover (or false to hide)
13429  * @cfg {String} placement how it is placed
13430  * @cfg {String} trigger click || hover (or false to trigger manually)
13431  * @cfg {String} over what (parent or false to trigger manually.)
13432  * 
13433  * @constructor
13434  * Create a new Popover
13435  * @param {Object} config The config object
13436  */
13437
13438 Roo.bootstrap.Popover = function(config){
13439     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
13440 };
13441
13442 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
13443     
13444     title: 'Fill in a title',
13445     html: false,
13446     
13447     placement : 'right',
13448     trigger : 'hover', // hover
13449     
13450     over: 'parent',
13451     
13452     can_build_overlaid : false,
13453     
13454     getChildContainer : function()
13455     {
13456         return this.el.select('.popover-content',true).first();
13457     },
13458     
13459     getAutoCreate : function(){
13460          Roo.log('make popover?');
13461         var cfg = {
13462            cls : 'popover roo-dynamic',
13463            style: 'display:block',
13464            cn : [
13465                 {
13466                     cls : 'arrow'
13467                 },
13468                 {
13469                     cls : 'popover-inner',
13470                     cn : [
13471                         {
13472                             tag: 'h3',
13473                             cls: 'popover-title',
13474                             html : this.title
13475                         },
13476                         {
13477                             cls : 'popover-content',
13478                             html : this.html
13479                         }
13480                     ]
13481                     
13482                 }
13483            ]
13484         };
13485         
13486         return cfg;
13487     },
13488     setTitle: function(str)
13489     {
13490         this.el.select('.popover-title',true).first().dom.innerHTML = str;
13491     },
13492     setContent: function(str)
13493     {
13494         this.el.select('.popover-content',true).first().dom.innerHTML = str;
13495     },
13496     // as it get's added to the bottom of the page.
13497     onRender : function(ct, position)
13498     {
13499         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
13500         if(!this.el){
13501             var cfg = Roo.apply({},  this.getAutoCreate());
13502             cfg.id = Roo.id();
13503             
13504             if (this.cls) {
13505                 cfg.cls += ' ' + this.cls;
13506             }
13507             if (this.style) {
13508                 cfg.style = this.style;
13509             }
13510             Roo.log("adding to ")
13511             this.el = Roo.get(document.body).createChild(cfg, position);
13512             Roo.log(this.el);
13513         }
13514         this.initEvents();
13515     },
13516     
13517     initEvents : function()
13518     {
13519         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
13520         this.el.enableDisplayMode('block');
13521         this.el.hide();
13522         if (this.over === false) {
13523             return; 
13524         }
13525         if (this.triggers === false) {
13526             return;
13527         }
13528         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13529         var triggers = this.trigger ? this.trigger.split(' ') : [];
13530         Roo.each(triggers, function(trigger) {
13531         
13532             if (trigger == 'click') {
13533                 on_el.on('click', this.toggle, this);
13534             } else if (trigger != 'manual') {
13535                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
13536                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
13537       
13538                 on_el.on(eventIn  ,this.enter, this);
13539                 on_el.on(eventOut, this.leave, this);
13540             }
13541         }, this);
13542         
13543     },
13544     
13545     
13546     // private
13547     timeout : null,
13548     hoverState : null,
13549     
13550     toggle : function () {
13551         this.hoverState == 'in' ? this.leave() : this.enter();
13552     },
13553     
13554     enter : function () {
13555        
13556     
13557         clearTimeout(this.timeout);
13558     
13559         this.hoverState = 'in'
13560     
13561         if (!this.delay || !this.delay.show) {
13562             this.show();
13563             return 
13564         }
13565         var _t = this;
13566         this.timeout = setTimeout(function () {
13567             if (_t.hoverState == 'in') {
13568                 _t.show();
13569             }
13570         }, this.delay.show)
13571     },
13572     leave : function() {
13573         clearTimeout(this.timeout);
13574     
13575         this.hoverState = 'out'
13576     
13577         if (!this.delay || !this.delay.hide) {
13578             this.hide();
13579             return 
13580         }
13581         var _t = this;
13582         this.timeout = setTimeout(function () {
13583             if (_t.hoverState == 'out') {
13584                 _t.hide();
13585             }
13586         }, this.delay.hide)
13587     },
13588     
13589     show : function (on_el)
13590     {
13591         if (!on_el) {
13592             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13593         }
13594         // set content.
13595         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
13596         if (this.html !== false) {
13597             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
13598         }
13599         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
13600         if (!this.title.length) {
13601             this.el.select('.popover-title',true).hide();
13602         }
13603         
13604         var placement = typeof this.placement == 'function' ?
13605             this.placement.call(this, this.el, on_el) :
13606             this.placement;
13607             
13608         var autoToken = /\s?auto?\s?/i;
13609         var autoPlace = autoToken.test(placement);
13610         if (autoPlace) {
13611             placement = placement.replace(autoToken, '') || 'top';
13612         }
13613         
13614         //this.el.detach()
13615         //this.el.setXY([0,0]);
13616         this.el.show();
13617         this.el.dom.style.display='block';
13618         this.el.addClass(placement);
13619         
13620         //this.el.appendTo(on_el);
13621         
13622         var p = this.getPosition();
13623         var box = this.el.getBox();
13624         
13625         if (autoPlace) {
13626             // fixme..
13627         }
13628         var align = Roo.bootstrap.Popover.alignment[placement]
13629         this.el.alignTo(on_el, align[0],align[1]);
13630         //var arrow = this.el.select('.arrow',true).first();
13631         //arrow.set(align[2], 
13632         
13633         this.el.addClass('in');
13634         this.hoverState = null;
13635         
13636         if (this.el.hasClass('fade')) {
13637             // fade it?
13638         }
13639         
13640     },
13641     hide : function()
13642     {
13643         this.el.setXY([0,0]);
13644         this.el.removeClass('in');
13645         this.el.hide();
13646         
13647     }
13648     
13649 });
13650
13651 Roo.bootstrap.Popover.alignment = {
13652     'left' : ['r-l', [-10,0], 'right'],
13653     'right' : ['l-r', [10,0], 'left'],
13654     'bottom' : ['t-b', [0,10], 'top'],
13655     'top' : [ 'b-t', [0,-10], 'bottom']
13656 };
13657
13658  /*
13659  * - LGPL
13660  *
13661  * Progress
13662  * 
13663  */
13664
13665 /**
13666  * @class Roo.bootstrap.Progress
13667  * @extends Roo.bootstrap.Component
13668  * Bootstrap Progress class
13669  * @cfg {Boolean} striped striped of the progress bar
13670  * @cfg {Boolean} active animated of the progress bar
13671  * 
13672  * 
13673  * @constructor
13674  * Create a new Progress
13675  * @param {Object} config The config object
13676  */
13677
13678 Roo.bootstrap.Progress = function(config){
13679     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
13680 };
13681
13682 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
13683     
13684     striped : false,
13685     active: false,
13686     
13687     getAutoCreate : function(){
13688         var cfg = {
13689             tag: 'div',
13690             cls: 'progress'
13691         };
13692         
13693         
13694         if(this.striped){
13695             cfg.cls += ' progress-striped';
13696         }
13697       
13698         if(this.active){
13699             cfg.cls += ' active';
13700         }
13701         
13702         
13703         return cfg;
13704     }
13705    
13706 });
13707
13708  
13709
13710  /*
13711  * - LGPL
13712  *
13713  * ProgressBar
13714  * 
13715  */
13716
13717 /**
13718  * @class Roo.bootstrap.ProgressBar
13719  * @extends Roo.bootstrap.Component
13720  * Bootstrap ProgressBar class
13721  * @cfg {Number} aria_valuenow aria-value now
13722  * @cfg {Number} aria_valuemin aria-value min
13723  * @cfg {Number} aria_valuemax aria-value max
13724  * @cfg {String} label label for the progress bar
13725  * @cfg {String} panel (success | info | warning | danger )
13726  * @cfg {String} role role of the progress bar
13727  * @cfg {String} sr_only text
13728  * 
13729  * 
13730  * @constructor
13731  * Create a new ProgressBar
13732  * @param {Object} config The config object
13733  */
13734
13735 Roo.bootstrap.ProgressBar = function(config){
13736     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
13737 };
13738
13739 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
13740     
13741     aria_valuenow : 0,
13742     aria_valuemin : 0,
13743     aria_valuemax : 100,
13744     label : false,
13745     panel : false,
13746     role : false,
13747     sr_only: false,
13748     
13749     getAutoCreate : function()
13750     {
13751         
13752         var cfg = {
13753             tag: 'div',
13754             cls: 'progress-bar',
13755             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
13756         };
13757         
13758         if(this.sr_only){
13759             cfg.cn = {
13760                 tag: 'span',
13761                 cls: 'sr-only',
13762                 html: this.sr_only
13763             }
13764         }
13765         
13766         if(this.role){
13767             cfg.role = this.role;
13768         }
13769         
13770         if(this.aria_valuenow){
13771             cfg['aria-valuenow'] = this.aria_valuenow;
13772         }
13773         
13774         if(this.aria_valuemin){
13775             cfg['aria-valuemin'] = this.aria_valuemin;
13776         }
13777         
13778         if(this.aria_valuemax){
13779             cfg['aria-valuemax'] = this.aria_valuemax;
13780         }
13781         
13782         if(this.label && !this.sr_only){
13783             cfg.html = this.label;
13784         }
13785         
13786         if(this.panel){
13787             cfg.cls += ' progress-bar-' + this.panel;
13788         }
13789         
13790         return cfg;
13791     },
13792     
13793     update : function(aria_valuenow)
13794     {
13795         this.aria_valuenow = aria_valuenow;
13796         
13797         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
13798     }
13799    
13800 });
13801
13802  
13803
13804  /*
13805  * - LGPL
13806  *
13807  * column
13808  * 
13809  */
13810
13811 /**
13812  * @class Roo.bootstrap.TabGroup
13813  * @extends Roo.bootstrap.Column
13814  * Bootstrap Column class
13815  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
13816  * @cfg {Boolean} carousel true to make the group behave like a carousel
13817  * 
13818  * @constructor
13819  * Create a new TabGroup
13820  * @param {Object} config The config object
13821  */
13822
13823 Roo.bootstrap.TabGroup = function(config){
13824     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
13825     if (!this.navId) {
13826         this.navId = Roo.id();
13827     }
13828     this.tabs = [];
13829     Roo.bootstrap.TabGroup.register(this);
13830     
13831 };
13832
13833 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
13834     
13835     carousel : false,
13836     transition : false,
13837      
13838     getAutoCreate : function()
13839     {
13840         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
13841         
13842         cfg.cls += ' tab-content';
13843         
13844         if (this.carousel) {
13845             cfg.cls += ' carousel slide';
13846             cfg.cn = [{
13847                cls : 'carousel-inner'
13848             }]
13849         }
13850         
13851         
13852         return cfg;
13853     },
13854     getChildContainer : function()
13855     {
13856         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
13857     },
13858     
13859     /**
13860     * register a Navigation item
13861     * @param {Roo.bootstrap.NavItem} the navitem to add
13862     */
13863     register : function(item)
13864     {
13865         this.tabs.push( item);
13866         item.navId = this.navId; // not really needed..
13867     
13868     },
13869     
13870     getActivePanel : function()
13871     {
13872         var r = false;
13873         Roo.each(this.tabs, function(t) {
13874             if (t.active) {
13875                 r = t;
13876                 return false;
13877             }
13878             return null;
13879         });
13880         return r;
13881         
13882     },
13883     getPanelByName : function(n)
13884     {
13885         var r = false;
13886         Roo.each(this.tabs, function(t) {
13887             if (t.tabId == n) {
13888                 r = t;
13889                 return false;
13890             }
13891             return null;
13892         });
13893         return r;
13894     },
13895     indexOfPanel : function(p)
13896     {
13897         var r = false;
13898         Roo.each(this.tabs, function(t,i) {
13899             if (t.tabId == p.tabId) {
13900                 r = i;
13901                 return false;
13902             }
13903             return null;
13904         });
13905         return r;
13906     },
13907     /**
13908      * show a specific panel
13909      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
13910      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
13911      */
13912     showPanel : function (pan)
13913     {
13914         
13915         if (typeof(pan) == 'number') {
13916             pan = this.tabs[pan];
13917         }
13918         if (typeof(pan) == 'string') {
13919             pan = this.getPanelByName(pan);
13920         }
13921         if (pan.tabId == this.getActivePanel().tabId) {
13922             return true;
13923         }
13924         var cur = this.getActivePanel();
13925         
13926         if (false === cur.fireEvent('beforedeactivate')) {
13927             return false;
13928         }
13929         
13930         if (this.carousel) {
13931             this.transition = true;
13932             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
13933             var lr = dir == 'next' ? 'left' : 'right';
13934             pan.el.addClass(dir); // or prev
13935             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
13936             cur.el.addClass(lr); // or right
13937             pan.el.addClass(lr);
13938             
13939             var _this = this;
13940             cur.el.on('transitionend', function() {
13941                 Roo.log("trans end?");
13942                 
13943                 pan.el.removeClass([lr,dir]);
13944                 pan.setActive(true);
13945                 
13946                 cur.el.removeClass([lr]);
13947                 cur.setActive(false);
13948                 
13949                 _this.transition = false;
13950                 
13951             }, this, { single:  true } );
13952             return true;
13953         }
13954         
13955         cur.setActive(false);
13956         pan.setActive(true);
13957         return true;
13958         
13959     },
13960     showPanelNext : function()
13961     {
13962         var i = this.indexOfPanel(this.getActivePanel());
13963         if (i > this.tabs.length) {
13964             return;
13965         }
13966         this.showPanel(this.tabs[i+1]);
13967     },
13968     showPanelPrev : function()
13969     {
13970         var i = this.indexOfPanel(this.getActivePanel());
13971         if (i  < 1) {
13972             return;
13973         }
13974         this.showPanel(this.tabs[i-1]);
13975     }
13976     
13977     
13978   
13979 });
13980
13981  
13982
13983  
13984  
13985 Roo.apply(Roo.bootstrap.TabGroup, {
13986     
13987     groups: {},
13988      /**
13989     * register a Navigation Group
13990     * @param {Roo.bootstrap.NavGroup} the navgroup to add
13991     */
13992     register : function(navgrp)
13993     {
13994         this.groups[navgrp.navId] = navgrp;
13995         
13996     },
13997     /**
13998     * fetch a Navigation Group based on the navigation ID
13999     * if one does not exist , it will get created.
14000     * @param {string} the navgroup to add
14001     * @returns {Roo.bootstrap.NavGroup} the navgroup 
14002     */
14003     get: function(navId) {
14004         if (typeof(this.groups[navId]) == 'undefined') {
14005             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
14006         }
14007         return this.groups[navId] ;
14008     }
14009     
14010     
14011     
14012 });
14013
14014  /*
14015  * - LGPL
14016  *
14017  * TabPanel
14018  * 
14019  */
14020
14021 /**
14022  * @class Roo.bootstrap.TabPanel
14023  * @extends Roo.bootstrap.Component
14024  * Bootstrap TabPanel class
14025  * @cfg {Boolean} active panel active
14026  * @cfg {String} html panel content
14027  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
14028  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
14029  * 
14030  * 
14031  * @constructor
14032  * Create a new TabPanel
14033  * @param {Object} config The config object
14034  */
14035
14036 Roo.bootstrap.TabPanel = function(config){
14037     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
14038     this.addEvents({
14039         /**
14040              * @event changed
14041              * Fires when the active status changes
14042              * @param {Roo.bootstrap.TabPanel} this
14043              * @param {Boolean} state the new state
14044             
14045          */
14046         'changed': true,
14047         /**
14048              * @event beforedeactivate
14049              * Fires before a tab is de-activated - can be used to do validation on a form.
14050              * @param {Roo.bootstrap.TabPanel} this
14051              * @return {Boolean} false if there is an error
14052             
14053          */
14054         'beforedeactivate': true
14055      });
14056     
14057     this.tabId = this.tabId || Roo.id();
14058   
14059 };
14060
14061 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
14062     
14063     active: false,
14064     html: false,
14065     tabId: false,
14066     navId : false,
14067     
14068     getAutoCreate : function(){
14069         var cfg = {
14070             tag: 'div',
14071             // item is needed for carousel - not sure if it has any effect otherwise
14072             cls: 'tab-pane item',
14073             html: this.html || ''
14074         };
14075         
14076         if(this.active){
14077             cfg.cls += ' active';
14078         }
14079         
14080         if(this.tabId){
14081             cfg.tabId = this.tabId;
14082         }
14083         
14084         
14085         return cfg;
14086     },
14087     
14088     initEvents:  function()
14089     {
14090         Roo.log('-------- init events on tab panel ---------');
14091         
14092         var p = this.parent();
14093         this.navId = this.navId || p.navId;
14094         
14095         if (typeof(this.navId) != 'undefined') {
14096             // not really needed.. but just in case.. parent should be a NavGroup.
14097             var tg = Roo.bootstrap.TabGroup.get(this.navId);
14098             Roo.log(['register', tg, this]);
14099             tg.register(this);
14100         }
14101     },
14102     
14103     
14104     onRender : function(ct, position)
14105     {
14106        // Roo.log("Call onRender: " + this.xtype);
14107         
14108         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
14109         
14110         
14111         
14112         
14113         
14114     },
14115     
14116     setActive: function(state)
14117     {
14118         Roo.log("panel - set active " + this.tabId + "=" + state);
14119         
14120         this.active = state;
14121         if (!state) {
14122             this.el.removeClass('active');
14123             
14124         } else  if (!this.el.hasClass('active')) {
14125             this.el.addClass('active');
14126         }
14127         this.fireEvent('changed', this, state);
14128     }
14129     
14130     
14131 });
14132  
14133
14134  
14135
14136  /*
14137  * - LGPL
14138  *
14139  * DateField
14140  * 
14141  */
14142
14143 /**
14144  * @class Roo.bootstrap.DateField
14145  * @extends Roo.bootstrap.Input
14146  * Bootstrap DateField class
14147  * @cfg {Number} weekStart default 0
14148  * @cfg {Number} weekStart default 0
14149  * @cfg {Number} viewMode default empty, (months|years)
14150  * @cfg {Number} minViewMode default empty, (months|years)
14151  * @cfg {Number} startDate default -Infinity
14152  * @cfg {Number} endDate default Infinity
14153  * @cfg {Boolean} todayHighlight default false
14154  * @cfg {Boolean} todayBtn default false
14155  * @cfg {Boolean} calendarWeeks default false
14156  * @cfg {Object} daysOfWeekDisabled default empty
14157  * 
14158  * @cfg {Boolean} keyboardNavigation default true
14159  * @cfg {String} language default en
14160  * 
14161  * @constructor
14162  * Create a new DateField
14163  * @param {Object} config The config object
14164  */
14165
14166 Roo.bootstrap.DateField = function(config){
14167     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
14168      this.addEvents({
14169             /**
14170              * @event show
14171              * Fires when this field show.
14172              * @param {Roo.bootstrap.DateField} this
14173              * @param {Mixed} date The date value
14174              */
14175             show : true,
14176             /**
14177              * @event show
14178              * Fires when this field hide.
14179              * @param {Roo.bootstrap.DateField} this
14180              * @param {Mixed} date The date value
14181              */
14182             hide : true,
14183             /**
14184              * @event select
14185              * Fires when select a date.
14186              * @param {Roo.bootstrap.DateField} this
14187              * @param {Mixed} date The date value
14188              */
14189             select : true
14190         });
14191 };
14192
14193 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
14194     
14195     /**
14196      * @cfg {String} format
14197      * The default date format string which can be overriden for localization support.  The format must be
14198      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
14199      */
14200     format : "m/d/y",
14201     /**
14202      * @cfg {String} altFormats
14203      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
14204      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
14205      */
14206     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
14207     
14208     weekStart : 0,
14209     
14210     viewMode : '',
14211     
14212     minViewMode : '',
14213     
14214     todayHighlight : false,
14215     
14216     todayBtn: false,
14217     
14218     language: 'en',
14219     
14220     keyboardNavigation: true,
14221     
14222     calendarWeeks: false,
14223     
14224     startDate: -Infinity,
14225     
14226     endDate: Infinity,
14227     
14228     daysOfWeekDisabled: [],
14229     
14230     _events: [],
14231     
14232     UTCDate: function()
14233     {
14234         return new Date(Date.UTC.apply(Date, arguments));
14235     },
14236     
14237     UTCToday: function()
14238     {
14239         var today = new Date();
14240         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
14241     },
14242     
14243     getDate: function() {
14244             var d = this.getUTCDate();
14245             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
14246     },
14247     
14248     getUTCDate: function() {
14249             return this.date;
14250     },
14251     
14252     setDate: function(d) {
14253             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
14254     },
14255     
14256     setUTCDate: function(d) {
14257             this.date = d;
14258             this.setValue(this.formatDate(this.date));
14259     },
14260         
14261     onRender: function(ct, position)
14262     {
14263         
14264         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
14265         
14266         this.language = this.language || 'en';
14267         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
14268         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
14269         
14270         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
14271         this.format = this.format || 'm/d/y';
14272         this.isInline = false;
14273         this.isInput = true;
14274         this.component = this.el.select('.add-on', true).first() || false;
14275         this.component = (this.component && this.component.length === 0) ? false : this.component;
14276         this.hasInput = this.component && this.inputEL().length;
14277         
14278         if (typeof(this.minViewMode === 'string')) {
14279             switch (this.minViewMode) {
14280                 case 'months':
14281                     this.minViewMode = 1;
14282                     break;
14283                 case 'years':
14284                     this.minViewMode = 2;
14285                     break;
14286                 default:
14287                     this.minViewMode = 0;
14288                     break;
14289             }
14290         }
14291         
14292         if (typeof(this.viewMode === 'string')) {
14293             switch (this.viewMode) {
14294                 case 'months':
14295                     this.viewMode = 1;
14296                     break;
14297                 case 'years':
14298                     this.viewMode = 2;
14299                     break;
14300                 default:
14301                     this.viewMode = 0;
14302                     break;
14303             }
14304         }
14305                 
14306         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
14307         
14308         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14309         
14310         this.picker().on('mousedown', this.onMousedown, this);
14311         this.picker().on('click', this.onClick, this);
14312         
14313         this.picker().addClass('datepicker-dropdown');
14314         
14315         this.startViewMode = this.viewMode;
14316         
14317         
14318         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
14319             if(!this.calendarWeeks){
14320                 v.remove();
14321                 return;
14322             };
14323             
14324             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
14325             v.attr('colspan', function(i, val){
14326                 return parseInt(val) + 1;
14327             });
14328         })
14329                         
14330         
14331         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
14332         
14333         this.setStartDate(this.startDate);
14334         this.setEndDate(this.endDate);
14335         
14336         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
14337         
14338         this.fillDow();
14339         this.fillMonths();
14340         this.update();
14341         this.showMode();
14342         
14343         if(this.isInline) {
14344             this.show();
14345         }
14346     },
14347     
14348     picker : function()
14349     {
14350         return this.el.select('.datepicker', true).first();
14351     },
14352     
14353     fillDow: function()
14354     {
14355         var dowCnt = this.weekStart;
14356         
14357         var dow = {
14358             tag: 'tr',
14359             cn: [
14360                 
14361             ]
14362         };
14363         
14364         if(this.calendarWeeks){
14365             dow.cn.push({
14366                 tag: 'th',
14367                 cls: 'cw',
14368                 html: '&nbsp;'
14369             })
14370         }
14371         
14372         while (dowCnt < this.weekStart + 7) {
14373             dow.cn.push({
14374                 tag: 'th',
14375                 cls: 'dow',
14376                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
14377             });
14378         }
14379         
14380         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
14381     },
14382     
14383     fillMonths: function()
14384     {    
14385         var i = 0
14386         var months = this.picker().select('>.datepicker-months td', true).first();
14387         
14388         months.dom.innerHTML = '';
14389         
14390         while (i < 12) {
14391             var month = {
14392                 tag: 'span',
14393                 cls: 'month',
14394                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
14395             }
14396             
14397             months.createChild(month);
14398         }
14399         
14400     },
14401     
14402     update: function()
14403     {
14404         
14405         this.date = (typeof(this.date) === 'undefined') ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
14406         
14407         if (this.date < this.startDate) {
14408             this.viewDate = new Date(this.startDate);
14409         } else if (this.date > this.endDate) {
14410             this.viewDate = new Date(this.endDate);
14411         } else {
14412             this.viewDate = new Date(this.date);
14413         }
14414         
14415         this.fill();
14416     },
14417     
14418     fill: function() 
14419     {
14420         var d = new Date(this.viewDate),
14421                 year = d.getUTCFullYear(),
14422                 month = d.getUTCMonth(),
14423                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
14424                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
14425                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
14426                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
14427                 currentDate = this.date && this.date.valueOf(),
14428                 today = this.UTCToday();
14429         
14430         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
14431         
14432 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
14433         
14434 //        this.picker.select('>tfoot th.today').
14435 //                                              .text(dates[this.language].today)
14436 //                                              .toggle(this.todayBtn !== false);
14437     
14438         this.updateNavArrows();
14439         this.fillMonths();
14440                                                 
14441         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
14442         
14443         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
14444          
14445         prevMonth.setUTCDate(day);
14446         
14447         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
14448         
14449         var nextMonth = new Date(prevMonth);
14450         
14451         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
14452         
14453         nextMonth = nextMonth.valueOf();
14454         
14455         var fillMonths = false;
14456         
14457         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
14458         
14459         while(prevMonth.valueOf() < nextMonth) {
14460             var clsName = '';
14461             
14462             if (prevMonth.getUTCDay() === this.weekStart) {
14463                 if(fillMonths){
14464                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
14465                 }
14466                     
14467                 fillMonths = {
14468                     tag: 'tr',
14469                     cn: []
14470                 };
14471                 
14472                 if(this.calendarWeeks){
14473                     // ISO 8601: First week contains first thursday.
14474                     // ISO also states week starts on Monday, but we can be more abstract here.
14475                     var
14476                     // Start of current week: based on weekstart/current date
14477                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
14478                     // Thursday of this week
14479                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
14480                     // First Thursday of year, year from thursday
14481                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
14482                     // Calendar week: ms between thursdays, div ms per day, div 7 days
14483                     calWeek =  (th - yth) / 864e5 / 7 + 1;
14484                     
14485                     fillMonths.cn.push({
14486                         tag: 'td',
14487                         cls: 'cw',
14488                         html: calWeek
14489                     });
14490                 }
14491             }
14492             
14493             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
14494                 clsName += ' old';
14495             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
14496                 clsName += ' new';
14497             }
14498             if (this.todayHighlight &&
14499                 prevMonth.getUTCFullYear() == today.getFullYear() &&
14500                 prevMonth.getUTCMonth() == today.getMonth() &&
14501                 prevMonth.getUTCDate() == today.getDate()) {
14502                 clsName += ' today';
14503             }
14504             
14505             if (currentDate && prevMonth.valueOf() === currentDate) {
14506                 clsName += ' active';
14507             }
14508             
14509             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
14510                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
14511                     clsName += ' disabled';
14512             }
14513             
14514             fillMonths.cn.push({
14515                 tag: 'td',
14516                 cls: 'day ' + clsName,
14517                 html: prevMonth.getDate()
14518             })
14519             
14520             prevMonth.setDate(prevMonth.getDate()+1);
14521         }
14522           
14523         var currentYear = this.date && this.date.getUTCFullYear();
14524         var currentMonth = this.date && this.date.getUTCMonth();
14525         
14526         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
14527         
14528         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
14529             v.removeClass('active');
14530             
14531             if(currentYear === year && k === currentMonth){
14532                 v.addClass('active');
14533             }
14534             
14535             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
14536                 v.addClass('disabled');
14537             }
14538             
14539         });
14540         
14541         
14542         year = parseInt(year/10, 10) * 10;
14543         
14544         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
14545         
14546         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
14547         
14548         year -= 1;
14549         for (var i = -1; i < 11; i++) {
14550             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
14551                 tag: 'span',
14552                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
14553                 html: year
14554             })
14555             
14556             year += 1;
14557         }
14558     },
14559     
14560     showMode: function(dir) 
14561     {
14562         if (dir) {
14563             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
14564         }
14565         Roo.each(this.picker().select('>div',true).elements, function(v){
14566             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14567             v.hide();
14568         });
14569         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
14570     },
14571     
14572     place: function()
14573     {
14574         if(this.isInline) return;
14575         
14576         this.picker().removeClass(['bottom', 'top']);
14577         
14578         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
14579             /*
14580              * place to the top of element!
14581              *
14582              */
14583             
14584             this.picker().addClass('top');
14585             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
14586             
14587             return;
14588         }
14589         
14590         this.picker().addClass('bottom');
14591         
14592         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
14593     },
14594     
14595     parseDate : function(value)
14596     {
14597         if(!value || value instanceof Date){
14598             return value;
14599         }
14600         var v = Date.parseDate(value, this.format);
14601         if (!v && this.useIso) {
14602             v = Date.parseDate(value, 'Y-m-d');
14603         }
14604         if(!v && this.altFormats){
14605             if(!this.altFormatsArray){
14606                 this.altFormatsArray = this.altFormats.split("|");
14607             }
14608             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
14609                 v = Date.parseDate(value, this.altFormatsArray[i]);
14610             }
14611         }
14612         return v;
14613     },
14614     
14615     formatDate : function(date, fmt)
14616     {
14617         return (!date || !(date instanceof Date)) ?
14618         date : date.dateFormat(fmt || this.format);
14619     },
14620     
14621     onFocus : function()
14622     {
14623         Roo.bootstrap.DateField.superclass.onFocus.call(this);
14624         this.show();
14625     },
14626     
14627     onBlur : function()
14628     {
14629         Roo.bootstrap.DateField.superclass.onBlur.call(this);
14630         
14631         var d = this.inputEl().getValue();
14632         
14633         if(d && d.length){
14634             this.setValue(d);
14635         }
14636                 
14637         this.hide();
14638     },
14639     
14640     show : function()
14641     {
14642         this.picker().show();
14643         this.update();
14644         this.place();
14645         
14646         this.fireEvent('show', this, this.date);
14647     },
14648     
14649     hide : function()
14650     {
14651         if(this.isInline) return;
14652         this.picker().hide();
14653         this.viewMode = this.startViewMode;
14654         this.showMode();
14655         
14656         this.fireEvent('hide', this, this.date);
14657         
14658     },
14659     
14660     onMousedown: function(e)
14661     {
14662         e.stopPropagation();
14663         e.preventDefault();
14664     },
14665     
14666     keyup: function(e)
14667     {
14668         Roo.bootstrap.DateField.superclass.keyup.call(this);
14669         this.update();
14670     },
14671
14672     setValue: function(v)
14673     {
14674         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
14675         
14676         var d = new Date(v);
14677         
14678         if(isNaN(d.getTime())){
14679             return;
14680         }
14681         
14682         this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
14683
14684         this.update();
14685
14686         this.fireEvent('select', this, this.date);
14687         
14688     },
14689     
14690     getValue: function()
14691     {
14692         return this.formatDate(this.date);
14693     },
14694     
14695     fireKey: function(e)
14696     {
14697         if (!this.picker().isVisible()){
14698             if (e.keyCode == 27) // allow escape to hide and re-show picker
14699                 this.show();
14700             return;
14701         }
14702         
14703         var dateChanged = false,
14704         dir, day, month,
14705         newDate, newViewDate;
14706         
14707         switch(e.keyCode){
14708             case 27: // escape
14709                 this.hide();
14710                 e.preventDefault();
14711                 break;
14712             case 37: // left
14713             case 39: // right
14714                 if (!this.keyboardNavigation) break;
14715                 dir = e.keyCode == 37 ? -1 : 1;
14716                 
14717                 if (e.ctrlKey){
14718                     newDate = this.moveYear(this.date, dir);
14719                     newViewDate = this.moveYear(this.viewDate, dir);
14720                 } else if (e.shiftKey){
14721                     newDate = this.moveMonth(this.date, dir);
14722                     newViewDate = this.moveMonth(this.viewDate, dir);
14723                 } else {
14724                     newDate = new Date(this.date);
14725                     newDate.setUTCDate(this.date.getUTCDate() + dir);
14726                     newViewDate = new Date(this.viewDate);
14727                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
14728                 }
14729                 if (this.dateWithinRange(newDate)){
14730                     this.date = newDate;
14731                     this.viewDate = newViewDate;
14732                     this.setValue(this.formatDate(this.date));
14733 //                    this.update();
14734                     e.preventDefault();
14735                     dateChanged = true;
14736                 }
14737                 break;
14738             case 38: // up
14739             case 40: // down
14740                 if (!this.keyboardNavigation) break;
14741                 dir = e.keyCode == 38 ? -1 : 1;
14742                 if (e.ctrlKey){
14743                     newDate = this.moveYear(this.date, dir);
14744                     newViewDate = this.moveYear(this.viewDate, dir);
14745                 } else if (e.shiftKey){
14746                     newDate = this.moveMonth(this.date, dir);
14747                     newViewDate = this.moveMonth(this.viewDate, dir);
14748                 } else {
14749                     newDate = new Date(this.date);
14750                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
14751                     newViewDate = new Date(this.viewDate);
14752                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
14753                 }
14754                 if (this.dateWithinRange(newDate)){
14755                     this.date = newDate;
14756                     this.viewDate = newViewDate;
14757                     this.setValue(this.formatDate(this.date));
14758 //                    this.update();
14759                     e.preventDefault();
14760                     dateChanged = true;
14761                 }
14762                 break;
14763             case 13: // enter
14764                 this.setValue(this.formatDate(this.date));
14765                 this.hide();
14766                 e.preventDefault();
14767                 break;
14768             case 9: // tab
14769                 this.setValue(this.formatDate(this.date));
14770                 this.hide();
14771                 break;
14772             case 16: // shift
14773             case 17: // ctrl
14774             case 18: // alt
14775                 break;
14776             default :
14777                 this.hide();
14778                 
14779         }
14780     },
14781     
14782     
14783     onClick: function(e) 
14784     {
14785         e.stopPropagation();
14786         e.preventDefault();
14787         
14788         var target = e.getTarget();
14789         
14790         if(target.nodeName.toLowerCase() === 'i'){
14791             target = Roo.get(target).dom.parentNode;
14792         }
14793         
14794         var nodeName = target.nodeName;
14795         var className = target.className;
14796         var html = target.innerHTML;
14797         
14798         switch(nodeName.toLowerCase()) {
14799             case 'th':
14800                 switch(className) {
14801                     case 'switch':
14802                         this.showMode(1);
14803                         break;
14804                     case 'prev':
14805                     case 'next':
14806                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
14807                         switch(this.viewMode){
14808                                 case 0:
14809                                         this.viewDate = this.moveMonth(this.viewDate, dir);
14810                                         break;
14811                                 case 1:
14812                                 case 2:
14813                                         this.viewDate = this.moveYear(this.viewDate, dir);
14814                                         break;
14815                         }
14816                         this.fill();
14817                         break;
14818                     case 'today':
14819                         var date = new Date();
14820                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
14821 //                        this.fill()
14822                         this.setValue(this.formatDate(this.date));
14823                         
14824                         this.hide();
14825                         break;
14826                 }
14827                 break;
14828             case 'span':
14829                 if (className.indexOf('disabled') === -1) {
14830                     this.viewDate.setUTCDate(1);
14831                     if (className.indexOf('month') !== -1) {
14832                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
14833                     } else {
14834                         var year = parseInt(html, 10) || 0;
14835                         this.viewDate.setUTCFullYear(year);
14836                         
14837                     }
14838                     this.showMode(-1);
14839                     this.fill();
14840                 }
14841                 break;
14842                 
14843             case 'td':
14844                 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
14845                     var day = parseInt(html, 10) || 1;
14846                     var year = this.viewDate.getUTCFullYear(),
14847                         month = this.viewDate.getUTCMonth();
14848
14849                     if (className.indexOf('old') !== -1) {
14850                         if(month === 0 ){
14851                             month = 11;
14852                             year -= 1;
14853                         }else{
14854                             month -= 1;
14855                         }
14856                     } else if (className.indexOf('new') !== -1) {
14857                         if (month == 11) {
14858                             month = 0;
14859                             year += 1;
14860                         } else {
14861                             month += 1;
14862                         }
14863                     }
14864                     this.date = this.UTCDate(year, month, day,0,0,0,0);
14865                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
14866 //                    this.fill();
14867                     this.setValue(this.formatDate(this.date));
14868                     this.hide();
14869                 }
14870                 break;
14871         }
14872     },
14873     
14874     setStartDate: function(startDate)
14875     {
14876         this.startDate = startDate || -Infinity;
14877         if (this.startDate !== -Infinity) {
14878             this.startDate = this.parseDate(this.startDate);
14879         }
14880         this.update();
14881         this.updateNavArrows();
14882     },
14883
14884     setEndDate: function(endDate)
14885     {
14886         this.endDate = endDate || Infinity;
14887         if (this.endDate !== Infinity) {
14888             this.endDate = this.parseDate(this.endDate);
14889         }
14890         this.update();
14891         this.updateNavArrows();
14892     },
14893     
14894     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
14895     {
14896         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
14897         if (typeof(this.daysOfWeekDisabled) !== 'object') {
14898             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
14899         }
14900         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
14901             return parseInt(d, 10);
14902         });
14903         this.update();
14904         this.updateNavArrows();
14905     },
14906     
14907     updateNavArrows: function() 
14908     {
14909         var d = new Date(this.viewDate),
14910         year = d.getUTCFullYear(),
14911         month = d.getUTCMonth();
14912         
14913         Roo.each(this.picker().select('.prev', true).elements, function(v){
14914             v.show();
14915             switch (this.viewMode) {
14916                 case 0:
14917
14918                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
14919                         v.hide();
14920                     }
14921                     break;
14922                 case 1:
14923                 case 2:
14924                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
14925                         v.hide();
14926                     }
14927                     break;
14928             }
14929         });
14930         
14931         Roo.each(this.picker().select('.next', true).elements, function(v){
14932             v.show();
14933             switch (this.viewMode) {
14934                 case 0:
14935
14936                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
14937                         v.hide();
14938                     }
14939                     break;
14940                 case 1:
14941                 case 2:
14942                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
14943                         v.hide();
14944                     }
14945                     break;
14946             }
14947         })
14948     },
14949     
14950     moveMonth: function(date, dir)
14951     {
14952         if (!dir) return date;
14953         var new_date = new Date(date.valueOf()),
14954         day = new_date.getUTCDate(),
14955         month = new_date.getUTCMonth(),
14956         mag = Math.abs(dir),
14957         new_month, test;
14958         dir = dir > 0 ? 1 : -1;
14959         if (mag == 1){
14960             test = dir == -1
14961             // If going back one month, make sure month is not current month
14962             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
14963             ? function(){
14964                 return new_date.getUTCMonth() == month;
14965             }
14966             // If going forward one month, make sure month is as expected
14967             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
14968             : function(){
14969                 return new_date.getUTCMonth() != new_month;
14970             };
14971             new_month = month + dir;
14972             new_date.setUTCMonth(new_month);
14973             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
14974             if (new_month < 0 || new_month > 11)
14975                 new_month = (new_month + 12) % 12;
14976         } else {
14977             // For magnitudes >1, move one month at a time...
14978             for (var i=0; i<mag; i++)
14979                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
14980                 new_date = this.moveMonth(new_date, dir);
14981             // ...then reset the day, keeping it in the new month
14982             new_month = new_date.getUTCMonth();
14983             new_date.setUTCDate(day);
14984             test = function(){
14985                 return new_month != new_date.getUTCMonth();
14986             };
14987         }
14988         // Common date-resetting loop -- if date is beyond end of month, make it
14989         // end of month
14990         while (test()){
14991             new_date.setUTCDate(--day);
14992             new_date.setUTCMonth(new_month);
14993         }
14994         return new_date;
14995     },
14996
14997     moveYear: function(date, dir)
14998     {
14999         return this.moveMonth(date, dir*12);
15000     },
15001
15002     dateWithinRange: function(date)
15003     {
15004         return date >= this.startDate && date <= this.endDate;
15005     },
15006
15007     
15008     remove: function() 
15009     {
15010         this.picker().remove();
15011     }
15012    
15013 });
15014
15015 Roo.apply(Roo.bootstrap.DateField,  {
15016     
15017     head : {
15018         tag: 'thead',
15019         cn: [
15020         {
15021             tag: 'tr',
15022             cn: [
15023             {
15024                 tag: 'th',
15025                 cls: 'prev',
15026                 html: '<i class="fa fa-arrow-left"/>'
15027             },
15028             {
15029                 tag: 'th',
15030                 cls: 'switch',
15031                 colspan: '5'
15032             },
15033             {
15034                 tag: 'th',
15035                 cls: 'next',
15036                 html: '<i class="fa fa-arrow-right"/>'
15037             }
15038
15039             ]
15040         }
15041         ]
15042     },
15043     
15044     content : {
15045         tag: 'tbody',
15046         cn: [
15047         {
15048             tag: 'tr',
15049             cn: [
15050             {
15051                 tag: 'td',
15052                 colspan: '7'
15053             }
15054             ]
15055         }
15056         ]
15057     },
15058     
15059     footer : {
15060         tag: 'tfoot',
15061         cn: [
15062         {
15063             tag: 'tr',
15064             cn: [
15065             {
15066                 tag: 'th',
15067                 colspan: '7',
15068                 cls: 'today'
15069             }
15070                     
15071             ]
15072         }
15073         ]
15074     },
15075     
15076     dates:{
15077         en: {
15078             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
15079             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
15080             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
15081             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
15082             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
15083             today: "Today"
15084         }
15085     },
15086     
15087     modes: [
15088     {
15089         clsName: 'days',
15090         navFnc: 'Month',
15091         navStep: 1
15092     },
15093     {
15094         clsName: 'months',
15095         navFnc: 'FullYear',
15096         navStep: 1
15097     },
15098     {
15099         clsName: 'years',
15100         navFnc: 'FullYear',
15101         navStep: 10
15102     }]
15103 });
15104
15105 Roo.apply(Roo.bootstrap.DateField,  {
15106   
15107     template : {
15108         tag: 'div',
15109         cls: 'datepicker dropdown-menu',
15110         cn: [
15111         {
15112             tag: 'div',
15113             cls: 'datepicker-days',
15114             cn: [
15115             {
15116                 tag: 'table',
15117                 cls: 'table-condensed',
15118                 cn:[
15119                 Roo.bootstrap.DateField.head,
15120                 {
15121                     tag: 'tbody'
15122                 },
15123                 Roo.bootstrap.DateField.footer
15124                 ]
15125             }
15126             ]
15127         },
15128         {
15129             tag: 'div',
15130             cls: 'datepicker-months',
15131             cn: [
15132             {
15133                 tag: 'table',
15134                 cls: 'table-condensed',
15135                 cn:[
15136                 Roo.bootstrap.DateField.head,
15137                 Roo.bootstrap.DateField.content,
15138                 Roo.bootstrap.DateField.footer
15139                 ]
15140             }
15141             ]
15142         },
15143         {
15144             tag: 'div',
15145             cls: 'datepicker-years',
15146             cn: [
15147             {
15148                 tag: 'table',
15149                 cls: 'table-condensed',
15150                 cn:[
15151                 Roo.bootstrap.DateField.head,
15152                 Roo.bootstrap.DateField.content,
15153                 Roo.bootstrap.DateField.footer
15154                 ]
15155             }
15156             ]
15157         }
15158         ]
15159     }
15160 });
15161
15162  
15163
15164  /*
15165  * - LGPL
15166  *
15167  * TimeField
15168  * 
15169  */
15170
15171 /**
15172  * @class Roo.bootstrap.TimeField
15173  * @extends Roo.bootstrap.Input
15174  * Bootstrap DateField class
15175  * 
15176  * 
15177  * @constructor
15178  * Create a new TimeField
15179  * @param {Object} config The config object
15180  */
15181
15182 Roo.bootstrap.TimeField = function(config){
15183     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
15184     this.addEvents({
15185             /**
15186              * @event show
15187              * Fires when this field show.
15188              * @param {Roo.bootstrap.DateField} this
15189              * @param {Mixed} date The date value
15190              */
15191             show : true,
15192             /**
15193              * @event show
15194              * Fires when this field hide.
15195              * @param {Roo.bootstrap.DateField} this
15196              * @param {Mixed} date The date value
15197              */
15198             hide : true,
15199             /**
15200              * @event select
15201              * Fires when select a date.
15202              * @param {Roo.bootstrap.DateField} this
15203              * @param {Mixed} date The date value
15204              */
15205             select : true
15206         });
15207 };
15208
15209 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
15210     
15211     /**
15212      * @cfg {String} format
15213      * The default time format string which can be overriden for localization support.  The format must be
15214      * valid according to {@link Date#parseDate} (defaults to 'H:i').
15215      */
15216     format : "H:i",
15217        
15218     onRender: function(ct, position)
15219     {
15220         
15221         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
15222                 
15223         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
15224         
15225         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15226         
15227         this.pop = this.picker().select('>.datepicker-time',true).first();
15228         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block' 
15229         
15230         this.picker().on('mousedown', this.onMousedown, this);
15231         this.picker().on('click', this.onClick, this);
15232         
15233         this.picker().addClass('datepicker-dropdown');
15234     
15235         this.fillTime();
15236         this.update();
15237             
15238         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
15239         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
15240         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
15241         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
15242         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
15243         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
15244
15245     },
15246     
15247     fireKey: function(e){
15248         if (!this.picker().isVisible()){
15249             if (e.keyCode == 27) // allow escape to hide and re-show picker
15250                 this.show();
15251             return;
15252         }
15253
15254         e.preventDefault();
15255         
15256         switch(e.keyCode){
15257             case 27: // escape
15258                 this.hide();
15259                 break;
15260             case 37: // left
15261             case 39: // right
15262                 this.onTogglePeriod();
15263                 break;
15264             case 38: // up
15265                 this.onIncrementMinutes();
15266                 break;
15267             case 40: // down
15268                 this.onDecrementMinutes();
15269                 break;
15270             case 13: // enter
15271             case 9: // tab
15272                 this.setTime();
15273                 break;
15274         }
15275     },
15276     
15277     onClick: function(e) {
15278         e.stopPropagation();
15279         e.preventDefault();
15280     },
15281     
15282     picker : function()
15283     {
15284         return this.el.select('.datepicker', true).first();
15285     },
15286     
15287     fillTime: function()
15288     {    
15289         var time = this.pop.select('tbody', true).first();
15290         
15291         time.dom.innerHTML = '';
15292         
15293         time.createChild({
15294             tag: 'tr',
15295             cn: [
15296                 {
15297                     tag: 'td',
15298                     cn: [
15299                         {
15300                             tag: 'a',
15301                             href: '#',
15302                             cls: 'btn',
15303                             cn: [
15304                                 {
15305                                     tag: 'span',
15306                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
15307                                 }
15308                             ]
15309                         } 
15310                     ]
15311                 },
15312                 {
15313                     tag: 'td',
15314                     cls: 'separator'
15315                 },
15316                 {
15317                     tag: 'td',
15318                     cn: [
15319                         {
15320                             tag: 'a',
15321                             href: '#',
15322                             cls: 'btn',
15323                             cn: [
15324                                 {
15325                                     tag: 'span',
15326                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
15327                                 }
15328                             ]
15329                         }
15330                     ]
15331                 },
15332                 {
15333                     tag: 'td',
15334                     cls: 'separator'
15335                 }
15336             ]
15337         });
15338         
15339         time.createChild({
15340             tag: 'tr',
15341             cn: [
15342                 {
15343                     tag: 'td',
15344                     cn: [
15345                         {
15346                             tag: 'span',
15347                             cls: 'timepicker-hour',
15348                             html: '00'
15349                         }  
15350                     ]
15351                 },
15352                 {
15353                     tag: 'td',
15354                     cls: 'separator',
15355                     html: ':'
15356                 },
15357                 {
15358                     tag: 'td',
15359                     cn: [
15360                         {
15361                             tag: 'span',
15362                             cls: 'timepicker-minute',
15363                             html: '00'
15364                         }  
15365                     ]
15366                 },
15367                 {
15368                     tag: 'td',
15369                     cls: 'separator'
15370                 },
15371                 {
15372                     tag: 'td',
15373                     cn: [
15374                         {
15375                             tag: 'button',
15376                             type: 'button',
15377                             cls: 'btn btn-primary period',
15378                             html: 'AM'
15379                             
15380                         }
15381                     ]
15382                 }
15383             ]
15384         });
15385         
15386         time.createChild({
15387             tag: 'tr',
15388             cn: [
15389                 {
15390                     tag: 'td',
15391                     cn: [
15392                         {
15393                             tag: 'a',
15394                             href: '#',
15395                             cls: 'btn',
15396                             cn: [
15397                                 {
15398                                     tag: 'span',
15399                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
15400                                 }
15401                             ]
15402                         }
15403                     ]
15404                 },
15405                 {
15406                     tag: 'td',
15407                     cls: 'separator'
15408                 },
15409                 {
15410                     tag: 'td',
15411                     cn: [
15412                         {
15413                             tag: 'a',
15414                             href: '#',
15415                             cls: 'btn',
15416                             cn: [
15417                                 {
15418                                     tag: 'span',
15419                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
15420                                 }
15421                             ]
15422                         }
15423                     ]
15424                 },
15425                 {
15426                     tag: 'td',
15427                     cls: 'separator'
15428                 }
15429             ]
15430         });
15431         
15432     },
15433     
15434     update: function()
15435     {
15436         
15437         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
15438         
15439         this.fill();
15440     },
15441     
15442     fill: function() 
15443     {
15444         var hours = this.time.getHours();
15445         var minutes = this.time.getMinutes();
15446         var period = 'AM';
15447         
15448         if(hours > 11){
15449             period = 'PM';
15450         }
15451         
15452         if(hours == 0){
15453             hours = 12;
15454         }
15455         
15456         
15457         if(hours > 12){
15458             hours = hours - 12;
15459         }
15460         
15461         if(hours < 10){
15462             hours = '0' + hours;
15463         }
15464         
15465         if(minutes < 10){
15466             minutes = '0' + minutes;
15467         }
15468         
15469         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
15470         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
15471         this.pop.select('button', true).first().dom.innerHTML = period;
15472         
15473     },
15474     
15475     place: function()
15476     {   
15477         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
15478         
15479         var cls = ['bottom'];
15480         
15481         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
15482             cls.pop();
15483             cls.push('top');
15484         }
15485         
15486         cls.push('right');
15487         
15488         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
15489             cls.pop();
15490             cls.push('left');
15491         }
15492         
15493         this.picker().addClass(cls.join('-'));
15494         
15495         var _this = this;
15496         
15497         Roo.each(cls, function(c){
15498             if(c == 'bottom'){
15499                 _this.picker().setTop(_this.inputEl().getHeight());
15500                 return;
15501             }
15502             if(c == 'top'){
15503                 _this.picker().setTop(0 - _this.picker().getHeight());
15504                 return;
15505             }
15506             
15507             if(c == 'left'){
15508                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
15509                 return;
15510             }
15511             if(c == 'right'){
15512                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
15513                 return;
15514             }
15515         });
15516         
15517     },
15518   
15519     onFocus : function()
15520     {
15521         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
15522         this.show();
15523     },
15524     
15525     onBlur : function()
15526     {
15527         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
15528         this.hide();
15529     },
15530     
15531     show : function()
15532     {
15533         this.picker().show();
15534         this.pop.show();
15535         this.update();
15536         this.place();
15537         
15538         this.fireEvent('show', this, this.date);
15539     },
15540     
15541     hide : function()
15542     {
15543         this.picker().hide();
15544         this.pop.hide();
15545         
15546         this.fireEvent('hide', this, this.date);
15547     },
15548     
15549     setTime : function()
15550     {
15551         this.hide();
15552         this.setValue(this.time.format(this.format));
15553         
15554         this.fireEvent('select', this, this.date);
15555         
15556         
15557     },
15558     
15559     onMousedown: function(e){
15560         e.stopPropagation();
15561         e.preventDefault();
15562     },
15563     
15564     onIncrementHours: function()
15565     {
15566         Roo.log('onIncrementHours');
15567         this.time = this.time.add(Date.HOUR, 1);
15568         this.update();
15569         
15570     },
15571     
15572     onDecrementHours: function()
15573     {
15574         Roo.log('onDecrementHours');
15575         this.time = this.time.add(Date.HOUR, -1);
15576         this.update();
15577     },
15578     
15579     onIncrementMinutes: function()
15580     {
15581         Roo.log('onIncrementMinutes');
15582         this.time = this.time.add(Date.MINUTE, 1);
15583         this.update();
15584     },
15585     
15586     onDecrementMinutes: function()
15587     {
15588         Roo.log('onDecrementMinutes');
15589         this.time = this.time.add(Date.MINUTE, -1);
15590         this.update();
15591     },
15592     
15593     onTogglePeriod: function()
15594     {
15595         Roo.log('onTogglePeriod');
15596         this.time = this.time.add(Date.HOUR, 12);
15597         this.update();
15598     }
15599     
15600    
15601 });
15602
15603 Roo.apply(Roo.bootstrap.TimeField,  {
15604     
15605     content : {
15606         tag: 'tbody',
15607         cn: [
15608             {
15609                 tag: 'tr',
15610                 cn: [
15611                 {
15612                     tag: 'td',
15613                     colspan: '7'
15614                 }
15615                 ]
15616             }
15617         ]
15618     },
15619     
15620     footer : {
15621         tag: 'tfoot',
15622         cn: [
15623             {
15624                 tag: 'tr',
15625                 cn: [
15626                 {
15627                     tag: 'th',
15628                     colspan: '7',
15629                     cls: '',
15630                     cn: [
15631                         {
15632                             tag: 'button',
15633                             cls: 'btn btn-info ok',
15634                             html: 'OK'
15635                         }
15636                     ]
15637                 }
15638
15639                 ]
15640             }
15641         ]
15642     }
15643 });
15644
15645 Roo.apply(Roo.bootstrap.TimeField,  {
15646   
15647     template : {
15648         tag: 'div',
15649         cls: 'datepicker dropdown-menu',
15650         cn: [
15651             {
15652                 tag: 'div',
15653                 cls: 'datepicker-time',
15654                 cn: [
15655                 {
15656                     tag: 'table',
15657                     cls: 'table-condensed',
15658                     cn:[
15659                     Roo.bootstrap.TimeField.content,
15660                     Roo.bootstrap.TimeField.footer
15661                     ]
15662                 }
15663                 ]
15664             }
15665         ]
15666     }
15667 });
15668
15669  
15670
15671  /*
15672  * - LGPL
15673  *
15674  * CheckBox
15675  * 
15676  */
15677
15678 /**
15679  * @class Roo.bootstrap.CheckBox
15680  * @extends Roo.bootstrap.Input
15681  * Bootstrap CheckBox class
15682  * 
15683  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
15684  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
15685  * @cfg {String} boxLabel The text that appears beside the checkbox
15686  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
15687  * @cfg {Boolean} checked initnal the element
15688  * 
15689  * 
15690  * @constructor
15691  * Create a new CheckBox
15692  * @param {Object} config The config object
15693  */
15694
15695 Roo.bootstrap.CheckBox = function(config){
15696     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
15697    
15698         this.addEvents({
15699             /**
15700             * @event check
15701             * Fires when the element is checked or unchecked.
15702             * @param {Roo.bootstrap.CheckBox} this This input
15703             * @param {Boolean} checked The new checked value
15704             */
15705            check : true
15706         });
15707 };
15708
15709 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
15710     
15711     inputType: 'checkbox',
15712     inputValue: 1,
15713     valueOff: 0,
15714     boxLabel: false,
15715     checked: false,
15716     weight : false,
15717     
15718     getAutoCreate : function()
15719     {
15720         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
15721         
15722         var id = Roo.id();
15723         
15724         var cfg = {};
15725         
15726         cfg.cls = 'form-group checkbox' //input-group
15727         
15728         
15729         
15730         
15731         var input =  {
15732             tag: 'input',
15733             id : id,
15734             type : this.inputType,
15735             value : (!this.checked) ? this.valueOff : this.inputValue,
15736             cls : 'roo-checkbox', //'form-box',
15737             placeholder : this.placeholder || ''
15738             
15739         };
15740         
15741         if (this.weight) { // Validity check?
15742             cfg.cls += " checkbox-" + this.weight;
15743         }
15744         
15745         if (this.disabled) {
15746             input.disabled=true;
15747         }
15748         
15749         if(this.checked){
15750             input.checked = this.checked;
15751         }
15752         
15753         if (this.name) {
15754             input.name = this.name;
15755         }
15756         
15757         if (this.size) {
15758             input.cls += ' input-' + this.size;
15759         }
15760         
15761         var settings=this;
15762         ['xs','sm','md','lg'].map(function(size){
15763             if (settings[size]) {
15764                 cfg.cls += ' col-' + size + '-' + settings[size];
15765             }
15766         });
15767         
15768        
15769         
15770         var inputblock = input;
15771         
15772         
15773         
15774         
15775         if (this.before || this.after) {
15776             
15777             inputblock = {
15778                 cls : 'input-group',
15779                 cn :  [] 
15780             };
15781             if (this.before) {
15782                 inputblock.cn.push({
15783                     tag :'span',
15784                     cls : 'input-group-addon',
15785                     html : this.before
15786                 });
15787             }
15788             inputblock.cn.push(input);
15789             if (this.after) {
15790                 inputblock.cn.push({
15791                     tag :'span',
15792                     cls : 'input-group-addon',
15793                     html : this.after
15794                 });
15795             }
15796             
15797         };
15798         
15799         if (align ==='left' && this.fieldLabel.length) {
15800                 Roo.log("left and has label");
15801                 cfg.cn = [
15802                     
15803                     {
15804                         tag: 'label',
15805                         'for' :  id,
15806                         cls : 'control-label col-md-' + this.labelWidth,
15807                         html : this.fieldLabel
15808                         
15809                     },
15810                     {
15811                         cls : "col-md-" + (12 - this.labelWidth), 
15812                         cn: [
15813                             inputblock
15814                         ]
15815                     }
15816                     
15817                 ];
15818         } else if ( this.fieldLabel.length) {
15819                 Roo.log(" label");
15820                 cfg.cn = [
15821                    
15822                     {
15823                         tag: this.boxLabel ? 'span' : 'label',
15824                         'for': id,
15825                         cls: 'control-label box-input-label',
15826                         //cls : 'input-group-addon',
15827                         html : this.fieldLabel
15828                         
15829                     },
15830                     
15831                     inputblock
15832                     
15833                 ];
15834
15835         } else {
15836             
15837                 Roo.log(" no label && no align");
15838                 cfg.cn = [  inputblock ] ;
15839                 
15840                 
15841         };
15842          if(this.boxLabel){
15843             cfg.cn.push( {
15844                 tag: 'label',
15845                 'for': id,
15846                 cls: 'box-label',
15847                 html: this.boxLabel
15848                 
15849             });
15850         }
15851         
15852         
15853        
15854         return cfg;
15855         
15856     },
15857     
15858     /**
15859      * return the real input element.
15860      */
15861     inputEl: function ()
15862     {
15863         return this.el.select('input.roo-checkbox',true).first();
15864     },
15865     
15866     label: function()
15867     {
15868         return this.el.select('label.control-label',true).first();
15869     },
15870     
15871     initEvents : function()
15872     {
15873 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
15874         
15875         this.inputEl().on('click', this.onClick,  this);
15876         
15877     },
15878     
15879     onClick : function()
15880     {   
15881         this.setChecked(!this.checked);
15882     },
15883     
15884     setChecked : function(state,suppressEvent)
15885     {
15886         this.checked = state;
15887         
15888         this.inputEl().dom.checked = state;
15889         
15890         if(suppressEvent !== true){
15891             this.fireEvent('check', this, state);
15892         }
15893         
15894         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
15895         
15896     },
15897     
15898     setValue : function(v,suppressEvent)
15899     {
15900         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
15901     }
15902     
15903 });
15904
15905  
15906 /*
15907  * - LGPL
15908  *
15909  * Radio
15910  * 
15911  */
15912
15913 /**
15914  * @class Roo.bootstrap.Radio
15915  * @extends Roo.bootstrap.CheckBox
15916  * Bootstrap Radio class
15917
15918  * @constructor
15919  * Create a new Radio
15920  * @param {Object} config The config object
15921  */
15922
15923 Roo.bootstrap.Radio = function(config){
15924     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
15925    
15926 };
15927
15928 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
15929     
15930     inputType: 'radio',
15931     inputValue: '',
15932     valueOff: '',
15933     
15934     getAutoCreate : function()
15935     {
15936         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
15937         
15938         var id = Roo.id();
15939         
15940         var cfg = {};
15941         
15942         cfg.cls = 'form-group radio' //input-group
15943         
15944         var input =  {
15945             tag: 'input',
15946             id : id,
15947             type : this.inputType,
15948             value : (!this.checked) ? this.valueOff : this.inputValue,
15949             cls : 'roo-radio',
15950             placeholder : this.placeholder || ''
15951             
15952         };
15953           if (this.weight) { // Validity check?
15954             cfg.cls += " radio-" + this.weight;
15955         }
15956         if (this.disabled) {
15957             input.disabled=true;
15958         }
15959         
15960         if(this.checked){
15961             input.checked = this.checked;
15962         }
15963         
15964         if (this.name) {
15965             input.name = this.name;
15966         }
15967         
15968         if (this.size) {
15969             input.cls += ' input-' + this.size;
15970         }
15971         
15972         var settings=this;
15973         ['xs','sm','md','lg'].map(function(size){
15974             if (settings[size]) {
15975                 cfg.cls += ' col-' + size + '-' + settings[size];
15976             }
15977         });
15978         
15979         var inputblock = input;
15980         
15981         if (this.before || this.after) {
15982             
15983             inputblock = {
15984                 cls : 'input-group',
15985                 cn :  [] 
15986             };
15987             if (this.before) {
15988                 inputblock.cn.push({
15989                     tag :'span',
15990                     cls : 'input-group-addon',
15991                     html : this.before
15992                 });
15993             }
15994             inputblock.cn.push(input);
15995             if (this.after) {
15996                 inputblock.cn.push({
15997                     tag :'span',
15998                     cls : 'input-group-addon',
15999                     html : this.after
16000                 });
16001             }
16002             
16003         };
16004         
16005         if (align ==='left' && this.fieldLabel.length) {
16006                 Roo.log("left and has label");
16007                 cfg.cn = [
16008                     
16009                     {
16010                         tag: 'label',
16011                         'for' :  id,
16012                         cls : 'control-label col-md-' + this.labelWidth,
16013                         html : this.fieldLabel
16014                         
16015                     },
16016                     {
16017                         cls : "col-md-" + (12 - this.labelWidth), 
16018                         cn: [
16019                             inputblock
16020                         ]
16021                     }
16022                     
16023                 ];
16024         } else if ( this.fieldLabel.length) {
16025                 Roo.log(" label");
16026                  cfg.cn = [
16027                    
16028                     {
16029                         tag: 'label',
16030                         'for': id,
16031                         cls: 'control-label box-input-label',
16032                         //cls : 'input-group-addon',
16033                         html : this.fieldLabel
16034                         
16035                     },
16036                     
16037                     inputblock
16038                     
16039                 ];
16040
16041         } else {
16042             
16043                    Roo.log(" no label && no align");
16044                 cfg.cn = [
16045                     
16046                         inputblock
16047                     
16048                 ];
16049                 
16050                 
16051         };
16052         
16053         if(this.boxLabel){
16054             cfg.cn.push({
16055                 tag: 'label',
16056                 'for': id,
16057                 cls: 'box-label',
16058                 html: this.boxLabel
16059             })
16060         }
16061         
16062         return cfg;
16063         
16064     },
16065     inputEl: function ()
16066     {
16067         return this.el.select('input.roo-radio',true).first();
16068     },
16069     onClick : function()
16070     {   
16071         this.setChecked(true);
16072     },
16073     
16074     setChecked : function(state,suppressEvent)
16075     {
16076         if(state){
16077             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
16078                 v.dom.checked = false;
16079             });
16080         }
16081         
16082         this.checked = state;
16083         this.inputEl().dom.checked = state;
16084         
16085         if(suppressEvent !== true){
16086             this.fireEvent('check', this, state);
16087         }
16088         
16089         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
16090         
16091     },
16092     
16093     getGroupValue : function()
16094     {
16095         var value = ''
16096         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
16097             if(v.dom.checked == true){
16098                 value = v.dom.value;
16099             }
16100         });
16101         
16102         return value;
16103     },
16104     
16105     /**
16106      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
16107      * @return {Mixed} value The field value
16108      */
16109     getValue : function(){
16110         return this.getGroupValue();
16111     }
16112     
16113 });
16114
16115  
16116 //<script type="text/javascript">
16117
16118 /*
16119  * Based  Ext JS Library 1.1.1
16120  * Copyright(c) 2006-2007, Ext JS, LLC.
16121  * LGPL
16122  *
16123  */
16124  
16125 /**
16126  * @class Roo.HtmlEditorCore
16127  * @extends Roo.Component
16128  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
16129  *
16130  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
16131  */
16132
16133 Roo.HtmlEditorCore = function(config){
16134     
16135     
16136     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
16137     this.addEvents({
16138         /**
16139          * @event initialize
16140          * Fires when the editor is fully initialized (including the iframe)
16141          * @param {Roo.HtmlEditorCore} this
16142          */
16143         initialize: true,
16144         /**
16145          * @event activate
16146          * Fires when the editor is first receives the focus. Any insertion must wait
16147          * until after this event.
16148          * @param {Roo.HtmlEditorCore} this
16149          */
16150         activate: true,
16151          /**
16152          * @event beforesync
16153          * Fires before the textarea is updated with content from the editor iframe. Return false
16154          * to cancel the sync.
16155          * @param {Roo.HtmlEditorCore} this
16156          * @param {String} html
16157          */
16158         beforesync: true,
16159          /**
16160          * @event beforepush
16161          * Fires before the iframe editor is updated with content from the textarea. Return false
16162          * to cancel the push.
16163          * @param {Roo.HtmlEditorCore} this
16164          * @param {String} html
16165          */
16166         beforepush: true,
16167          /**
16168          * @event sync
16169          * Fires when the textarea is updated with content from the editor iframe.
16170          * @param {Roo.HtmlEditorCore} this
16171          * @param {String} html
16172          */
16173         sync: true,
16174          /**
16175          * @event push
16176          * Fires when the iframe editor is updated with content from the textarea.
16177          * @param {Roo.HtmlEditorCore} this
16178          * @param {String} html
16179          */
16180         push: true,
16181         
16182         /**
16183          * @event editorevent
16184          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
16185          * @param {Roo.HtmlEditorCore} this
16186          */
16187         editorevent: true
16188     });
16189      
16190 };
16191
16192
16193 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
16194
16195
16196      /**
16197      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
16198      */
16199     
16200     owner : false,
16201     
16202      /**
16203      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
16204      *                        Roo.resizable.
16205      */
16206     resizable : false,
16207      /**
16208      * @cfg {Number} height (in pixels)
16209      */   
16210     height: 300,
16211    /**
16212      * @cfg {Number} width (in pixels)
16213      */   
16214     width: 500,
16215     
16216     /**
16217      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
16218      * 
16219      */
16220     stylesheets: false,
16221     
16222     // id of frame..
16223     frameId: false,
16224     
16225     // private properties
16226     validationEvent : false,
16227     deferHeight: true,
16228     initialized : false,
16229     activated : false,
16230     sourceEditMode : false,
16231     onFocus : Roo.emptyFn,
16232     iframePad:3,
16233     hideMode:'offsets',
16234     
16235     clearUp: true,
16236     
16237      
16238     
16239
16240     /**
16241      * Protected method that will not generally be called directly. It
16242      * is called when the editor initializes the iframe with HTML contents. Override this method if you
16243      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
16244      */
16245     getDocMarkup : function(){
16246         // body styles..
16247         var st = '';
16248         Roo.log(this.stylesheets);
16249         
16250         // inherit styels from page...?? 
16251         if (this.stylesheets === false) {
16252             
16253             Roo.get(document.head).select('style').each(function(node) {
16254                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16255             });
16256             
16257             Roo.get(document.head).select('link').each(function(node) { 
16258                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16259             });
16260             
16261         } else if (!this.stylesheets.length) {
16262                 // simple..
16263                 st = '<style type="text/css">' +
16264                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16265                    '</style>';
16266         } else {
16267             Roo.each(this.stylesheets, function(s) {
16268                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
16269             });
16270             
16271         }
16272         
16273         st +=  '<style type="text/css">' +
16274             'IMG { cursor: pointer } ' +
16275         '</style>';
16276
16277         
16278         return '<html><head>' + st  +
16279             //<style type="text/css">' +
16280             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16281             //'</style>' +
16282             ' </head><body class="roo-htmleditor-body"></body></html>';
16283     },
16284
16285     // private
16286     onRender : function(ct, position)
16287     {
16288         var _t = this;
16289         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
16290         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
16291         
16292         
16293         this.el.dom.style.border = '0 none';
16294         this.el.dom.setAttribute('tabIndex', -1);
16295         this.el.addClass('x-hidden hide');
16296         
16297         
16298         
16299         if(Roo.isIE){ // fix IE 1px bogus margin
16300             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
16301         }
16302        
16303         
16304         this.frameId = Roo.id();
16305         
16306          
16307         
16308         var iframe = this.owner.wrap.createChild({
16309             tag: 'iframe',
16310             cls: 'form-control', // bootstrap..
16311             id: this.frameId,
16312             name: this.frameId,
16313             frameBorder : 'no',
16314             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
16315         }, this.el
16316         );
16317         
16318         
16319         this.iframe = iframe.dom;
16320
16321          this.assignDocWin();
16322         
16323         this.doc.designMode = 'on';
16324        
16325         this.doc.open();
16326         this.doc.write(this.getDocMarkup());
16327         this.doc.close();
16328
16329         
16330         var task = { // must defer to wait for browser to be ready
16331             run : function(){
16332                 //console.log("run task?" + this.doc.readyState);
16333                 this.assignDocWin();
16334                 if(this.doc.body || this.doc.readyState == 'complete'){
16335                     try {
16336                         this.doc.designMode="on";
16337                     } catch (e) {
16338                         return;
16339                     }
16340                     Roo.TaskMgr.stop(task);
16341                     this.initEditor.defer(10, this);
16342                 }
16343             },
16344             interval : 10,
16345             duration: 10000,
16346             scope: this
16347         };
16348         Roo.TaskMgr.start(task);
16349
16350         
16351          
16352     },
16353
16354     // private
16355     onResize : function(w, h)
16356     {
16357          Roo.log('resize: ' +w + ',' + h );
16358         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
16359         if(!this.iframe){
16360             return;
16361         }
16362         if(typeof w == 'number'){
16363             
16364             this.iframe.style.width = w + 'px';
16365         }
16366         if(typeof h == 'number'){
16367             
16368             this.iframe.style.height = h + 'px';
16369             if(this.doc){
16370                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
16371             }
16372         }
16373         
16374     },
16375
16376     /**
16377      * Toggles the editor between standard and source edit mode.
16378      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
16379      */
16380     toggleSourceEdit : function(sourceEditMode){
16381         
16382         this.sourceEditMode = sourceEditMode === true;
16383         
16384         if(this.sourceEditMode){
16385  
16386             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
16387             
16388         }else{
16389             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
16390             //this.iframe.className = '';
16391             this.deferFocus();
16392         }
16393         //this.setSize(this.owner.wrap.getSize());
16394         //this.fireEvent('editmodechange', this, this.sourceEditMode);
16395     },
16396
16397     
16398   
16399
16400     /**
16401      * Protected method that will not generally be called directly. If you need/want
16402      * custom HTML cleanup, this is the method you should override.
16403      * @param {String} html The HTML to be cleaned
16404      * return {String} The cleaned HTML
16405      */
16406     cleanHtml : function(html){
16407         html = String(html);
16408         if(html.length > 5){
16409             if(Roo.isSafari){ // strip safari nonsense
16410                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
16411             }
16412         }
16413         if(html == '&nbsp;'){
16414             html = '';
16415         }
16416         return html;
16417     },
16418
16419     /**
16420      * HTML Editor -> Textarea
16421      * Protected method that will not generally be called directly. Syncs the contents
16422      * of the editor iframe with the textarea.
16423      */
16424     syncValue : function(){
16425         if(this.initialized){
16426             var bd = (this.doc.body || this.doc.documentElement);
16427             //this.cleanUpPaste(); -- this is done else where and causes havoc..
16428             var html = bd.innerHTML;
16429             if(Roo.isSafari){
16430                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
16431                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
16432                 if(m && m[1]){
16433                     html = '<div style="'+m[0]+'">' + html + '</div>';
16434                 }
16435             }
16436             html = this.cleanHtml(html);
16437             // fix up the special chars.. normaly like back quotes in word...
16438             // however we do not want to do this with chinese..
16439             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
16440                 var cc = b.charCodeAt();
16441                 if (
16442                     (cc >= 0x4E00 && cc < 0xA000 ) ||
16443                     (cc >= 0x3400 && cc < 0x4E00 ) ||
16444                     (cc >= 0xf900 && cc < 0xfb00 )
16445                 ) {
16446                         return b;
16447                 }
16448                 return "&#"+cc+";" 
16449             });
16450             if(this.owner.fireEvent('beforesync', this, html) !== false){
16451                 this.el.dom.value = html;
16452                 this.owner.fireEvent('sync', this, html);
16453             }
16454         }
16455     },
16456
16457     /**
16458      * Protected method that will not generally be called directly. Pushes the value of the textarea
16459      * into the iframe editor.
16460      */
16461     pushValue : function(){
16462         if(this.initialized){
16463             var v = this.el.dom.value.trim();
16464             
16465 //            if(v.length < 1){
16466 //                v = '&#160;';
16467 //            }
16468             
16469             if(this.owner.fireEvent('beforepush', this, v) !== false){
16470                 var d = (this.doc.body || this.doc.documentElement);
16471                 d.innerHTML = v;
16472                 this.cleanUpPaste();
16473                 this.el.dom.value = d.innerHTML;
16474                 this.owner.fireEvent('push', this, v);
16475             }
16476         }
16477     },
16478
16479     // private
16480     deferFocus : function(){
16481         this.focus.defer(10, this);
16482     },
16483
16484     // doc'ed in Field
16485     focus : function(){
16486         if(this.win && !this.sourceEditMode){
16487             this.win.focus();
16488         }else{
16489             this.el.focus();
16490         }
16491     },
16492     
16493     assignDocWin: function()
16494     {
16495         var iframe = this.iframe;
16496         
16497          if(Roo.isIE){
16498             this.doc = iframe.contentWindow.document;
16499             this.win = iframe.contentWindow;
16500         } else {
16501             if (!Roo.get(this.frameId)) {
16502                 return;
16503             }
16504             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
16505             this.win = Roo.get(this.frameId).dom.contentWindow;
16506         }
16507     },
16508     
16509     // private
16510     initEditor : function(){
16511         //console.log("INIT EDITOR");
16512         this.assignDocWin();
16513         
16514         
16515         
16516         this.doc.designMode="on";
16517         this.doc.open();
16518         this.doc.write(this.getDocMarkup());
16519         this.doc.close();
16520         
16521         var dbody = (this.doc.body || this.doc.documentElement);
16522         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
16523         // this copies styles from the containing element into thsi one..
16524         // not sure why we need all of this..
16525         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
16526         
16527         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
16528         //ss['background-attachment'] = 'fixed'; // w3c
16529         dbody.bgProperties = 'fixed'; // ie
16530         //Roo.DomHelper.applyStyles(dbody, ss);
16531         Roo.EventManager.on(this.doc, {
16532             //'mousedown': this.onEditorEvent,
16533             'mouseup': this.onEditorEvent,
16534             'dblclick': this.onEditorEvent,
16535             'click': this.onEditorEvent,
16536             'keyup': this.onEditorEvent,
16537             buffer:100,
16538             scope: this
16539         });
16540         if(Roo.isGecko){
16541             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
16542         }
16543         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
16544             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
16545         }
16546         this.initialized = true;
16547
16548         this.owner.fireEvent('initialize', this);
16549         this.pushValue();
16550     },
16551
16552     // private
16553     onDestroy : function(){
16554         
16555         
16556         
16557         if(this.rendered){
16558             
16559             //for (var i =0; i < this.toolbars.length;i++) {
16560             //    // fixme - ask toolbars for heights?
16561             //    this.toolbars[i].onDestroy();
16562            // }
16563             
16564             //this.wrap.dom.innerHTML = '';
16565             //this.wrap.remove();
16566         }
16567     },
16568
16569     // private
16570     onFirstFocus : function(){
16571         
16572         this.assignDocWin();
16573         
16574         
16575         this.activated = true;
16576          
16577     
16578         if(Roo.isGecko){ // prevent silly gecko errors
16579             this.win.focus();
16580             var s = this.win.getSelection();
16581             if(!s.focusNode || s.focusNode.nodeType != 3){
16582                 var r = s.getRangeAt(0);
16583                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
16584                 r.collapse(true);
16585                 this.deferFocus();
16586             }
16587             try{
16588                 this.execCmd('useCSS', true);
16589                 this.execCmd('styleWithCSS', false);
16590             }catch(e){}
16591         }
16592         this.owner.fireEvent('activate', this);
16593     },
16594
16595     // private
16596     adjustFont: function(btn){
16597         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
16598         //if(Roo.isSafari){ // safari
16599         //    adjust *= 2;
16600        // }
16601         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
16602         if(Roo.isSafari){ // safari
16603             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
16604             v =  (v < 10) ? 10 : v;
16605             v =  (v > 48) ? 48 : v;
16606             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
16607             
16608         }
16609         
16610         
16611         v = Math.max(1, v+adjust);
16612         
16613         this.execCmd('FontSize', v  );
16614     },
16615
16616     onEditorEvent : function(e){
16617         this.owner.fireEvent('editorevent', this, e);
16618       //  this.updateToolbar();
16619         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
16620     },
16621
16622     insertTag : function(tg)
16623     {
16624         // could be a bit smarter... -> wrap the current selected tRoo..
16625         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
16626             
16627             range = this.createRange(this.getSelection());
16628             var wrappingNode = this.doc.createElement(tg.toLowerCase());
16629             wrappingNode.appendChild(range.extractContents());
16630             range.insertNode(wrappingNode);
16631
16632             return;
16633             
16634             
16635             
16636         }
16637         this.execCmd("formatblock",   tg);
16638         
16639     },
16640     
16641     insertText : function(txt)
16642     {
16643         
16644         
16645         var range = this.createRange();
16646         range.deleteContents();
16647                //alert(Sender.getAttribute('label'));
16648                
16649         range.insertNode(this.doc.createTextNode(txt));
16650     } ,
16651     
16652      
16653
16654     /**
16655      * Executes a Midas editor command on the editor document and performs necessary focus and
16656      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
16657      * @param {String} cmd The Midas command
16658      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16659      */
16660     relayCmd : function(cmd, value){
16661         this.win.focus();
16662         this.execCmd(cmd, value);
16663         this.owner.fireEvent('editorevent', this);
16664         //this.updateToolbar();
16665         this.owner.deferFocus();
16666     },
16667
16668     /**
16669      * Executes a Midas editor command directly on the editor document.
16670      * For visual commands, you should use {@link #relayCmd} instead.
16671      * <b>This should only be called after the editor is initialized.</b>
16672      * @param {String} cmd The Midas command
16673      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16674      */
16675     execCmd : function(cmd, value){
16676         this.doc.execCommand(cmd, false, value === undefined ? null : value);
16677         this.syncValue();
16678     },
16679  
16680  
16681    
16682     /**
16683      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
16684      * to insert tRoo.
16685      * @param {String} text | dom node.. 
16686      */
16687     insertAtCursor : function(text)
16688     {
16689         
16690         
16691         
16692         if(!this.activated){
16693             return;
16694         }
16695         /*
16696         if(Roo.isIE){
16697             this.win.focus();
16698             var r = this.doc.selection.createRange();
16699             if(r){
16700                 r.collapse(true);
16701                 r.pasteHTML(text);
16702                 this.syncValue();
16703                 this.deferFocus();
16704             
16705             }
16706             return;
16707         }
16708         */
16709         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
16710             this.win.focus();
16711             
16712             
16713             // from jquery ui (MIT licenced)
16714             var range, node;
16715             var win = this.win;
16716             
16717             if (win.getSelection && win.getSelection().getRangeAt) {
16718                 range = win.getSelection().getRangeAt(0);
16719                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
16720                 range.insertNode(node);
16721             } else if (win.document.selection && win.document.selection.createRange) {
16722                 // no firefox support
16723                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
16724                 win.document.selection.createRange().pasteHTML(txt);
16725             } else {
16726                 // no firefox support
16727                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
16728                 this.execCmd('InsertHTML', txt);
16729             } 
16730             
16731             this.syncValue();
16732             
16733             this.deferFocus();
16734         }
16735     },
16736  // private
16737     mozKeyPress : function(e){
16738         if(e.ctrlKey){
16739             var c = e.getCharCode(), cmd;
16740           
16741             if(c > 0){
16742                 c = String.fromCharCode(c).toLowerCase();
16743                 switch(c){
16744                     case 'b':
16745                         cmd = 'bold';
16746                         break;
16747                     case 'i':
16748                         cmd = 'italic';
16749                         break;
16750                     
16751                     case 'u':
16752                         cmd = 'underline';
16753                         break;
16754                     
16755                     case 'v':
16756                         this.cleanUpPaste.defer(100, this);
16757                         return;
16758                         
16759                 }
16760                 if(cmd){
16761                     this.win.focus();
16762                     this.execCmd(cmd);
16763                     this.deferFocus();
16764                     e.preventDefault();
16765                 }
16766                 
16767             }
16768         }
16769     },
16770
16771     // private
16772     fixKeys : function(){ // load time branching for fastest keydown performance
16773         if(Roo.isIE){
16774             return function(e){
16775                 var k = e.getKey(), r;
16776                 if(k == e.TAB){
16777                     e.stopEvent();
16778                     r = this.doc.selection.createRange();
16779                     if(r){
16780                         r.collapse(true);
16781                         r.pasteHTML('&#160;&#160;&#160;&#160;');
16782                         this.deferFocus();
16783                     }
16784                     return;
16785                 }
16786                 
16787                 if(k == e.ENTER){
16788                     r = this.doc.selection.createRange();
16789                     if(r){
16790                         var target = r.parentElement();
16791                         if(!target || target.tagName.toLowerCase() != 'li'){
16792                             e.stopEvent();
16793                             r.pasteHTML('<br />');
16794                             r.collapse(false);
16795                             r.select();
16796                         }
16797                     }
16798                 }
16799                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
16800                     this.cleanUpPaste.defer(100, this);
16801                     return;
16802                 }
16803                 
16804                 
16805             };
16806         }else if(Roo.isOpera){
16807             return function(e){
16808                 var k = e.getKey();
16809                 if(k == e.TAB){
16810                     e.stopEvent();
16811                     this.win.focus();
16812                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
16813                     this.deferFocus();
16814                 }
16815                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
16816                     this.cleanUpPaste.defer(100, this);
16817                     return;
16818                 }
16819                 
16820             };
16821         }else if(Roo.isSafari){
16822             return function(e){
16823                 var k = e.getKey();
16824                 
16825                 if(k == e.TAB){
16826                     e.stopEvent();
16827                     this.execCmd('InsertText','\t');
16828                     this.deferFocus();
16829                     return;
16830                 }
16831                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
16832                     this.cleanUpPaste.defer(100, this);
16833                     return;
16834                 }
16835                 
16836              };
16837         }
16838     }(),
16839     
16840     getAllAncestors: function()
16841     {
16842         var p = this.getSelectedNode();
16843         var a = [];
16844         if (!p) {
16845             a.push(p); // push blank onto stack..
16846             p = this.getParentElement();
16847         }
16848         
16849         
16850         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
16851             a.push(p);
16852             p = p.parentNode;
16853         }
16854         a.push(this.doc.body);
16855         return a;
16856     },
16857     lastSel : false,
16858     lastSelNode : false,
16859     
16860     
16861     getSelection : function() 
16862     {
16863         this.assignDocWin();
16864         return Roo.isIE ? this.doc.selection : this.win.getSelection();
16865     },
16866     
16867     getSelectedNode: function() 
16868     {
16869         // this may only work on Gecko!!!
16870         
16871         // should we cache this!!!!
16872         
16873         
16874         
16875          
16876         var range = this.createRange(this.getSelection()).cloneRange();
16877         
16878         if (Roo.isIE) {
16879             var parent = range.parentElement();
16880             while (true) {
16881                 var testRange = range.duplicate();
16882                 testRange.moveToElementText(parent);
16883                 if (testRange.inRange(range)) {
16884                     break;
16885                 }
16886                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
16887                     break;
16888                 }
16889                 parent = parent.parentElement;
16890             }
16891             return parent;
16892         }
16893         
16894         // is ancestor a text element.
16895         var ac =  range.commonAncestorContainer;
16896         if (ac.nodeType == 3) {
16897             ac = ac.parentNode;
16898         }
16899         
16900         var ar = ac.childNodes;
16901          
16902         var nodes = [];
16903         var other_nodes = [];
16904         var has_other_nodes = false;
16905         for (var i=0;i<ar.length;i++) {
16906             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
16907                 continue;
16908             }
16909             // fullly contained node.
16910             
16911             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
16912                 nodes.push(ar[i]);
16913                 continue;
16914             }
16915             
16916             // probably selected..
16917             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
16918                 other_nodes.push(ar[i]);
16919                 continue;
16920             }
16921             // outer..
16922             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
16923                 continue;
16924             }
16925             
16926             
16927             has_other_nodes = true;
16928         }
16929         if (!nodes.length && other_nodes.length) {
16930             nodes= other_nodes;
16931         }
16932         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
16933             return false;
16934         }
16935         
16936         return nodes[0];
16937     },
16938     createRange: function(sel)
16939     {
16940         // this has strange effects when using with 
16941         // top toolbar - not sure if it's a great idea.
16942         //this.editor.contentWindow.focus();
16943         if (typeof sel != "undefined") {
16944             try {
16945                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
16946             } catch(e) {
16947                 return this.doc.createRange();
16948             }
16949         } else {
16950             return this.doc.createRange();
16951         }
16952     },
16953     getParentElement: function()
16954     {
16955         
16956         this.assignDocWin();
16957         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
16958         
16959         var range = this.createRange(sel);
16960          
16961         try {
16962             var p = range.commonAncestorContainer;
16963             while (p.nodeType == 3) { // text node
16964                 p = p.parentNode;
16965             }
16966             return p;
16967         } catch (e) {
16968             return null;
16969         }
16970     
16971     },
16972     /***
16973      *
16974      * Range intersection.. the hard stuff...
16975      *  '-1' = before
16976      *  '0' = hits..
16977      *  '1' = after.
16978      *         [ -- selected range --- ]
16979      *   [fail]                        [fail]
16980      *
16981      *    basically..
16982      *      if end is before start or  hits it. fail.
16983      *      if start is after end or hits it fail.
16984      *
16985      *   if either hits (but other is outside. - then it's not 
16986      *   
16987      *    
16988      **/
16989     
16990     
16991     // @see http://www.thismuchiknow.co.uk/?p=64.
16992     rangeIntersectsNode : function(range, node)
16993     {
16994         var nodeRange = node.ownerDocument.createRange();
16995         try {
16996             nodeRange.selectNode(node);
16997         } catch (e) {
16998             nodeRange.selectNodeContents(node);
16999         }
17000     
17001         var rangeStartRange = range.cloneRange();
17002         rangeStartRange.collapse(true);
17003     
17004         var rangeEndRange = range.cloneRange();
17005         rangeEndRange.collapse(false);
17006     
17007         var nodeStartRange = nodeRange.cloneRange();
17008         nodeStartRange.collapse(true);
17009     
17010         var nodeEndRange = nodeRange.cloneRange();
17011         nodeEndRange.collapse(false);
17012     
17013         return rangeStartRange.compareBoundaryPoints(
17014                  Range.START_TO_START, nodeEndRange) == -1 &&
17015                rangeEndRange.compareBoundaryPoints(
17016                  Range.START_TO_START, nodeStartRange) == 1;
17017         
17018          
17019     },
17020     rangeCompareNode : function(range, node)
17021     {
17022         var nodeRange = node.ownerDocument.createRange();
17023         try {
17024             nodeRange.selectNode(node);
17025         } catch (e) {
17026             nodeRange.selectNodeContents(node);
17027         }
17028         
17029         
17030         range.collapse(true);
17031     
17032         nodeRange.collapse(true);
17033      
17034         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
17035         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
17036          
17037         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
17038         
17039         var nodeIsBefore   =  ss == 1;
17040         var nodeIsAfter    = ee == -1;
17041         
17042         if (nodeIsBefore && nodeIsAfter)
17043             return 0; // outer
17044         if (!nodeIsBefore && nodeIsAfter)
17045             return 1; //right trailed.
17046         
17047         if (nodeIsBefore && !nodeIsAfter)
17048             return 2;  // left trailed.
17049         // fully contined.
17050         return 3;
17051     },
17052
17053     // private? - in a new class?
17054     cleanUpPaste :  function()
17055     {
17056         // cleans up the whole document..
17057         Roo.log('cleanuppaste');
17058         
17059         this.cleanUpChildren(this.doc.body);
17060         var clean = this.cleanWordChars(this.doc.body.innerHTML);
17061         if (clean != this.doc.body.innerHTML) {
17062             this.doc.body.innerHTML = clean;
17063         }
17064         
17065     },
17066     
17067     cleanWordChars : function(input) {// change the chars to hex code
17068         var he = Roo.HtmlEditorCore;
17069         
17070         var output = input;
17071         Roo.each(he.swapCodes, function(sw) { 
17072             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
17073             
17074             output = output.replace(swapper, sw[1]);
17075         });
17076         
17077         return output;
17078     },
17079     
17080     
17081     cleanUpChildren : function (n)
17082     {
17083         if (!n.childNodes.length) {
17084             return;
17085         }
17086         for (var i = n.childNodes.length-1; i > -1 ; i--) {
17087            this.cleanUpChild(n.childNodes[i]);
17088         }
17089     },
17090     
17091     
17092         
17093     
17094     cleanUpChild : function (node)
17095     {
17096         var ed = this;
17097         //console.log(node);
17098         if (node.nodeName == "#text") {
17099             // clean up silly Windows -- stuff?
17100             return; 
17101         }
17102         if (node.nodeName == "#comment") {
17103             node.parentNode.removeChild(node);
17104             // clean up silly Windows -- stuff?
17105             return; 
17106         }
17107         
17108         if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1 && this.clearUp) {
17109             // remove node.
17110             node.parentNode.removeChild(node);
17111             return;
17112             
17113         }
17114         
17115         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
17116         
17117         // remove <a name=....> as rendering on yahoo mailer is borked with this.
17118         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
17119         
17120         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
17121         //    remove_keep_children = true;
17122         //}
17123         
17124         if (remove_keep_children) {
17125             this.cleanUpChildren(node);
17126             // inserts everything just before this node...
17127             while (node.childNodes.length) {
17128                 var cn = node.childNodes[0];
17129                 node.removeChild(cn);
17130                 node.parentNode.insertBefore(cn, node);
17131             }
17132             node.parentNode.removeChild(node);
17133             return;
17134         }
17135         
17136         if (!node.attributes || !node.attributes.length) {
17137             this.cleanUpChildren(node);
17138             return;
17139         }
17140         
17141         function cleanAttr(n,v)
17142         {
17143             
17144             if (v.match(/^\./) || v.match(/^\//)) {
17145                 return;
17146             }
17147             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
17148                 return;
17149             }
17150             if (v.match(/^#/)) {
17151                 return;
17152             }
17153 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
17154             node.removeAttribute(n);
17155             
17156         }
17157         
17158         function cleanStyle(n,v)
17159         {
17160             if (v.match(/expression/)) { //XSS?? should we even bother..
17161                 node.removeAttribute(n);
17162                 return;
17163             }
17164             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
17165             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
17166             
17167             
17168             var parts = v.split(/;/);
17169             var clean = [];
17170             
17171             Roo.each(parts, function(p) {
17172                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
17173                 if (!p.length) {
17174                     return true;
17175                 }
17176                 var l = p.split(':').shift().replace(/\s+/g,'');
17177                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
17178                 
17179                 if ( cblack.indexOf(l) > -1) {
17180 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17181                     //node.removeAttribute(n);
17182                     return true;
17183                 }
17184                 //Roo.log()
17185                 // only allow 'c whitelisted system attributes'
17186                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
17187 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17188                     //node.removeAttribute(n);
17189                     return true;
17190                 }
17191                 
17192                 
17193                  
17194                 
17195                 clean.push(p);
17196                 return true;
17197             });
17198             if (clean.length) { 
17199                 node.setAttribute(n, clean.join(';'));
17200             } else {
17201                 node.removeAttribute(n);
17202             }
17203             
17204         }
17205         
17206         
17207         for (var i = node.attributes.length-1; i > -1 ; i--) {
17208             var a = node.attributes[i];
17209             //console.log(a);
17210             
17211             if (a.name.toLowerCase().substr(0,2)=='on')  {
17212                 node.removeAttribute(a.name);
17213                 continue;
17214             }
17215             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
17216                 node.removeAttribute(a.name);
17217                 continue;
17218             }
17219             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
17220                 cleanAttr(a.name,a.value); // fixme..
17221                 continue;
17222             }
17223             if (a.name == 'style') {
17224                 cleanStyle(a.name,a.value);
17225                 continue;
17226             }
17227             /// clean up MS crap..
17228             // tecnically this should be a list of valid class'es..
17229             
17230             
17231             if (a.name == 'class') {
17232                 if (a.value.match(/^Mso/)) {
17233                     node.className = '';
17234                 }
17235                 
17236                 if (a.value.match(/body/)) {
17237                     node.className = '';
17238                 }
17239                 continue;
17240             }
17241             
17242             // style cleanup!?
17243             // class cleanup?
17244             
17245         }
17246         
17247         
17248         this.cleanUpChildren(node);
17249         
17250         
17251     },
17252     /**
17253      * Clean up MS wordisms...
17254      */
17255     cleanWord : function(node)
17256     {
17257         var _t = this;
17258         var cleanWordChildren = function()
17259         {
17260             if (!node.childNodes.length) {
17261                 return;
17262             }
17263             for (var i = node.childNodes.length-1; i > -1 ; i--) {
17264                _t.cleanWord(node.childNodes[i]);
17265             }
17266         }
17267         
17268         
17269         if (!node) {
17270             this.cleanWord(this.doc.body);
17271             return;
17272         }
17273         if (node.nodeName == "#text") {
17274             // clean up silly Windows -- stuff?
17275             return; 
17276         }
17277         if (node.nodeName == "#comment") {
17278             node.parentNode.removeChild(node);
17279             // clean up silly Windows -- stuff?
17280             return; 
17281         }
17282         
17283         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
17284             node.parentNode.removeChild(node);
17285             return;
17286         }
17287         
17288         // remove - but keep children..
17289         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
17290             while (node.childNodes.length) {
17291                 var cn = node.childNodes[0];
17292                 node.removeChild(cn);
17293                 node.parentNode.insertBefore(cn, node);
17294             }
17295             node.parentNode.removeChild(node);
17296             cleanWordChildren();
17297             return;
17298         }
17299         // clean styles
17300         if (node.className.length) {
17301             
17302             var cn = node.className.split(/\W+/);
17303             var cna = [];
17304             Roo.each(cn, function(cls) {
17305                 if (cls.match(/Mso[a-zA-Z]+/)) {
17306                     return;
17307                 }
17308                 cna.push(cls);
17309             });
17310             node.className = cna.length ? cna.join(' ') : '';
17311             if (!cna.length) {
17312                 node.removeAttribute("class");
17313             }
17314         }
17315         
17316         if (node.hasAttribute("lang")) {
17317             node.removeAttribute("lang");
17318         }
17319         
17320         if (node.hasAttribute("style")) {
17321             
17322             var styles = node.getAttribute("style").split(";");
17323             var nstyle = [];
17324             Roo.each(styles, function(s) {
17325                 if (!s.match(/:/)) {
17326                     return;
17327                 }
17328                 var kv = s.split(":");
17329                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
17330                     return;
17331                 }
17332                 // what ever is left... we allow.
17333                 nstyle.push(s);
17334             });
17335             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
17336             if (!nstyle.length) {
17337                 node.removeAttribute('style');
17338             }
17339         }
17340         
17341         cleanWordChildren();
17342         
17343         
17344     },
17345     domToHTML : function(currentElement, depth, nopadtext) {
17346         
17347             depth = depth || 0;
17348             nopadtext = nopadtext || false;
17349         
17350             if (!currentElement) {
17351                 return this.domToHTML(this.doc.body);
17352             }
17353             
17354             //Roo.log(currentElement);
17355             var j;
17356             var allText = false;
17357             var nodeName = currentElement.nodeName;
17358             var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
17359             
17360             if  (nodeName == '#text') {
17361                 return currentElement.nodeValue;
17362             }
17363             
17364             
17365             var ret = '';
17366             if (nodeName != 'BODY') {
17367                  
17368                 var i = 0;
17369                 // Prints the node tagName, such as <A>, <IMG>, etc
17370                 if (tagName) {
17371                     var attr = [];
17372                     for(i = 0; i < currentElement.attributes.length;i++) {
17373                         // quoting?
17374                         var aname = currentElement.attributes.item(i).name;
17375                         if (!currentElement.attributes.item(i).value.length) {
17376                             continue;
17377                         }
17378                         attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
17379                     }
17380                     
17381                     ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
17382                 } 
17383                 else {
17384                     
17385                     // eack
17386                 }
17387             } else {
17388                 tagName = false;
17389             }
17390             if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
17391                 return ret;
17392             }
17393             if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
17394                 nopadtext = true;
17395             }
17396             
17397             
17398             // Traverse the tree
17399             i = 0;
17400             var currentElementChild = currentElement.childNodes.item(i);
17401             var allText = true;
17402             var innerHTML  = '';
17403             lastnode = '';
17404             while (currentElementChild) {
17405                 // Formatting code (indent the tree so it looks nice on the screen)
17406                 var nopad = nopadtext;
17407                 if (lastnode == 'SPAN') {
17408                     nopad  = true;
17409                 }
17410                 // text
17411                 if  (currentElementChild.nodeName == '#text') {
17412                     var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
17413                     if (!nopad && toadd.length > 80) {
17414                         innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
17415                     }
17416                     innerHTML  += toadd;
17417                     
17418                     i++;
17419                     currentElementChild = currentElement.childNodes.item(i);
17420                     lastNode = '';
17421                     continue;
17422                 }
17423                 allText = false;
17424                 
17425                 innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
17426                     
17427                 // Recursively traverse the tree structure of the child node
17428                 innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
17429                 lastnode = currentElementChild.nodeName;
17430                 i++;
17431                 currentElementChild=currentElement.childNodes.item(i);
17432             }
17433             
17434             ret += innerHTML;
17435             
17436             if (!allText) {
17437                     // The remaining code is mostly for formatting the tree
17438                 ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
17439             }
17440             
17441             
17442             if (tagName) {
17443                 ret+= "</"+tagName+">";
17444             }
17445             return ret;
17446             
17447         }
17448     
17449     // hide stuff that is not compatible
17450     /**
17451      * @event blur
17452      * @hide
17453      */
17454     /**
17455      * @event change
17456      * @hide
17457      */
17458     /**
17459      * @event focus
17460      * @hide
17461      */
17462     /**
17463      * @event specialkey
17464      * @hide
17465      */
17466     /**
17467      * @cfg {String} fieldClass @hide
17468      */
17469     /**
17470      * @cfg {String} focusClass @hide
17471      */
17472     /**
17473      * @cfg {String} autoCreate @hide
17474      */
17475     /**
17476      * @cfg {String} inputType @hide
17477      */
17478     /**
17479      * @cfg {String} invalidClass @hide
17480      */
17481     /**
17482      * @cfg {String} invalidText @hide
17483      */
17484     /**
17485      * @cfg {String} msgFx @hide
17486      */
17487     /**
17488      * @cfg {String} validateOnBlur @hide
17489      */
17490 });
17491
17492 Roo.HtmlEditorCore.white = [
17493         'area', 'br', 'img', 'input', 'hr', 'wbr',
17494         
17495        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
17496        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
17497        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
17498        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
17499        'table',   'ul',         'xmp', 
17500        
17501        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
17502       'thead',   'tr', 
17503      
17504       'dir', 'menu', 'ol', 'ul', 'dl',
17505        
17506       'embed',  'object'
17507 ];
17508
17509
17510 Roo.HtmlEditorCore.black = [
17511     //    'embed',  'object', // enable - backend responsiblity to clean thiese
17512         'applet', // 
17513         'base',   'basefont', 'bgsound', 'blink',  'body', 
17514         'frame',  'frameset', 'head',    'html',   'ilayer', 
17515         'iframe', 'layer',  'link',     'meta',    'object',   
17516         'script', 'style' ,'title',  'xml' // clean later..
17517 ];
17518 Roo.HtmlEditorCore.clean = [
17519     'script', 'style', 'title', 'xml'
17520 ];
17521 Roo.HtmlEditorCore.remove = [
17522     'font'
17523 ];
17524 // attributes..
17525
17526 Roo.HtmlEditorCore.ablack = [
17527     'on'
17528 ];
17529     
17530 Roo.HtmlEditorCore.aclean = [ 
17531     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
17532 ];
17533
17534 // protocols..
17535 Roo.HtmlEditorCore.pwhite= [
17536         'http',  'https',  'mailto'
17537 ];
17538
17539 // white listed style attributes.
17540 Roo.HtmlEditorCore.cwhite= [
17541       //  'text-align', /// default is to allow most things..
17542       
17543          
17544 //        'font-size'//??
17545 ];
17546
17547 // black listed style attributes.
17548 Roo.HtmlEditorCore.cblack= [
17549       //  'font-size' -- this can be set by the project 
17550 ];
17551
17552
17553 Roo.HtmlEditorCore.swapCodes   =[ 
17554     [    8211, "--" ], 
17555     [    8212, "--" ], 
17556     [    8216,  "'" ],  
17557     [    8217, "'" ],  
17558     [    8220, '"' ],  
17559     [    8221, '"' ],  
17560     [    8226, "*" ],  
17561     [    8230, "..." ]
17562 ]; 
17563
17564     /*
17565  * - LGPL
17566  *
17567  * HtmlEditor
17568  * 
17569  */
17570
17571 /**
17572  * @class Roo.bootstrap.HtmlEditor
17573  * @extends Roo.bootstrap.TextArea
17574  * Bootstrap HtmlEditor class
17575
17576  * @constructor
17577  * Create a new HtmlEditor
17578  * @param {Object} config The config object
17579  */
17580
17581 Roo.bootstrap.HtmlEditor = function(config){
17582     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
17583     if (!this.toolbars) {
17584         this.toolbars = [];
17585     }
17586     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
17587     this.addEvents({
17588             /**
17589              * @event initialize
17590              * Fires when the editor is fully initialized (including the iframe)
17591              * @param {HtmlEditor} this
17592              */
17593             initialize: true,
17594             /**
17595              * @event activate
17596              * Fires when the editor is first receives the focus. Any insertion must wait
17597              * until after this event.
17598              * @param {HtmlEditor} this
17599              */
17600             activate: true,
17601              /**
17602              * @event beforesync
17603              * Fires before the textarea is updated with content from the editor iframe. Return false
17604              * to cancel the sync.
17605              * @param {HtmlEditor} this
17606              * @param {String} html
17607              */
17608             beforesync: true,
17609              /**
17610              * @event beforepush
17611              * Fires before the iframe editor is updated with content from the textarea. Return false
17612              * to cancel the push.
17613              * @param {HtmlEditor} this
17614              * @param {String} html
17615              */
17616             beforepush: true,
17617              /**
17618              * @event sync
17619              * Fires when the textarea is updated with content from the editor iframe.
17620              * @param {HtmlEditor} this
17621              * @param {String} html
17622              */
17623             sync: true,
17624              /**
17625              * @event push
17626              * Fires when the iframe editor is updated with content from the textarea.
17627              * @param {HtmlEditor} this
17628              * @param {String} html
17629              */
17630             push: true,
17631              /**
17632              * @event editmodechange
17633              * Fires when the editor switches edit modes
17634              * @param {HtmlEditor} this
17635              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
17636              */
17637             editmodechange: true,
17638             /**
17639              * @event editorevent
17640              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
17641              * @param {HtmlEditor} this
17642              */
17643             editorevent: true,
17644             /**
17645              * @event firstfocus
17646              * Fires when on first focus - needed by toolbars..
17647              * @param {HtmlEditor} this
17648              */
17649             firstfocus: true,
17650             /**
17651              * @event autosave
17652              * Auto save the htmlEditor value as a file into Events
17653              * @param {HtmlEditor} this
17654              */
17655             autosave: true,
17656             /**
17657              * @event savedpreview
17658              * preview the saved version of htmlEditor
17659              * @param {HtmlEditor} this
17660              */
17661             savedpreview: true
17662         });
17663 };
17664
17665
17666 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
17667     
17668     
17669       /**
17670      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
17671      */
17672     toolbars : false,
17673    
17674      /**
17675      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
17676      *                        Roo.resizable.
17677      */
17678     resizable : false,
17679      /**
17680      * @cfg {Number} height (in pixels)
17681      */   
17682     height: 300,
17683    /**
17684      * @cfg {Number} width (in pixels)
17685      */   
17686     width: false,
17687     
17688     /**
17689      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
17690      * 
17691      */
17692     stylesheets: false,
17693     
17694     // id of frame..
17695     frameId: false,
17696     
17697     // private properties
17698     validationEvent : false,
17699     deferHeight: true,
17700     initialized : false,
17701     activated : false,
17702     
17703     onFocus : Roo.emptyFn,
17704     iframePad:3,
17705     hideMode:'offsets',
17706     
17707     
17708     tbContainer : false,
17709     
17710     toolbarContainer :function() {
17711         return this.wrap.select('.x-html-editor-tb',true).first();
17712     },
17713
17714     /**
17715      * Protected method that will not generally be called directly. It
17716      * is called when the editor creates its toolbar. Override this method if you need to
17717      * add custom toolbar buttons.
17718      * @param {HtmlEditor} editor
17719      */
17720     createToolbar : function(){
17721         
17722         Roo.log("create toolbars");
17723         
17724         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
17725         this.toolbars[0].render(this.toolbarContainer());
17726         
17727         return;
17728         
17729 //        if (!editor.toolbars || !editor.toolbars.length) {
17730 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
17731 //        }
17732 //        
17733 //        for (var i =0 ; i < editor.toolbars.length;i++) {
17734 //            editor.toolbars[i] = Roo.factory(
17735 //                    typeof(editor.toolbars[i]) == 'string' ?
17736 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
17737 //                Roo.bootstrap.HtmlEditor);
17738 //            editor.toolbars[i].init(editor);
17739 //        }
17740     },
17741
17742      
17743     // private
17744     onRender : function(ct, position)
17745     {
17746        // Roo.log("Call onRender: " + this.xtype);
17747         var _t = this;
17748         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
17749       
17750         this.wrap = this.inputEl().wrap({
17751             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
17752         });
17753         
17754         this.editorcore.onRender(ct, position);
17755          
17756         if (this.resizable) {
17757             this.resizeEl = new Roo.Resizable(this.wrap, {
17758                 pinned : true,
17759                 wrap: true,
17760                 dynamic : true,
17761                 minHeight : this.height,
17762                 height: this.height,
17763                 handles : this.resizable,
17764                 width: this.width,
17765                 listeners : {
17766                     resize : function(r, w, h) {
17767                         _t.onResize(w,h); // -something
17768                     }
17769                 }
17770             });
17771             
17772         }
17773         this.createToolbar(this);
17774        
17775         
17776         if(!this.width && this.resizable){
17777             this.setSize(this.wrap.getSize());
17778         }
17779         if (this.resizeEl) {
17780             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
17781             // should trigger onReize..
17782         }
17783         
17784     },
17785
17786     // private
17787     onResize : function(w, h)
17788     {
17789         Roo.log('resize: ' +w + ',' + h );
17790         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
17791         var ew = false;
17792         var eh = false;
17793         
17794         if(this.inputEl() ){
17795             if(typeof w == 'number'){
17796                 var aw = w - this.wrap.getFrameWidth('lr');
17797                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
17798                 ew = aw;
17799             }
17800             if(typeof h == 'number'){
17801                  var tbh = -11;  // fixme it needs to tool bar size!
17802                 for (var i =0; i < this.toolbars.length;i++) {
17803                     // fixme - ask toolbars for heights?
17804                     tbh += this.toolbars[i].el.getHeight();
17805                     //if (this.toolbars[i].footer) {
17806                     //    tbh += this.toolbars[i].footer.el.getHeight();
17807                     //}
17808                 }
17809               
17810                 
17811                 
17812                 
17813                 
17814                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
17815                 ah -= 5; // knock a few pixes off for look..
17816                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
17817                 var eh = ah;
17818             }
17819         }
17820         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
17821         this.editorcore.onResize(ew,eh);
17822         
17823     },
17824
17825     /**
17826      * Toggles the editor between standard and source edit mode.
17827      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
17828      */
17829     toggleSourceEdit : function(sourceEditMode)
17830     {
17831         this.editorcore.toggleSourceEdit(sourceEditMode);
17832         
17833         if(this.editorcore.sourceEditMode){
17834             Roo.log('editor - showing textarea');
17835             
17836 //            Roo.log('in');
17837 //            Roo.log(this.syncValue());
17838             this.syncValue();
17839             this.inputEl().removeClass(['hide', 'x-hidden']);
17840             this.inputEl().dom.removeAttribute('tabIndex');
17841             this.inputEl().focus();
17842         }else{
17843             Roo.log('editor - hiding textarea');
17844 //            Roo.log('out')
17845 //            Roo.log(this.pushValue()); 
17846             this.pushValue();
17847             
17848             this.inputEl().addClass(['hide', 'x-hidden']);
17849             this.inputEl().dom.setAttribute('tabIndex', -1);
17850             //this.deferFocus();
17851         }
17852          
17853         if(this.resizable){
17854             this.setSize(this.wrap.getSize());
17855         }
17856         
17857         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
17858     },
17859  
17860     // private (for BoxComponent)
17861     adjustSize : Roo.BoxComponent.prototype.adjustSize,
17862
17863     // private (for BoxComponent)
17864     getResizeEl : function(){
17865         return this.wrap;
17866     },
17867
17868     // private (for BoxComponent)
17869     getPositionEl : function(){
17870         return this.wrap;
17871     },
17872
17873     // private
17874     initEvents : function(){
17875         this.originalValue = this.getValue();
17876     },
17877
17878 //    /**
17879 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
17880 //     * @method
17881 //     */
17882 //    markInvalid : Roo.emptyFn,
17883 //    /**
17884 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
17885 //     * @method
17886 //     */
17887 //    clearInvalid : Roo.emptyFn,
17888
17889     setValue : function(v){
17890         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
17891         this.editorcore.pushValue();
17892     },
17893
17894      
17895     // private
17896     deferFocus : function(){
17897         this.focus.defer(10, this);
17898     },
17899
17900     // doc'ed in Field
17901     focus : function(){
17902         this.editorcore.focus();
17903         
17904     },
17905       
17906
17907     // private
17908     onDestroy : function(){
17909         
17910         
17911         
17912         if(this.rendered){
17913             
17914             for (var i =0; i < this.toolbars.length;i++) {
17915                 // fixme - ask toolbars for heights?
17916                 this.toolbars[i].onDestroy();
17917             }
17918             
17919             this.wrap.dom.innerHTML = '';
17920             this.wrap.remove();
17921         }
17922     },
17923
17924     // private
17925     onFirstFocus : function(){
17926         //Roo.log("onFirstFocus");
17927         this.editorcore.onFirstFocus();
17928          for (var i =0; i < this.toolbars.length;i++) {
17929             this.toolbars[i].onFirstFocus();
17930         }
17931         
17932     },
17933     
17934     // private
17935     syncValue : function()
17936     {   
17937         this.editorcore.syncValue();
17938     },
17939     
17940     pushValue : function()
17941     {   
17942         this.editorcore.pushValue();
17943     }
17944      
17945     
17946     // hide stuff that is not compatible
17947     /**
17948      * @event blur
17949      * @hide
17950      */
17951     /**
17952      * @event change
17953      * @hide
17954      */
17955     /**
17956      * @event focus
17957      * @hide
17958      */
17959     /**
17960      * @event specialkey
17961      * @hide
17962      */
17963     /**
17964      * @cfg {String} fieldClass @hide
17965      */
17966     /**
17967      * @cfg {String} focusClass @hide
17968      */
17969     /**
17970      * @cfg {String} autoCreate @hide
17971      */
17972     /**
17973      * @cfg {String} inputType @hide
17974      */
17975     /**
17976      * @cfg {String} invalidClass @hide
17977      */
17978     /**
17979      * @cfg {String} invalidText @hide
17980      */
17981     /**
17982      * @cfg {String} msgFx @hide
17983      */
17984     /**
17985      * @cfg {String} validateOnBlur @hide
17986      */
17987 });
17988  
17989     
17990    
17991    
17992    
17993       
17994 Roo.namespace('Roo.bootstrap.htmleditor');
17995 /**
17996  * @class Roo.bootstrap.HtmlEditorToolbar1
17997  * Basic Toolbar
17998  * 
17999  * Usage:
18000  *
18001  new Roo.bootstrap.HtmlEditor({
18002     ....
18003     toolbars : [
18004         new Roo.bootstrap.HtmlEditorToolbar1({
18005             disable : { fonts: 1 , format: 1, ..., ... , ...],
18006             btns : [ .... ]
18007         })
18008     }
18009      
18010  * 
18011  * @cfg {Object} disable List of elements to disable..
18012  * @cfg {Array} btns List of additional buttons.
18013  * 
18014  * 
18015  * NEEDS Extra CSS? 
18016  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
18017  */
18018  
18019 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
18020 {
18021     
18022     Roo.apply(this, config);
18023     
18024     // default disabled, based on 'good practice'..
18025     this.disable = this.disable || {};
18026     Roo.applyIf(this.disable, {
18027         fontSize : true,
18028         colors : true,
18029         specialElements : true
18030     });
18031     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
18032     
18033     this.editor = config.editor;
18034     this.editorcore = config.editor.editorcore;
18035     
18036     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
18037     
18038     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
18039     // dont call parent... till later.
18040 }
18041 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
18042      
18043     bar : true,
18044     
18045     editor : false,
18046     editorcore : false,
18047     
18048     
18049     formats : [
18050         "p" ,  
18051         "h1","h2","h3","h4","h5","h6", 
18052         "pre", "code", 
18053         "abbr", "acronym", "address", "cite", "samp", "var",
18054         'div','span'
18055     ],
18056     
18057     onRender : function(ct, position)
18058     {
18059        // Roo.log("Call onRender: " + this.xtype);
18060         
18061        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
18062        Roo.log(this.el);
18063        this.el.dom.style.marginBottom = '0';
18064        var _this = this;
18065        var editorcore = this.editorcore;
18066        var editor= this.editor;
18067        
18068        var children = [];
18069        var btn = function(id,cmd , toggle, handler){
18070        
18071             var  event = toggle ? 'toggle' : 'click';
18072        
18073             var a = {
18074                 size : 'sm',
18075                 xtype: 'Button',
18076                 xns: Roo.bootstrap,
18077                 glyphicon : id,
18078                 cmd : id || cmd,
18079                 enableToggle:toggle !== false,
18080                 //html : 'submit'
18081                 pressed : toggle ? false : null,
18082                 listeners : {}
18083             }
18084             a.listeners[toggle ? 'toggle' : 'click'] = function() {
18085                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
18086             }
18087             children.push(a);
18088             return a;
18089        }
18090         
18091         var style = {
18092                 xtype: 'Button',
18093                 size : 'sm',
18094                 xns: Roo.bootstrap,
18095                 glyphicon : 'font',
18096                 //html : 'submit'
18097                 menu : {
18098                     xtype: 'Menu',
18099                     xns: Roo.bootstrap,
18100                     items:  []
18101                 }
18102         };
18103         Roo.each(this.formats, function(f) {
18104             style.menu.items.push({
18105                 xtype :'MenuItem',
18106                 xns: Roo.bootstrap,
18107                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
18108                 tagname : f,
18109                 listeners : {
18110                     click : function()
18111                     {
18112                         editorcore.insertTag(this.tagname);
18113                         editor.focus();
18114                     }
18115                 }
18116                 
18117             });
18118         });
18119          children.push(style);   
18120             
18121             
18122         btn('bold',false,true);
18123         btn('italic',false,true);
18124         btn('align-left', 'justifyleft',true);
18125         btn('align-center', 'justifycenter',true);
18126         btn('align-right' , 'justifyright',true);
18127         btn('link', false, false, function(btn) {
18128             //Roo.log("create link?");
18129             var url = prompt(this.createLinkText, this.defaultLinkValue);
18130             if(url && url != 'http:/'+'/'){
18131                 this.editorcore.relayCmd('createlink', url);
18132             }
18133         }),
18134         btn('list','insertunorderedlist',true);
18135         btn('pencil', false,true, function(btn){
18136                 Roo.log(this);
18137                 
18138                 this.toggleSourceEdit(btn.pressed);
18139         });
18140         /*
18141         var cog = {
18142                 xtype: 'Button',
18143                 size : 'sm',
18144                 xns: Roo.bootstrap,
18145                 glyphicon : 'cog',
18146                 //html : 'submit'
18147                 menu : {
18148                     xtype: 'Menu',
18149                     xns: Roo.bootstrap,
18150                     items:  []
18151                 }
18152         };
18153         
18154         cog.menu.items.push({
18155             xtype :'MenuItem',
18156             xns: Roo.bootstrap,
18157             html : Clean styles,
18158             tagname : f,
18159             listeners : {
18160                 click : function()
18161                 {
18162                     editorcore.insertTag(this.tagname);
18163                     editor.focus();
18164                 }
18165             }
18166             
18167         });
18168        */
18169         
18170          
18171        this.xtype = 'NavSimplebar';
18172         
18173         for(var i=0;i< children.length;i++) {
18174             
18175             this.buttons.add(this.addxtypeChild(children[i]));
18176             
18177         }
18178         
18179         editor.on('editorevent', this.updateToolbar, this);
18180     },
18181     onBtnClick : function(id)
18182     {
18183        this.editorcore.relayCmd(id);
18184        this.editorcore.focus();
18185     },
18186     
18187     /**
18188      * Protected method that will not generally be called directly. It triggers
18189      * a toolbar update by reading the markup state of the current selection in the editor.
18190      */
18191     updateToolbar: function(){
18192
18193         if(!this.editorcore.activated){
18194             this.editor.onFirstFocus(); // is this neeed?
18195             return;
18196         }
18197
18198         var btns = this.buttons; 
18199         var doc = this.editorcore.doc;
18200         btns.get('bold').setActive(doc.queryCommandState('bold'));
18201         btns.get('italic').setActive(doc.queryCommandState('italic'));
18202         //btns.get('underline').setActive(doc.queryCommandState('underline'));
18203         
18204         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
18205         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
18206         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
18207         
18208         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
18209         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
18210          /*
18211         
18212         var ans = this.editorcore.getAllAncestors();
18213         if (this.formatCombo) {
18214             
18215             
18216             var store = this.formatCombo.store;
18217             this.formatCombo.setValue("");
18218             for (var i =0; i < ans.length;i++) {
18219                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
18220                     // select it..
18221                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
18222                     break;
18223                 }
18224             }
18225         }
18226         
18227         
18228         
18229         // hides menus... - so this cant be on a menu...
18230         Roo.bootstrap.MenuMgr.hideAll();
18231         */
18232         Roo.bootstrap.MenuMgr.hideAll();
18233         //this.editorsyncValue();
18234     },
18235     onFirstFocus: function() {
18236         this.buttons.each(function(item){
18237            item.enable();
18238         });
18239     },
18240     toggleSourceEdit : function(sourceEditMode){
18241         
18242           
18243         if(sourceEditMode){
18244             Roo.log("disabling buttons");
18245            this.buttons.each( function(item){
18246                 if(item.cmd != 'pencil'){
18247                     item.disable();
18248                 }
18249             });
18250           
18251         }else{
18252             Roo.log("enabling buttons");
18253             if(this.editorcore.initialized){
18254                 this.buttons.each( function(item){
18255                     item.enable();
18256                 });
18257             }
18258             
18259         }
18260         Roo.log("calling toggole on editor");
18261         // tell the editor that it's been pressed..
18262         this.editor.toggleSourceEdit(sourceEditMode);
18263        
18264     }
18265 });
18266
18267
18268
18269
18270
18271 /**
18272  * @class Roo.bootstrap.Table.AbstractSelectionModel
18273  * @extends Roo.util.Observable
18274  * Abstract base class for grid SelectionModels.  It provides the interface that should be
18275  * implemented by descendant classes.  This class should not be directly instantiated.
18276  * @constructor
18277  */
18278 Roo.bootstrap.Table.AbstractSelectionModel = function(){
18279     this.locked = false;
18280     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
18281 };
18282
18283
18284 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
18285     /** @ignore Called by the grid automatically. Do not call directly. */
18286     init : function(grid){
18287         this.grid = grid;
18288         this.initEvents();
18289     },
18290
18291     /**
18292      * Locks the selections.
18293      */
18294     lock : function(){
18295         this.locked = true;
18296     },
18297
18298     /**
18299      * Unlocks the selections.
18300      */
18301     unlock : function(){
18302         this.locked = false;
18303     },
18304
18305     /**
18306      * Returns true if the selections are locked.
18307      * @return {Boolean}
18308      */
18309     isLocked : function(){
18310         return this.locked;
18311     }
18312 });
18313 /**
18314  * @extends Roo.bootstrap.Table.AbstractSelectionModel
18315  * @class Roo.bootstrap.Table.RowSelectionModel
18316  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
18317  * It supports multiple selections and keyboard selection/navigation. 
18318  * @constructor
18319  * @param {Object} config
18320  */
18321
18322 Roo.bootstrap.Table.RowSelectionModel = function(config){
18323     Roo.apply(this, config);
18324     this.selections = new Roo.util.MixedCollection(false, function(o){
18325         return o.id;
18326     });
18327
18328     this.last = false;
18329     this.lastActive = false;
18330
18331     this.addEvents({
18332         /**
18333              * @event selectionchange
18334              * Fires when the selection changes
18335              * @param {SelectionModel} this
18336              */
18337             "selectionchange" : true,
18338         /**
18339              * @event afterselectionchange
18340              * Fires after the selection changes (eg. by key press or clicking)
18341              * @param {SelectionModel} this
18342              */
18343             "afterselectionchange" : true,
18344         /**
18345              * @event beforerowselect
18346              * Fires when a row is selected being selected, return false to cancel.
18347              * @param {SelectionModel} this
18348              * @param {Number} rowIndex The selected index
18349              * @param {Boolean} keepExisting False if other selections will be cleared
18350              */
18351             "beforerowselect" : true,
18352         /**
18353              * @event rowselect
18354              * Fires when a row is selected.
18355              * @param {SelectionModel} this
18356              * @param {Number} rowIndex The selected index
18357              * @param {Roo.data.Record} r The record
18358              */
18359             "rowselect" : true,
18360         /**
18361              * @event rowdeselect
18362              * Fires when a row is deselected.
18363              * @param {SelectionModel} this
18364              * @param {Number} rowIndex The selected index
18365              */
18366         "rowdeselect" : true
18367     });
18368     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
18369     this.locked = false;
18370 };
18371
18372 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
18373     /**
18374      * @cfg {Boolean} singleSelect
18375      * True to allow selection of only one row at a time (defaults to false)
18376      */
18377     singleSelect : false,
18378
18379     // private
18380     initEvents : function(){
18381
18382         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
18383             this.grid.on("mousedown", this.handleMouseDown, this);
18384         }else{ // allow click to work like normal
18385             this.grid.on("rowclick", this.handleDragableRowClick, this);
18386         }
18387
18388         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
18389             "up" : function(e){
18390                 if(!e.shiftKey){
18391                     this.selectPrevious(e.shiftKey);
18392                 }else if(this.last !== false && this.lastActive !== false){
18393                     var last = this.last;
18394                     this.selectRange(this.last,  this.lastActive-1);
18395                     this.grid.getView().focusRow(this.lastActive);
18396                     if(last !== false){
18397                         this.last = last;
18398                     }
18399                 }else{
18400                     this.selectFirstRow();
18401                 }
18402                 this.fireEvent("afterselectionchange", this);
18403             },
18404             "down" : function(e){
18405                 if(!e.shiftKey){
18406                     this.selectNext(e.shiftKey);
18407                 }else if(this.last !== false && this.lastActive !== false){
18408                     var last = this.last;
18409                     this.selectRange(this.last,  this.lastActive+1);
18410                     this.grid.getView().focusRow(this.lastActive);
18411                     if(last !== false){
18412                         this.last = last;
18413                     }
18414                 }else{
18415                     this.selectFirstRow();
18416                 }
18417                 this.fireEvent("afterselectionchange", this);
18418             },
18419             scope: this
18420         });
18421
18422         var view = this.grid.view;
18423         view.on("refresh", this.onRefresh, this);
18424         view.on("rowupdated", this.onRowUpdated, this);
18425         view.on("rowremoved", this.onRemove, this);
18426     },
18427
18428     // private
18429     onRefresh : function(){
18430         var ds = this.grid.dataSource, i, v = this.grid.view;
18431         var s = this.selections;
18432         s.each(function(r){
18433             if((i = ds.indexOfId(r.id)) != -1){
18434                 v.onRowSelect(i);
18435             }else{
18436                 s.remove(r);
18437             }
18438         });
18439     },
18440
18441     // private
18442     onRemove : function(v, index, r){
18443         this.selections.remove(r);
18444     },
18445
18446     // private
18447     onRowUpdated : function(v, index, r){
18448         if(this.isSelected(r)){
18449             v.onRowSelect(index);
18450         }
18451     },
18452
18453     /**
18454      * Select records.
18455      * @param {Array} records The records to select
18456      * @param {Boolean} keepExisting (optional) True to keep existing selections
18457      */
18458     selectRecords : function(records, keepExisting){
18459         if(!keepExisting){
18460             this.clearSelections();
18461         }
18462         var ds = this.grid.dataSource;
18463         for(var i = 0, len = records.length; i < len; i++){
18464             this.selectRow(ds.indexOf(records[i]), true);
18465         }
18466     },
18467
18468     /**
18469      * Gets the number of selected rows.
18470      * @return {Number}
18471      */
18472     getCount : function(){
18473         return this.selections.length;
18474     },
18475
18476     /**
18477      * Selects the first row in the grid.
18478      */
18479     selectFirstRow : function(){
18480         this.selectRow(0);
18481     },
18482
18483     /**
18484      * Select the last row.
18485      * @param {Boolean} keepExisting (optional) True to keep existing selections
18486      */
18487     selectLastRow : function(keepExisting){
18488         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
18489     },
18490
18491     /**
18492      * Selects the row immediately following the last selected row.
18493      * @param {Boolean} keepExisting (optional) True to keep existing selections
18494      */
18495     selectNext : function(keepExisting){
18496         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
18497             this.selectRow(this.last+1, keepExisting);
18498             this.grid.getView().focusRow(this.last);
18499         }
18500     },
18501
18502     /**
18503      * Selects the row that precedes the last selected row.
18504      * @param {Boolean} keepExisting (optional) True to keep existing selections
18505      */
18506     selectPrevious : function(keepExisting){
18507         if(this.last){
18508             this.selectRow(this.last-1, keepExisting);
18509             this.grid.getView().focusRow(this.last);
18510         }
18511     },
18512
18513     /**
18514      * Returns the selected records
18515      * @return {Array} Array of selected records
18516      */
18517     getSelections : function(){
18518         return [].concat(this.selections.items);
18519     },
18520
18521     /**
18522      * Returns the first selected record.
18523      * @return {Record}
18524      */
18525     getSelected : function(){
18526         return this.selections.itemAt(0);
18527     },
18528
18529
18530     /**
18531      * Clears all selections.
18532      */
18533     clearSelections : function(fast){
18534         if(this.locked) return;
18535         if(fast !== true){
18536             var ds = this.grid.dataSource;
18537             var s = this.selections;
18538             s.each(function(r){
18539                 this.deselectRow(ds.indexOfId(r.id));
18540             }, this);
18541             s.clear();
18542         }else{
18543             this.selections.clear();
18544         }
18545         this.last = false;
18546     },
18547
18548
18549     /**
18550      * Selects all rows.
18551      */
18552     selectAll : function(){
18553         if(this.locked) return;
18554         this.selections.clear();
18555         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
18556             this.selectRow(i, true);
18557         }
18558     },
18559
18560     /**
18561      * Returns True if there is a selection.
18562      * @return {Boolean}
18563      */
18564     hasSelection : function(){
18565         return this.selections.length > 0;
18566     },
18567
18568     /**
18569      * Returns True if the specified row is selected.
18570      * @param {Number/Record} record The record or index of the record to check
18571      * @return {Boolean}
18572      */
18573     isSelected : function(index){
18574         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
18575         return (r && this.selections.key(r.id) ? true : false);
18576     },
18577
18578     /**
18579      * Returns True if the specified record id is selected.
18580      * @param {String} id The id of record to check
18581      * @return {Boolean}
18582      */
18583     isIdSelected : function(id){
18584         return (this.selections.key(id) ? true : false);
18585     },
18586
18587     // private
18588     handleMouseDown : function(e, t){
18589         var view = this.grid.getView(), rowIndex;
18590         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
18591             return;
18592         };
18593         if(e.shiftKey && this.last !== false){
18594             var last = this.last;
18595             this.selectRange(last, rowIndex, e.ctrlKey);
18596             this.last = last; // reset the last
18597             view.focusRow(rowIndex);
18598         }else{
18599             var isSelected = this.isSelected(rowIndex);
18600             if(e.button !== 0 && isSelected){
18601                 view.focusRow(rowIndex);
18602             }else if(e.ctrlKey && isSelected){
18603                 this.deselectRow(rowIndex);
18604             }else if(!isSelected){
18605                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
18606                 view.focusRow(rowIndex);
18607             }
18608         }
18609         this.fireEvent("afterselectionchange", this);
18610     },
18611     // private
18612     handleDragableRowClick :  function(grid, rowIndex, e) 
18613     {
18614         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
18615             this.selectRow(rowIndex, false);
18616             grid.view.focusRow(rowIndex);
18617              this.fireEvent("afterselectionchange", this);
18618         }
18619     },
18620     
18621     /**
18622      * Selects multiple rows.
18623      * @param {Array} rows Array of the indexes of the row to select
18624      * @param {Boolean} keepExisting (optional) True to keep existing selections
18625      */
18626     selectRows : function(rows, keepExisting){
18627         if(!keepExisting){
18628             this.clearSelections();
18629         }
18630         for(var i = 0, len = rows.length; i < len; i++){
18631             this.selectRow(rows[i], true);
18632         }
18633     },
18634
18635     /**
18636      * Selects a range of rows. All rows in between startRow and endRow are also selected.
18637      * @param {Number} startRow The index of the first row in the range
18638      * @param {Number} endRow The index of the last row in the range
18639      * @param {Boolean} keepExisting (optional) True to retain existing selections
18640      */
18641     selectRange : function(startRow, endRow, keepExisting){
18642         if(this.locked) return;
18643         if(!keepExisting){
18644             this.clearSelections();
18645         }
18646         if(startRow <= endRow){
18647             for(var i = startRow; i <= endRow; i++){
18648                 this.selectRow(i, true);
18649             }
18650         }else{
18651             for(var i = startRow; i >= endRow; i--){
18652                 this.selectRow(i, true);
18653             }
18654         }
18655     },
18656
18657     /**
18658      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
18659      * @param {Number} startRow The index of the first row in the range
18660      * @param {Number} endRow The index of the last row in the range
18661      */
18662     deselectRange : function(startRow, endRow, preventViewNotify){
18663         if(this.locked) return;
18664         for(var i = startRow; i <= endRow; i++){
18665             this.deselectRow(i, preventViewNotify);
18666         }
18667     },
18668
18669     /**
18670      * Selects a row.
18671      * @param {Number} row The index of the row to select
18672      * @param {Boolean} keepExisting (optional) True to keep existing selections
18673      */
18674     selectRow : function(index, keepExisting, preventViewNotify){
18675         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
18676         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
18677             if(!keepExisting || this.singleSelect){
18678                 this.clearSelections();
18679             }
18680             var r = this.grid.dataSource.getAt(index);
18681             this.selections.add(r);
18682             this.last = this.lastActive = index;
18683             if(!preventViewNotify){
18684                 this.grid.getView().onRowSelect(index);
18685             }
18686             this.fireEvent("rowselect", this, index, r);
18687             this.fireEvent("selectionchange", this);
18688         }
18689     },
18690
18691     /**
18692      * Deselects a row.
18693      * @param {Number} row The index of the row to deselect
18694      */
18695     deselectRow : function(index, preventViewNotify){
18696         if(this.locked) return;
18697         if(this.last == index){
18698             this.last = false;
18699         }
18700         if(this.lastActive == index){
18701             this.lastActive = false;
18702         }
18703         var r = this.grid.dataSource.getAt(index);
18704         this.selections.remove(r);
18705         if(!preventViewNotify){
18706             this.grid.getView().onRowDeselect(index);
18707         }
18708         this.fireEvent("rowdeselect", this, index);
18709         this.fireEvent("selectionchange", this);
18710     },
18711
18712     // private
18713     restoreLast : function(){
18714         if(this._last){
18715             this.last = this._last;
18716         }
18717     },
18718
18719     // private
18720     acceptsNav : function(row, col, cm){
18721         return !cm.isHidden(col) && cm.isCellEditable(col, row);
18722     },
18723
18724     // private
18725     onEditorKey : function(field, e){
18726         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
18727         if(k == e.TAB){
18728             e.stopEvent();
18729             ed.completeEdit();
18730             if(e.shiftKey){
18731                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
18732             }else{
18733                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
18734             }
18735         }else if(k == e.ENTER && !e.ctrlKey){
18736             e.stopEvent();
18737             ed.completeEdit();
18738             if(e.shiftKey){
18739                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
18740             }else{
18741                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
18742             }
18743         }else if(k == e.ESC){
18744             ed.cancelEdit();
18745         }
18746         if(newCell){
18747             g.startEditing(newCell[0], newCell[1]);
18748         }
18749     }
18750 });/*
18751  * Based on:
18752  * Ext JS Library 1.1.1
18753  * Copyright(c) 2006-2007, Ext JS, LLC.
18754  *
18755  * Originally Released Under LGPL - original licence link has changed is not relivant.
18756  *
18757  * Fork - LGPL
18758  * <script type="text/javascript">
18759  */
18760  
18761 /**
18762  * @class Roo.bootstrap.PagingToolbar
18763  * @extends Roo.Row
18764  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
18765  * @constructor
18766  * Create a new PagingToolbar
18767  * @param {Object} config The config object
18768  */
18769 Roo.bootstrap.PagingToolbar = function(config)
18770 {
18771     // old args format still supported... - xtype is prefered..
18772         // created from xtype...
18773     var ds = config.dataSource;
18774     this.toolbarItems = [];
18775     if (config.items) {
18776         this.toolbarItems = config.items;
18777 //        config.items = [];
18778     }
18779     
18780     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
18781     this.ds = ds;
18782     this.cursor = 0;
18783     if (ds) { 
18784         this.bind(ds);
18785     }
18786     
18787     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
18788     
18789 };
18790
18791 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
18792     /**
18793      * @cfg {Roo.data.Store} dataSource
18794      * The underlying data store providing the paged data
18795      */
18796     /**
18797      * @cfg {String/HTMLElement/Element} container
18798      * container The id or element that will contain the toolbar
18799      */
18800     /**
18801      * @cfg {Boolean} displayInfo
18802      * True to display the displayMsg (defaults to false)
18803      */
18804     /**
18805      * @cfg {Number} pageSize
18806      * The number of records to display per page (defaults to 20)
18807      */
18808     pageSize: 20,
18809     /**
18810      * @cfg {String} displayMsg
18811      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
18812      */
18813     displayMsg : 'Displaying {0} - {1} of {2}',
18814     /**
18815      * @cfg {String} emptyMsg
18816      * The message to display when no records are found (defaults to "No data to display")
18817      */
18818     emptyMsg : 'No data to display',
18819     /**
18820      * Customizable piece of the default paging text (defaults to "Page")
18821      * @type String
18822      */
18823     beforePageText : "Page",
18824     /**
18825      * Customizable piece of the default paging text (defaults to "of %0")
18826      * @type String
18827      */
18828     afterPageText : "of {0}",
18829     /**
18830      * Customizable piece of the default paging text (defaults to "First Page")
18831      * @type String
18832      */
18833     firstText : "First Page",
18834     /**
18835      * Customizable piece of the default paging text (defaults to "Previous Page")
18836      * @type String
18837      */
18838     prevText : "Previous Page",
18839     /**
18840      * Customizable piece of the default paging text (defaults to "Next Page")
18841      * @type String
18842      */
18843     nextText : "Next Page",
18844     /**
18845      * Customizable piece of the default paging text (defaults to "Last Page")
18846      * @type String
18847      */
18848     lastText : "Last Page",
18849     /**
18850      * Customizable piece of the default paging text (defaults to "Refresh")
18851      * @type String
18852      */
18853     refreshText : "Refresh",
18854
18855     buttons : false,
18856     // private
18857     onRender : function(ct, position) 
18858     {
18859         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
18860         this.navgroup.parentId = this.id;
18861         this.navgroup.onRender(this.el, null);
18862         // add the buttons to the navgroup
18863         
18864         if(this.displayInfo){
18865             Roo.log(this.el.select('ul.navbar-nav',true).first());
18866             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
18867             this.displayEl = this.el.select('.x-paging-info', true).first();
18868 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
18869 //            this.displayEl = navel.el.select('span',true).first();
18870         }
18871         
18872         var _this = this;
18873         
18874         if(this.buttons){
18875             Roo.each(_this.buttons, function(e){
18876                Roo.factory(e).onRender(_this.el, null);
18877             });
18878         }
18879             
18880         Roo.each(_this.toolbarItems, function(e) {
18881             _this.navgroup.addItem(e);
18882         });
18883         
18884         this.first = this.navgroup.addItem({
18885             tooltip: this.firstText,
18886             cls: "prev",
18887             icon : 'fa fa-backward',
18888             disabled: true,
18889             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
18890         });
18891         
18892         this.prev =  this.navgroup.addItem({
18893             tooltip: this.prevText,
18894             cls: "prev",
18895             icon : 'fa fa-step-backward',
18896             disabled: true,
18897             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
18898         });
18899     //this.addSeparator();
18900         
18901         
18902         var field = this.navgroup.addItem( {
18903             tagtype : 'span',
18904             cls : 'x-paging-position',
18905             
18906             html : this.beforePageText  +
18907                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
18908                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
18909          } ); //?? escaped?
18910         
18911         this.field = field.el.select('input', true).first();
18912         this.field.on("keydown", this.onPagingKeydown, this);
18913         this.field.on("focus", function(){this.dom.select();});
18914     
18915     
18916         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
18917         //this.field.setHeight(18);
18918         //this.addSeparator();
18919         this.next = this.navgroup.addItem({
18920             tooltip: this.nextText,
18921             cls: "next",
18922             html : ' <i class="fa fa-step-forward">',
18923             disabled: true,
18924             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
18925         });
18926         this.last = this.navgroup.addItem({
18927             tooltip: this.lastText,
18928             icon : 'fa fa-forward',
18929             cls: "next",
18930             disabled: true,
18931             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
18932         });
18933     //this.addSeparator();
18934         this.loading = this.navgroup.addItem({
18935             tooltip: this.refreshText,
18936             icon: 'fa fa-refresh',
18937             
18938             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
18939         });
18940
18941     },
18942
18943     // private
18944     updateInfo : function(){
18945         if(this.displayEl){
18946             var count = this.ds.getCount();
18947             var msg = count == 0 ?
18948                 this.emptyMsg :
18949                 String.format(
18950                     this.displayMsg,
18951                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
18952                 );
18953             this.displayEl.update(msg);
18954         }
18955     },
18956
18957     // private
18958     onLoad : function(ds, r, o){
18959        this.cursor = o.params ? o.params.start : 0;
18960        var d = this.getPageData(),
18961             ap = d.activePage,
18962             ps = d.pages;
18963         
18964        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
18965        this.field.dom.value = ap;
18966        this.first.setDisabled(ap == 1);
18967        this.prev.setDisabled(ap == 1);
18968        this.next.setDisabled(ap == ps);
18969        this.last.setDisabled(ap == ps);
18970        this.loading.enable();
18971        this.updateInfo();
18972     },
18973
18974     // private
18975     getPageData : function(){
18976         var total = this.ds.getTotalCount();
18977         return {
18978             total : total,
18979             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
18980             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
18981         };
18982     },
18983
18984     // private
18985     onLoadError : function(){
18986         this.loading.enable();
18987     },
18988
18989     // private
18990     onPagingKeydown : function(e){
18991         var k = e.getKey();
18992         var d = this.getPageData();
18993         if(k == e.RETURN){
18994             var v = this.field.dom.value, pageNum;
18995             if(!v || isNaN(pageNum = parseInt(v, 10))){
18996                 this.field.dom.value = d.activePage;
18997                 return;
18998             }
18999             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
19000             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
19001             e.stopEvent();
19002         }
19003         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))
19004         {
19005           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
19006           this.field.dom.value = pageNum;
19007           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
19008           e.stopEvent();
19009         }
19010         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
19011         {
19012           var v = this.field.dom.value, pageNum; 
19013           var increment = (e.shiftKey) ? 10 : 1;
19014           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
19015             increment *= -1;
19016           if(!v || isNaN(pageNum = parseInt(v, 10))) {
19017             this.field.dom.value = d.activePage;
19018             return;
19019           }
19020           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
19021           {
19022             this.field.dom.value = parseInt(v, 10) + increment;
19023             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
19024             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
19025           }
19026           e.stopEvent();
19027         }
19028     },
19029
19030     // private
19031     beforeLoad : function(){
19032         if(this.loading){
19033             this.loading.disable();
19034         }
19035     },
19036
19037     // private
19038     onClick : function(which){
19039         var ds = this.ds;
19040         if (!ds) {
19041             return;
19042         }
19043         switch(which){
19044             case "first":
19045                 ds.load({params:{start: 0, limit: this.pageSize}});
19046             break;
19047             case "prev":
19048                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
19049             break;
19050             case "next":
19051                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
19052             break;
19053             case "last":
19054                 var total = ds.getTotalCount();
19055                 var extra = total % this.pageSize;
19056                 var lastStart = extra ? (total - extra) : total-this.pageSize;
19057                 ds.load({params:{start: lastStart, limit: this.pageSize}});
19058             break;
19059             case "refresh":
19060                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
19061             break;
19062         }
19063     },
19064
19065     /**
19066      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
19067      * @param {Roo.data.Store} store The data store to unbind
19068      */
19069     unbind : function(ds){
19070         ds.un("beforeload", this.beforeLoad, this);
19071         ds.un("load", this.onLoad, this);
19072         ds.un("loadexception", this.onLoadError, this);
19073         ds.un("remove", this.updateInfo, this);
19074         ds.un("add", this.updateInfo, this);
19075         this.ds = undefined;
19076     },
19077
19078     /**
19079      * Binds the paging toolbar to the specified {@link Roo.data.Store}
19080      * @param {Roo.data.Store} store The data store to bind
19081      */
19082     bind : function(ds){
19083         ds.on("beforeload", this.beforeLoad, this);
19084         ds.on("load", this.onLoad, this);
19085         ds.on("loadexception", this.onLoadError, this);
19086         ds.on("remove", this.updateInfo, this);
19087         ds.on("add", this.updateInfo, this);
19088         this.ds = ds;
19089     }
19090 });/*
19091  * - LGPL
19092  *
19093  * element
19094  * 
19095  */
19096
19097 /**
19098  * @class Roo.bootstrap.MessageBar
19099  * @extends Roo.bootstrap.Component
19100  * Bootstrap MessageBar class
19101  * @cfg {String} html contents of the MessageBar
19102  * @cfg {String} weight (info | success | warning | danger) default info
19103  * @cfg {String} beforeClass insert the bar before the given class
19104  * @cfg {Boolean} closable (true | false) default false
19105  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
19106  * 
19107  * @constructor
19108  * Create a new Element
19109  * @param {Object} config The config object
19110  */
19111
19112 Roo.bootstrap.MessageBar = function(config){
19113     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
19114 };
19115
19116 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
19117     
19118     html: '',
19119     weight: 'info',
19120     closable: false,
19121     fixed: false,
19122     beforeClass: 'bootstrap-sticky-wrap',
19123     
19124     getAutoCreate : function(){
19125         
19126         var cfg = {
19127             tag: 'div',
19128             cls: 'alert alert-dismissable alert-' + this.weight,
19129             cn: [
19130                 {
19131                     tag: 'span',
19132                     cls: 'message',
19133                     html: this.html || ''
19134                 }
19135             ]
19136         }
19137         
19138         if(this.fixed){
19139             cfg.cls += ' alert-messages-fixed';
19140         }
19141         
19142         if(this.closable){
19143             cfg.cn.push({
19144                 tag: 'button',
19145                 cls: 'close',
19146                 html: 'x'
19147             });
19148         }
19149         
19150         return cfg;
19151     },
19152     
19153     onRender : function(ct, position)
19154     {
19155         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19156         
19157         if(!this.el){
19158             var cfg = Roo.apply({},  this.getAutoCreate());
19159             cfg.id = Roo.id();
19160             
19161             if (this.cls) {
19162                 cfg.cls += ' ' + this.cls;
19163             }
19164             if (this.style) {
19165                 cfg.style = this.style;
19166             }
19167             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
19168             
19169             this.el.setVisibilityMode(Roo.Element.DISPLAY);
19170         }
19171         
19172         this.el.select('>button.close').on('click', this.hide, this);
19173         
19174     },
19175     
19176     show : function()
19177     {
19178         if (!this.rendered) {
19179             this.render();
19180         }
19181         
19182         this.el.show();
19183         
19184         this.fireEvent('show', this);
19185         
19186     },
19187     
19188     hide : function()
19189     {
19190         if (!this.rendered) {
19191             this.render();
19192         }
19193         
19194         this.el.hide();
19195         
19196         this.fireEvent('hide', this);
19197     },
19198     
19199     update : function()
19200     {
19201 //        var e = this.el.dom.firstChild;
19202 //        
19203 //        if(this.closable){
19204 //            e = e.nextSibling;
19205 //        }
19206 //        
19207 //        e.data = this.html || '';
19208
19209         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
19210     }
19211    
19212 });
19213
19214  
19215
19216      /*
19217  * - LGPL
19218  *
19219  * Graph
19220  * 
19221  */
19222
19223
19224 /**
19225  * @class Roo.bootstrap.Graph
19226  * @extends Roo.bootstrap.Component
19227  * Bootstrap Graph class
19228 > Prameters
19229  -sm {number} sm 4
19230  -md {number} md 5
19231  @cfg {String} graphtype  bar | vbar | pie
19232  @cfg {number} g_x coodinator | centre x (pie)
19233  @cfg {number} g_y coodinator | centre y (pie)
19234  @cfg {number} g_r radius (pie)
19235  @cfg {number} g_height height of the chart (respected by all elements in the set)
19236  @cfg {number} g_width width of the chart (respected by all elements in the set)
19237  @cfg {Object} title The title of the chart
19238     
19239  -{Array}  values
19240  -opts (object) options for the chart 
19241      o {
19242      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
19243      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
19244      o vgutter (number)
19245      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.
19246      o stacked (boolean) whether or not to tread values as in a stacked bar chart
19247      o to
19248      o stretch (boolean)
19249      o }
19250  -opts (object) options for the pie
19251      o{
19252      o cut
19253      o startAngle (number)
19254      o endAngle (number)
19255      } 
19256  *
19257  * @constructor
19258  * Create a new Input
19259  * @param {Object} config The config object
19260  */
19261
19262 Roo.bootstrap.Graph = function(config){
19263     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
19264     
19265     this.addEvents({
19266         // img events
19267         /**
19268          * @event click
19269          * The img click event for the img.
19270          * @param {Roo.EventObject} e
19271          */
19272         "click" : true
19273     });
19274 };
19275
19276 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
19277     
19278     sm: 4,
19279     md: 5,
19280     graphtype: 'bar',
19281     g_height: 250,
19282     g_width: 400,
19283     g_x: 50,
19284     g_y: 50,
19285     g_r: 30,
19286     opts:{
19287         //g_colors: this.colors,
19288         g_type: 'soft',
19289         g_gutter: '20%'
19290
19291     },
19292     title : false,
19293
19294     getAutoCreate : function(){
19295         
19296         var cfg = {
19297             tag: 'div',
19298             html : null
19299         }
19300         
19301         
19302         return  cfg;
19303     },
19304
19305     onRender : function(ct,position){
19306         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
19307         this.raphael = Raphael(this.el.dom);
19308         
19309                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19310                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19311                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19312                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
19313                 /*
19314                 r.text(160, 10, "Single Series Chart").attr(txtattr);
19315                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
19316                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
19317                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
19318                 
19319                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
19320                 r.barchart(330, 10, 300, 220, data1);
19321                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
19322                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
19323                 */
19324                 
19325                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19326                 // r.barchart(30, 30, 560, 250,  xdata, {
19327                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
19328                 //     axis : "0 0 1 1",
19329                 //     axisxlabels :  xdata
19330                 //     //yvalues : cols,
19331                    
19332                 // });
19333 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19334 //        
19335 //        this.load(null,xdata,{
19336 //                axis : "0 0 1 1",
19337 //                axisxlabels :  xdata
19338 //                });
19339
19340     },
19341
19342     load : function(graphtype,xdata,opts){
19343         this.raphael.clear();
19344         if(!graphtype) {
19345             graphtype = this.graphtype;
19346         }
19347         if(!opts){
19348             opts = this.opts;
19349         }
19350         var r = this.raphael,
19351             fin = function () {
19352                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
19353             },
19354             fout = function () {
19355                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
19356             },
19357             pfin = function() {
19358                 this.sector.stop();
19359                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
19360
19361                 if (this.label) {
19362                     this.label[0].stop();
19363                     this.label[0].attr({ r: 7.5 });
19364                     this.label[1].attr({ "font-weight": 800 });
19365                 }
19366             },
19367             pfout = function() {
19368                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
19369
19370                 if (this.label) {
19371                     this.label[0].animate({ r: 5 }, 500, "bounce");
19372                     this.label[1].attr({ "font-weight": 400 });
19373                 }
19374             };
19375
19376         switch(graphtype){
19377             case 'bar':
19378                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
19379                 break;
19380             case 'hbar':
19381                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
19382                 break;
19383             case 'pie':
19384 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
19385 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
19386 //            
19387                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
19388                 
19389                 break;
19390
19391         }
19392         
19393         if(this.title){
19394             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
19395         }
19396         
19397     },
19398     
19399     setTitle: function(o)
19400     {
19401         this.title = o;
19402     },
19403     
19404     initEvents: function() {
19405         
19406         if(!this.href){
19407             this.el.on('click', this.onClick, this);
19408         }
19409     },
19410     
19411     onClick : function(e)
19412     {
19413         Roo.log('img onclick');
19414         this.fireEvent('click', this, e);
19415     }
19416    
19417 });
19418
19419  
19420 /*
19421  * - LGPL
19422  *
19423  * numberBox
19424  * 
19425  */
19426 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19427
19428 /**
19429  * @class Roo.bootstrap.dash.NumberBox
19430  * @extends Roo.bootstrap.Component
19431  * Bootstrap NumberBox class
19432  * @cfg {String} bgcolor Box background color, such as (aqua | green | yellow | red etc..) Default aqua
19433  * @cfg {String} headline Box headline
19434  * @cfg {String} content Box content
19435  * @cfg {String} icon Box icon
19436  * @cfg {String} footer Footer text
19437  * @cfg {String} fhref Footer href
19438  * 
19439  * @constructor
19440  * Create a new NumberBox
19441  * @param {Object} config The config object
19442  */
19443
19444
19445 Roo.bootstrap.dash.NumberBox = function(config){
19446     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
19447     
19448 };
19449
19450 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
19451     
19452     bgcolor : 'aqua',
19453     headline : '',
19454     content : '',
19455     icon : '',
19456     footer : '',
19457     fhref : '',
19458     ficon : '',
19459     
19460     getAutoCreate : function(){
19461         
19462         var cfg = {
19463             tag : 'div',
19464             cls : 'small-box bg-' + this.bgcolor,
19465             cn : [
19466                 {
19467                     tag : 'div',
19468                     cls : 'inner',
19469                     cn :[
19470                         {
19471                             tag : 'h3',
19472                             cls : 'roo-headline',
19473                             html : this.headline
19474                         },
19475                         {
19476                             tag : 'p',
19477                             cls : 'roo-content',
19478                             html : this.content
19479                         }
19480                     ]
19481                 }
19482             ]
19483         }
19484         
19485         if(this.icon){
19486             cfg.cn.push({
19487                 tag : 'div',
19488                 cls : 'icon',
19489                 cn :[
19490                     {
19491                         tag : 'i',
19492                         cls : 'ion ' + this.icon
19493                     }
19494                 ]
19495             });
19496         }
19497         
19498         if(this.footer){
19499             var footer = {
19500                 tag : 'a',
19501                 cls : 'small-box-footer',
19502                 href : this.fhref || '#',
19503                 html : this.footer
19504             };
19505             
19506             cfg.cn.push(footer);
19507             
19508         }
19509         
19510         return  cfg;
19511     },
19512
19513     onRender : function(ct,position){
19514         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
19515
19516
19517        
19518                 
19519     },
19520
19521     setHeadline: function (value)
19522     {
19523         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
19524     },
19525     
19526     setFooter: function (value, href)
19527     {
19528         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
19529         
19530         if(href){
19531             this.el.select('a.small-box-footer',true).first().attr('href', href);
19532         }
19533         
19534     },
19535
19536     setContent: function (value)
19537     {
19538         this.el.select('.roo-content',true).first().dom.innerHTML = value;
19539     },
19540
19541     initEvents: function() 
19542     {   
19543         
19544     }
19545     
19546 });
19547
19548  
19549 /*
19550  * - LGPL
19551  *
19552  * TabBox
19553  * 
19554  */
19555 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19556
19557 /**
19558  * @class Roo.bootstrap.dash.TabBox
19559  * @extends Roo.bootstrap.Component
19560  * Bootstrap TabBox class
19561  * @cfg {String} title Title of the TabBox
19562  * @cfg {String} icon Icon of the TabBox
19563  * @cfg {Boolean} showtabs (true|false) show the tabs default true
19564  * 
19565  * @constructor
19566  * Create a new TabBox
19567  * @param {Object} config The config object
19568  */
19569
19570
19571 Roo.bootstrap.dash.TabBox = function(config){
19572     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
19573     this.addEvents({
19574         // raw events
19575         /**
19576          * @event addpane
19577          * When a pane is added
19578          * @param {Roo.bootstrap.dash.TabPane} pane
19579          */
19580         "addpane" : true
19581          
19582     });
19583 };
19584
19585 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
19586
19587     title : '',
19588     icon : false,
19589     showtabs : true,
19590     
19591     getChildContainer : function()
19592     {
19593         return this.el.select('.tab-content', true).first();
19594     },
19595     
19596     getAutoCreate : function(){
19597         
19598         var header = {
19599             tag: 'li',
19600             cls: 'pull-left header',
19601             html: this.title,
19602             cn : []
19603         };
19604         
19605         if(this.icon){
19606             header.cn.push({
19607                 tag: 'i',
19608                 cls: 'fa ' + this.icon
19609             });
19610         }
19611         
19612         
19613         var cfg = {
19614             tag: 'div',
19615             cls: 'nav-tabs-custom',
19616             cn: [
19617                 {
19618                     tag: 'ul',
19619                     cls: 'nav nav-tabs pull-right',
19620                     cn: [
19621                         header
19622                     ]
19623                 },
19624                 {
19625                     tag: 'div',
19626                     cls: 'tab-content no-padding',
19627                     cn: []
19628                 }
19629             ]
19630         }
19631
19632         return  cfg;
19633     },
19634     initEvents : function()
19635     {
19636         //Roo.log('add add pane handler');
19637         this.on('addpane', this.onAddPane, this);
19638     },
19639      /**
19640      * Updates the box title
19641      * @param {String} html to set the title to.
19642      */
19643     setTitle : function(value)
19644     {
19645         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
19646     },
19647     onAddPane : function(pane)
19648     {
19649         //Roo.log('addpane');
19650         //Roo.log(pane);
19651         // tabs are rendere left to right..
19652         if(!this.showtabs){
19653             return;
19654         }
19655         
19656         var ctr = this.el.select('.nav-tabs', true).first();
19657          
19658          
19659         var existing = ctr.select('.nav-tab',true);
19660         var qty = existing.getCount();;
19661         
19662         
19663         var tab = ctr.createChild({
19664             tag : 'li',
19665             cls : 'nav-tab' + (qty ? '' : ' active'),
19666             cn : [
19667                 {
19668                     tag : 'a',
19669                     href:'#',
19670                     html : pane.title
19671                 }
19672             ]
19673         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
19674         pane.tab = tab;
19675         
19676         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
19677         if (!qty) {
19678             pane.el.addClass('active');
19679         }
19680         
19681                 
19682     },
19683     onTabClick : function(ev,un,ob,pane)
19684     {
19685         //Roo.log('tab - prev default');
19686         ev.preventDefault();
19687         
19688         
19689         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
19690         pane.tab.addClass('active');
19691         //Roo.log(pane.title);
19692         this.getChildContainer().select('.tab-pane',true).removeClass('active');
19693         // technically we should have a deactivate event.. but maybe add later.
19694         // and it should not de-activate the selected tab...
19695         
19696         pane.el.addClass('active');
19697         pane.fireEvent('activate');
19698         
19699         
19700     }
19701     
19702     
19703 });
19704
19705  
19706 /*
19707  * - LGPL
19708  *
19709  * Tab pane
19710  * 
19711  */
19712 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19713 /**
19714  * @class Roo.bootstrap.TabPane
19715  * @extends Roo.bootstrap.Component
19716  * Bootstrap TabPane class
19717  * @cfg {Boolean} active (false | true) Default false
19718  * @cfg {String} title title of panel
19719
19720  * 
19721  * @constructor
19722  * Create a new TabPane
19723  * @param {Object} config The config object
19724  */
19725
19726 Roo.bootstrap.dash.TabPane = function(config){
19727     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
19728     
19729 };
19730
19731 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
19732     
19733     active : false,
19734     title : '',
19735     
19736     // the tabBox that this is attached to.
19737     tab : false,
19738      
19739     getAutoCreate : function() 
19740     {
19741         var cfg = {
19742             tag: 'div',
19743             cls: 'tab-pane'
19744         }
19745         
19746         if(this.active){
19747             cfg.cls += ' active';
19748         }
19749         
19750         return cfg;
19751     },
19752     initEvents  : function()
19753     {
19754         //Roo.log('trigger add pane handler');
19755         this.parent().fireEvent('addpane', this)
19756     },
19757     
19758      /**
19759      * Updates the tab title 
19760      * @param {String} html to set the title to.
19761      */
19762     setTitle: function(str)
19763     {
19764         if (!this.tab) {
19765             return;
19766         }
19767         this.title = str;
19768         this.tab.select('a', true).first().dom.innerHTML = str;
19769         
19770     }
19771     
19772     
19773     
19774 });
19775
19776  
19777
19778
19779  /*
19780  * - LGPL
19781  *
19782  * menu
19783  * 
19784  */
19785 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
19786
19787 /**
19788  * @class Roo.bootstrap.menu.Menu
19789  * @extends Roo.bootstrap.Component
19790  * Bootstrap Menu class - container for Menu
19791  * @cfg {String} html Text of the menu
19792  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
19793  * @cfg {String} icon Font awesome icon
19794  * @cfg {String} pos Menu align to (top | bottom) default bottom
19795  * 
19796  * 
19797  * @constructor
19798  * Create a new Menu
19799  * @param {Object} config The config object
19800  */
19801
19802
19803 Roo.bootstrap.menu.Menu = function(config){
19804     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
19805     
19806     this.addEvents({
19807         /**
19808          * @event beforeshow
19809          * Fires before this menu is displayed
19810          * @param {Roo.bootstrap.menu.Menu} this
19811          */
19812         beforeshow : true,
19813         /**
19814          * @event beforehide
19815          * Fires before this menu is hidden
19816          * @param {Roo.bootstrap.menu.Menu} this
19817          */
19818         beforehide : true,
19819         /**
19820          * @event show
19821          * Fires after this menu is displayed
19822          * @param {Roo.bootstrap.menu.Menu} this
19823          */
19824         show : true,
19825         /**
19826          * @event hide
19827          * Fires after this menu is hidden
19828          * @param {Roo.bootstrap.menu.Menu} this
19829          */
19830         hide : true,
19831         /**
19832          * @event click
19833          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19834          * @param {Roo.bootstrap.menu.Menu} this
19835          * @param {Roo.EventObject} e
19836          */
19837         click : true
19838     });
19839     
19840 };
19841
19842 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
19843     
19844     submenu : false,
19845     html : '',
19846     weight : 'default',
19847     icon : false,
19848     pos : 'bottom',
19849     
19850     
19851     getChildContainer : function() {
19852         if(this.isSubMenu){
19853             return this.el;
19854         }
19855         
19856         return this.el.select('ul.dropdown-menu', true).first();  
19857     },
19858     
19859     getAutoCreate : function()
19860     {
19861         var text = [
19862             {
19863                 tag : 'span',
19864                 cls : 'roo-menu-text',
19865                 html : this.html
19866             }
19867         ];
19868         
19869         if(this.icon){
19870             text.unshift({
19871                 tag : 'i',
19872                 cls : 'fa ' + this.icon
19873             })
19874         }
19875         
19876         
19877         var cfg = {
19878             tag : 'div',
19879             cls : 'btn-group',
19880             cn : [
19881                 {
19882                     tag : 'button',
19883                     cls : 'dropdown-button btn btn-' + this.weight,
19884                     cn : text
19885                 },
19886                 {
19887                     tag : 'button',
19888                     cls : 'dropdown-toggle btn btn-' + this.weight,
19889                     cn : [
19890                         {
19891                             tag : 'span',
19892                             cls : 'caret'
19893                         }
19894                     ]
19895                 },
19896                 {
19897                     tag : 'ul',
19898                     cls : 'dropdown-menu'
19899                 }
19900             ]
19901             
19902         };
19903         
19904         if(this.pos == 'top'){
19905             cfg.cls += ' dropup';
19906         }
19907         
19908         if(this.isSubMenu){
19909             cfg = {
19910                 tag : 'ul',
19911                 cls : 'dropdown-menu'
19912             }
19913         }
19914         
19915         return cfg;
19916     },
19917     
19918     onRender : function(ct, position)
19919     {
19920         this.isSubMenu = ct.hasClass('dropdown-submenu');
19921         
19922         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
19923     },
19924     
19925     initEvents : function() 
19926     {
19927         if(this.isSubMenu){
19928             return;
19929         }
19930         
19931         this.hidden = true;
19932         
19933         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
19934         this.triggerEl.on('click', this.onTriggerPress, this);
19935         
19936         this.buttonEl = this.el.select('button.dropdown-button', true).first();
19937         this.buttonEl.on('click', this.onClick, this);
19938         
19939     },
19940     
19941     list : function()
19942     {
19943         if(this.isSubMenu){
19944             return this.el;
19945         }
19946         
19947         return this.el.select('ul.dropdown-menu', true).first();
19948     },
19949     
19950     onClick : function(e)
19951     {
19952         this.fireEvent("click", this, e);
19953     },
19954     
19955     onTriggerPress  : function(e)
19956     {   
19957         if (this.isVisible()) {
19958             this.hide();
19959         } else {
19960             this.show();
19961         }
19962     },
19963     
19964     isVisible : function(){
19965         return !this.hidden;
19966     },
19967     
19968     show : function()
19969     {
19970         this.fireEvent("beforeshow", this);
19971         
19972         this.hidden = false;
19973         this.el.addClass('open');
19974         
19975         Roo.get(document).on("mouseup", this.onMouseUp, this);
19976         
19977         this.fireEvent("show", this);
19978         
19979         
19980     },
19981     
19982     hide : function()
19983     {
19984         this.fireEvent("beforehide", this);
19985         
19986         this.hidden = true;
19987         this.el.removeClass('open');
19988         
19989         Roo.get(document).un("mouseup", this.onMouseUp);
19990         
19991         this.fireEvent("hide", this);
19992     },
19993     
19994     onMouseUp : function()
19995     {
19996         this.hide();
19997     }
19998     
19999 });
20000
20001  
20002  /*
20003  * - LGPL
20004  *
20005  * menu item
20006  * 
20007  */
20008 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20009
20010 /**
20011  * @class Roo.bootstrap.menu.Item
20012  * @extends Roo.bootstrap.Component
20013  * Bootstrap MenuItem class
20014  * @cfg {Boolean} submenu (true | false) default false
20015  * @cfg {String} html text of the item
20016  * @cfg {String} href the link
20017  * @cfg {Boolean} disable (true | false) default false
20018  * @cfg {Boolean} preventDefault (true | false) default true
20019  * @cfg {String} icon Font awesome icon
20020  * @cfg {String} pos Submenu align to (left | right) default right 
20021  * 
20022  * 
20023  * @constructor
20024  * Create a new Item
20025  * @param {Object} config The config object
20026  */
20027
20028
20029 Roo.bootstrap.menu.Item = function(config){
20030     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
20031     this.addEvents({
20032         /**
20033          * @event mouseover
20034          * Fires when the mouse is hovering over this menu
20035          * @param {Roo.bootstrap.menu.Item} this
20036          * @param {Roo.EventObject} e
20037          */
20038         mouseover : true,
20039         /**
20040          * @event mouseout
20041          * Fires when the mouse exits this menu
20042          * @param {Roo.bootstrap.menu.Item} this
20043          * @param {Roo.EventObject} e
20044          */
20045         mouseout : true,
20046         // raw events
20047         /**
20048          * @event click
20049          * The raw click event for the entire grid.
20050          * @param {Roo.EventObject} e
20051          */
20052         click : true
20053     });
20054 };
20055
20056 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
20057     
20058     submenu : false,
20059     href : '',
20060     html : '',
20061     preventDefault: true,
20062     disable : false,
20063     icon : false,
20064     pos : 'right',
20065     
20066     getAutoCreate : function()
20067     {
20068         var text = [
20069             {
20070                 tag : 'span',
20071                 cls : 'roo-menu-item-text',
20072                 html : this.html
20073             }
20074         ];
20075         
20076         if(this.icon){
20077             text.unshift({
20078                 tag : 'i',
20079                 cls : 'fa ' + this.icon
20080             })
20081         }
20082         
20083         var cfg = {
20084             tag : 'li',
20085             cn : [
20086                 {
20087                     tag : 'a',
20088                     href : this.href || '#',
20089                     cn : text
20090                 }
20091             ]
20092         };
20093         
20094         if(this.disable){
20095             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
20096         }
20097         
20098         if(this.submenu){
20099             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
20100             
20101             if(this.pos == 'left'){
20102                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
20103             }
20104         }
20105         
20106         return cfg;
20107     },
20108     
20109     initEvents : function() 
20110     {
20111         this.el.on('mouseover', this.onMouseOver, this);
20112         this.el.on('mouseout', this.onMouseOut, this);
20113         
20114         this.el.select('a', true).first().on('click', this.onClick, this);
20115         
20116     },
20117     
20118     onClick : function(e)
20119     {
20120         if(this.preventDefault){
20121             e.preventDefault();
20122         }
20123         
20124         this.fireEvent("click", this, e);
20125     },
20126     
20127     onMouseOver : function(e)
20128     {
20129         if(this.submenu && this.pos == 'left'){
20130             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
20131         }
20132         
20133         this.fireEvent("mouseover", this, e);
20134     },
20135     
20136     onMouseOut : function(e)
20137     {
20138         this.fireEvent("mouseout", this, e);
20139     }
20140 });
20141
20142  
20143
20144  /*
20145  * - LGPL
20146  *
20147  * menu separator
20148  * 
20149  */
20150 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20151
20152 /**
20153  * @class Roo.bootstrap.menu.Separator
20154  * @extends Roo.bootstrap.Component
20155  * Bootstrap Separator class
20156  * 
20157  * @constructor
20158  * Create a new Separator
20159  * @param {Object} config The config object
20160  */
20161
20162
20163 Roo.bootstrap.menu.Separator = function(config){
20164     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
20165 };
20166
20167 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
20168     
20169     getAutoCreate : function(){
20170         var cfg = {
20171             tag : 'li',
20172             cls: 'divider'
20173         };
20174         
20175         return cfg;
20176     }
20177    
20178 });
20179
20180  
20181
20182