docs/default.css
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * 
20  * @constructor
21  * Do not use directly - it does not do anything..
22  * @param {Object} config The config object
23  */
24
25
26
27 Roo.bootstrap.Component = function(config){
28     Roo.bootstrap.Component.superclass.constructor.call(this, config);
29 };
30
31 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
32     
33     
34     allowDomMove : false, // to stop relocations in parent onRender...
35     
36     cls : false,
37     
38     style : false,
39     
40     autoCreate : false,
41     
42     initEvents : function() {  },
43     
44     xattr : false,
45     
46     parentId : false,
47     
48     can_build_overlaid : true,
49     
50     dataId : false,
51     
52     name : false,
53     
54     parent: function() {
55         // returns the parent component..
56         return Roo.ComponentMgr.get(this.parentId)
57         
58         
59     },
60     
61     // private
62     onRender : function(ct, position)
63     {
64        // Roo.log("Call onRender: " + this.xtype);
65         
66         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
67         
68         if(this.el){
69             if (this.el.attr('xtype')) {
70                 this.el.attr('xtypex', this.el.attr('xtype'));
71                 this.el.dom.removeAttribute('xtype');
72                 
73                 this.initEvents();
74             }
75             
76             return;
77         }
78         
79          
80         
81         var cfg = Roo.apply({},  this.getAutoCreate());
82         cfg.id = Roo.id();
83         
84         // fill in the extra attributes 
85         if (this.xattr && typeof(this.xattr) =='object') {
86             for (var i in this.xattr) {
87                 cfg[i] = this.xattr[i];
88             }
89         }
90         
91         if(this.dataId){
92             cfg.dataId = this.dataId;
93         }
94         
95         if (this.cls) {
96             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
97         }
98         
99         if (this.style) { // fixme needs to support more complex style data.
100             cfg.style = this.style;
101         }
102         
103         if(this.name){
104             cfg.name = this.name;
105         }
106         
107         this.el = ct.createChild(cfg, position);
108         
109         if(this.tabIndex !== undefined){
110             this.el.dom.setAttribute('tabIndex', this.tabIndex);
111         }
112         this.initEvents();
113         
114         
115     },
116     
117     getChildContainer : function()
118     {
119         return this.el;
120     },
121     
122     
123     addxtype  : function(tree,cntr)
124     {
125         var cn = this;
126         
127         cn = Roo.factory(tree);
128            
129         cn.parentType = this.xtype; //??
130         cn.parentId = this.id;
131         
132         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
133         
134         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
135         
136         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
137         
138         var build_from_html =  Roo.XComponent.build_from_html;
139           
140         var is_body  = (tree.xtype == 'Body') ;
141           
142         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
143           
144         var self_cntr_el = Roo.get(this[cntr](false));
145         
146         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
147             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
148                 return this.addxtypeChild(tree,cntr);
149             }
150             
151             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
152                 
153             if(echild){
154                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
155             }
156             
157             Roo.log('skipping render');
158             return cn;
159             
160         }
161         
162         var ret = false;
163         
164         while (true) {
165             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
166             
167             if (!echild) {
168                 break;
169             }
170             
171             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
172                 break;
173             }
174             
175             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
176         }
177         return ret;
178     },
179     
180     addxtypeChild : function (tree, cntr)
181     {
182         Roo.log('addxtypeChild:' + cntr);
183         var cn = this;
184         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
185         
186         
187         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
188                     (typeof(tree['flexy:foreach']) != 'undefined');
189           
190         
191         
192         
193         // render the element if it's not BODY.
194         if (tree.xtype != 'Body') {
195            
196             cn = Roo.factory(tree);
197            
198             cn.parentType = this.xtype; //??
199             cn.parentId = this.id;
200             
201             var build_from_html =  Roo.XComponent.build_from_html;
202             
203             
204             // does the container contain child eleemnts with 'xtype' attributes.
205             // that match this xtype..
206             // note - when we render we create these as well..
207             // so we should check to see if body has xtype set.
208             if (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
209                
210                 var self_cntr_el = Roo.get(this[cntr](false));
211                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
212                 
213                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
214                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
215                   
216                   
217                   
218                     cn.el = echild;
219                   //  Roo.log("GOT");
220                     //echild.dom.removeAttribute('xtype');
221                 } else {
222                     Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
223                    
224                 }
225             }
226            
227             
228                
229             // if object has flexy:if - then it may or may not be rendered.
230             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
231                 // skip a flexy if element.
232                 Roo.log('skipping render');
233              } else {
234                  
235                 // actually if flexy:foreach is found, we really want to create 
236                 // multiple copies here...
237                 //Roo.log('render');
238                 //Roo.log(this[cntr]());
239                 cn.render(this[cntr](true));
240              }
241             // then add the element..
242         }
243         
244         
245         // handle the kids..
246         
247         var nitems = [];
248         /*
249         if (typeof (tree.menu) != 'undefined') {
250             tree.menu.parentType = cn.xtype;
251             tree.menu.triggerEl = cn.el;
252             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
253             
254         }
255         */
256         if (!tree.items || !tree.items.length) {
257             cn.items = nitems;
258             return cn;
259         }
260         var items = tree.items;
261         delete tree.items;
262         
263         //Roo.log(items.length);
264             // add the items..
265         for(var i =0;i < items.length;i++) {
266             nitems.push(cn.addxtype(Roo.apply({}, items[i])));
267         }
268         
269         cn.items = nitems;
270         
271         return cn;
272     }
273     
274     
275     
276     
277 });
278
279  /*
280  * - LGPL
281  *
282  * Body
283  * 
284  */
285
286 /**
287  * @class Roo.bootstrap.Body
288  * @extends Roo.bootstrap.Component
289  * Bootstrap Body class
290  * 
291  * @constructor
292  * Create a new body
293  * @param {Object} config The config object
294  */
295
296 Roo.bootstrap.Body = function(config){
297     Roo.bootstrap.Body.superclass.constructor.call(this, config);
298     this.el = Roo.get(document.body);
299     if (this.cls && this.cls.length) {
300         Roo.get(document.body).addClass(this.cls);
301     }
302 };
303
304 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
305       
306         autoCreate : {
307         cls: 'container'
308     },
309     onRender : function(ct, position)
310     {
311        /* Roo.log("Roo.bootstrap.Body - onRender");
312         if (this.cls && this.cls.length) {
313             Roo.get(document.body).addClass(this.cls);
314         }
315         // style??? xttr???
316         */
317     }
318     
319     
320  
321    
322 });
323
324  /*
325  * - LGPL
326  *
327  * button group
328  * 
329  */
330
331
332 /**
333  * @class Roo.bootstrap.ButtonGroup
334  * @extends Roo.bootstrap.Component
335  * Bootstrap ButtonGroup class
336  * @cfg {String} size lg | sm | xs (default empty normal)
337  * @cfg {String} align vertical | justified  (default none)
338  * @cfg {String} direction up | down (default down)
339  * @cfg {Boolean} toolbar false | true
340  * @cfg {Boolean} btn true | false
341  * 
342  * 
343  * @constructor
344  * Create a new Input
345  * @param {Object} config The config object
346  */
347
348 Roo.bootstrap.ButtonGroup = function(config){
349     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
350 };
351
352 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
353     
354     size: '',
355     align: '',
356     direction: '',
357     toolbar: false,
358     btn: true,
359
360     getAutoCreate : function(){
361         var cfg = {
362             cls: 'btn-group',
363             html : null
364         }
365         
366         cfg.html = this.html || cfg.html;
367         
368         if (this.toolbar) {
369             cfg = {
370                 cls: 'btn-toolbar',
371                 html: null
372             }
373             
374             return cfg;
375         }
376         
377         if (['vertical','justified'].indexOf(this.align)!==-1) {
378             cfg.cls = 'btn-group-' + this.align;
379             
380             if (this.align == 'justified') {
381                 console.log(this.items);
382             }
383         }
384         
385         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
386             cfg.cls += ' btn-group-' + this.size;
387         }
388         
389         if (this.direction == 'up') {
390             cfg.cls += ' dropup' ;
391         }
392         
393         return cfg;
394     }
395    
396 });
397
398  /*
399  * - LGPL
400  *
401  * button
402  * 
403  */
404
405 /**
406  * @class Roo.bootstrap.Button
407  * @extends Roo.bootstrap.Component
408  * Bootstrap Button class
409  * @cfg {String} html The button content
410  * @cfg {String} weight default (or empty) | primary | success | info | warning | danger | link
411  * @cfg {String} size empty | lg | sm | xs
412  * @cfg {String} tag empty | a | input | submit
413  * @cfg {String} href empty or href
414  * @cfg {Boolean} disabled false | true
415  * @cfg {Boolean} isClose false | true
416  * @cfg {String} glyphicon empty | adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out
417  * @cfg {String} badge text for badge
418  * @cfg {String} theme default (or empty) | glow
419  * @cfg {Boolean} inverse false | true
420  * @cfg {Boolean} toggle false | true
421  * @cfg {String} ontext text for on toggle state
422  * @cfg {String} offtext text for off toggle state
423  * @cfg {Boolean} defaulton true | false
424  * @cfg {Boolean} preventDefault (true | false) default true
425  * @cfg {Boolean} removeClass true | false remove the standard class..
426  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
427  * 
428  * @constructor
429  * Create a new button
430  * @param {Object} config The config object
431  */
432
433
434 Roo.bootstrap.Button = function(config){
435     Roo.bootstrap.Button.superclass.constructor.call(this, config);
436     this.addEvents({
437         // raw events
438         /**
439          * @event click
440          * When a butotn is pressed
441          * @param {Roo.EventObject} e
442          */
443         "click" : true,
444          /**
445          * @event toggle
446          * After the button has been toggles
447          * @param {Roo.EventObject} e
448          * @param {boolean} pressed (also available as button.pressed)
449          */
450         "toggle" : true
451     });
452 };
453
454 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
455     html: false,
456     active: false,
457     weight: '',
458     size: '',
459     tag: 'button',
460     href: '',
461     disabled: false,
462     isClose: false,
463     glyphicon: '',
464     badge: '',
465     theme: 'default',
466     inverse: false,
467     
468     toggle: false,
469     ontext: 'ON',
470     offtext: 'OFF',
471     defaulton: true,
472     preventDefault: true,
473     removeClass: false,
474     name: false,
475     target: false,
476     
477     
478     pressed : null,
479      
480     
481     getAutoCreate : function(){
482         
483         var cfg = {
484             tag : 'button',
485             cls : 'roo-button',
486             html: ''
487         };
488         
489         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
490             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
491             this.tag = 'button';
492         } else {
493             cfg.tag = this.tag;
494         }
495         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
496         
497         if (this.toggle == true) {
498             cfg={
499                 tag: 'div',
500                 cls: 'slider-frame roo-button',
501                 cn: [
502                     {
503                         tag: 'span',
504                         'data-on-text':'ON',
505                         'data-off-text':'OFF',
506                         cls: 'slider-button',
507                         html: this.offtext
508                     }
509                 ]
510             };
511             
512             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
513                 cfg.cls += ' '+this.weight;
514             }
515             
516             return cfg;
517         }
518         
519         if (this.isClose) {
520             cfg.cls += ' close';
521             
522             cfg["aria-hidden"] = true;
523             
524             cfg.html = "&times;";
525             
526             return cfg;
527         }
528         
529          
530         if (this.theme==='default') {
531             cfg.cls = 'btn roo-button';
532             
533             //if (this.parentType != 'Navbar') {
534             this.weight = this.weight.length ?  this.weight : 'default';
535             //}
536             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
537                 
538                 cfg.cls += ' btn-' + this.weight;
539             }
540         } else if (this.theme==='glow') {
541             
542             cfg.tag = 'a';
543             cfg.cls = 'btn-glow roo-button';
544             
545             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
546                 
547                 cfg.cls += ' ' + this.weight;
548             }
549         }
550    
551         
552         if (this.inverse) {
553             this.cls += ' inverse';
554         }
555         
556         
557         if (this.active) {
558             cfg.cls += ' active';
559         }
560         
561         if (this.disabled) {
562             cfg.disabled = 'disabled';
563         }
564         
565         if (this.items) {
566             Roo.log('changing to ul' );
567             cfg.tag = 'ul';
568             this.glyphicon = 'caret';
569         }
570         
571         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
572          
573         //gsRoo.log(this.parentType);
574         if (this.parentType === 'Navbar' && !this.parent().bar) {
575             Roo.log('changing to li?');
576             
577             cfg.tag = 'li';
578             
579             cfg.cls = '';
580             cfg.cn =  [{
581                 tag : 'a',
582                 cls : 'roo-button',
583                 html : this.html,
584                 href : this.href || '#'
585             }];
586             if (this.menu) {
587                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
588                 cfg.cls += ' dropdown';
589             }   
590             
591             delete cfg.html;
592             
593         }
594         
595        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
596         
597         if (this.glyphicon) {
598             cfg.html = ' ' + cfg.html;
599             
600             cfg.cn = [
601                 {
602                     tag: 'span',
603                     cls: 'glyphicon glyphicon-' + this.glyphicon
604                 }
605             ];
606         }
607         
608         if (this.badge) {
609             cfg.html += ' ';
610             
611             cfg.tag = 'a';
612             
613 //            cfg.cls='btn roo-button';
614             
615             cfg.href=this.href;
616             
617             var value = cfg.html;
618             
619             if(this.glyphicon){
620                 value = {
621                             tag: 'span',
622                             cls: 'glyphicon glyphicon-' + this.glyphicon,
623                             html: this.html
624                         };
625                 
626             }
627             
628             cfg.cn = [
629                 value,
630                 {
631                     tag: 'span',
632                     cls: 'badge',
633                     html: this.badge
634                 }
635             ];
636             
637             cfg.html='';
638         }
639         
640         if (this.menu) {
641             cfg.cls += ' dropdown';
642             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
643         }
644         
645         if (cfg.tag !== 'a' && this.href !== '') {
646             throw "Tag must be a to set href.";
647         } else if (this.href.length > 0) {
648             cfg.href = this.href;
649         }
650         
651         if(this.removeClass){
652             cfg.cls = '';
653         }
654         
655         if(this.target){
656             cfg.target = this.target;
657         }
658         
659         return cfg;
660     },
661     initEvents: function() {
662        // Roo.log('init events?');
663 //        Roo.log(this.el.dom);
664         // add the menu...
665         
666         if (typeof (this.menu) != 'undefined') {
667             this.menu.parentType = this.xtype;
668             this.menu.triggerEl = this.el;
669             this.addxtype(Roo.apply({}, this.menu));
670         }
671
672
673        if (this.el.hasClass('roo-button')) {
674             this.el.on('click', this.onClick, this);
675        } else {
676             this.el.select('.roo-button').on('click', this.onClick, this);
677        }
678        
679        if(this.removeClass){
680            this.el.on('click', this.onClick, this);
681        }
682        
683        this.el.enableDisplayMode();
684         
685     },
686     onClick : function(e)
687     {
688         if (this.disabled) {
689             return;
690         }
691         
692         Roo.log('button on click ');
693         if(this.preventDefault){
694             e.preventDefault();
695         }
696         if (this.pressed === true || this.pressed === false) {
697             this.pressed = !this.pressed;
698             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
699             this.fireEvent('toggle', this, e, this.pressed);
700         }
701         
702         
703         this.fireEvent('click', this, e);
704     },
705     
706     /**
707      * Enables this button
708      */
709     enable : function()
710     {
711         this.disabled = false;
712         this.el.removeClass('disabled');
713     },
714     
715     /**
716      * Disable this button
717      */
718     disable : function()
719     {
720         this.disabled = true;
721         this.el.addClass('disabled');
722     },
723      /**
724      * sets the active state on/off, 
725      * @param {Boolean} state (optional) Force a particular state
726      */
727     setActive : function(v) {
728         
729         this.el[v ? 'addClass' : 'removeClass']('active');
730     },
731      /**
732      * toggles the current active state 
733      */
734     toggleActive : function()
735     {
736        var active = this.el.hasClass('active');
737        this.setActive(!active);
738        
739         
740     },
741     setText : function(str)
742     {
743         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
744     },
745     getText : function()
746     {
747         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
748     },
749     hide: function() {
750        
751      
752         this.el.hide();   
753     },
754     show: function() {
755        
756         this.el.show();   
757     }
758     
759     
760 });
761
762  /*
763  * - LGPL
764  *
765  * column
766  * 
767  */
768
769 /**
770  * @class Roo.bootstrap.Column
771  * @extends Roo.bootstrap.Component
772  * Bootstrap Column class
773  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
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){
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     Roo.bootstrap.NavGroup.register(this);
3181      this.addEvents({
3182         /**
3183              * @event changed
3184              * Fires when the active item changes
3185              * @param {Roo.bootstrap.NavGroup} this
3186              * @param {Roo.bootstrap.Navbar.Item} item The item selected
3187              * @param {Roo.bootstrap.Navbar.Item} item The previously selected item 
3188          */
3189         'changed': true
3190      });
3191     
3192 };
3193
3194 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3195     
3196     align: '',
3197     inverse: false,
3198     form: false,
3199     type: 'nav',
3200     navId : '',
3201     // private
3202     
3203     navItems : false,
3204     
3205     getAutoCreate : function()
3206     {
3207         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3208         
3209         cfg = {
3210             tag : 'ul',
3211             cls: 'nav' 
3212         }
3213         
3214         if (['tabs','pills'].indexOf(this.type)!==-1) {
3215             cfg.cls += ' nav-' + this.type
3216         } else {
3217             if (this.type!=='nav') {
3218                 Roo.log('nav type must be nav/tabs/pills')
3219             }
3220             cfg.cls += ' navbar-nav'
3221         }
3222         
3223         if (this.parent().sidebar) {
3224             cfg = {
3225                 tag: 'ul',
3226                 cls: 'dashboard-menu sidebar-menu'
3227             }
3228             
3229             return cfg;
3230         }
3231         
3232         if (this.form === true) {
3233             cfg = {
3234                 tag: 'form',
3235                 cls: 'navbar-form'
3236             }
3237             
3238             if (this.align === 'right') {
3239                 cfg.cls += ' navbar-right';
3240             } else {
3241                 cfg.cls += ' navbar-left';
3242             }
3243         }
3244         
3245         if (this.align === 'right') {
3246             cfg.cls += ' navbar-right';
3247         }
3248         
3249         if (this.inverse) {
3250             cfg.cls += ' navbar-inverse';
3251             
3252         }
3253         
3254         
3255         return cfg;
3256     },
3257     
3258     setActiveItem : function(item)
3259     {
3260         var prev = false;
3261         Roo.each(this.navItems, function(v){
3262             if (v == item) {
3263                 return ;
3264             }
3265             if (v.isActive()) {
3266                 v.setActive(false, true);
3267                 prev = v;
3268                 
3269             }
3270             
3271         });
3272
3273         item.setActive(true, true);
3274         this.fireEvent('changed', this, item, prev);
3275         
3276         
3277     },
3278     
3279     addItem : function(cfg)
3280     {
3281         var cn = new Roo.bootstrap.NavItem(cfg);
3282         this.register(cn);
3283         cn.parentId = this.id;
3284         cn.onRender(this.el, null);
3285         return cn;
3286     },
3287     
3288     register : function(item)
3289     {
3290         this.navItems.push( item);
3291         item.navId = this.navId;
3292     
3293     },
3294     getNavItem: function(tabId)
3295     {
3296         var ret = false;
3297         Roo.each(this.navItems, function(e) {
3298             if (e.tabId == tabId) {
3299                ret =  e;
3300                return false;
3301             }
3302             return true;
3303             
3304         });
3305         return ret;
3306     }
3307     
3308     
3309     
3310     
3311 });
3312
3313  
3314 Roo.apply(Roo.bootstrap.NavGroup, {
3315     
3316     groups: {},
3317     
3318     register : function(navgrp)
3319     {
3320         this.groups[navgrp.navId] = navgrp;
3321         
3322     },
3323     get: function(navId) {
3324         return this.groups[navId];
3325     }
3326     
3327     
3328     
3329 });
3330
3331  /*
3332  * - LGPL
3333  *
3334  * row
3335  * 
3336  */
3337
3338 /**
3339  * @class Roo.bootstrap.NavItem
3340  * @extends Roo.bootstrap.Component
3341  * Bootstrap Navbar.NavItem class
3342  * @cfg {String} href  link to
3343  * @cfg {String} html content of button
3344  * @cfg {String} badge text inside badge
3345  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
3346  * @cfg {String} glyphicon name of glyphicon
3347  * @cfg {String} icon name of font awesome icon
3348  * @cfg {Boolean} active Is item active
3349  * @cfg {Boolean} disabled Is item disabled
3350  
3351  * @cfg {Boolean} preventDefault (true | false) default false
3352  * @cfg {String} tabId the tab that this item activates.
3353  * @cfg {String} tagtype (a|span) render as a href or span?
3354   
3355  * @constructor
3356  * Create a new Navbar Item
3357  * @param {Object} config The config object
3358  */
3359 Roo.bootstrap.NavItem = function(config){
3360     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
3361     this.addEvents({
3362         // raw events
3363         /**
3364          * @event click
3365          * The raw click event for the entire grid.
3366          * @param {Roo.EventObject} e
3367          */
3368         "click" : true,
3369          /**
3370             * @event changed
3371             * Fires when the active item active state changes
3372             * @param {Roo.bootstrap.NavItem} this
3373             * @param {boolean} state the new state
3374              
3375          */
3376         'changed': true
3377     });
3378    
3379 };
3380
3381 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
3382     
3383     href: false,
3384     html: '',
3385     badge: '',
3386     icon: false,
3387     glyphicon: false,
3388     active: false,
3389     preventDefault : false,
3390     tabId : false,
3391     tagtype : 'a',
3392     disabled : false,
3393     
3394     getAutoCreate : function(){
3395          
3396         var cfg = {
3397             tag: 'li',
3398             cls: 'nav-item'
3399             
3400         }
3401         if (this.active) {
3402             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
3403         }
3404         if (this.disabled) {
3405             cfg.cls += ' disabled';
3406         }
3407         
3408         if (this.href || this.html || this.glyphicon || this.icon) {
3409             cfg.cn = [
3410                 {
3411                     tag: this.tagtype,
3412                     href : this.href || "#",
3413                     html: this.html || ''
3414                 }
3415             ];
3416             
3417             if (this.icon) {
3418                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
3419             }
3420
3421             if(this.glyphicon) {
3422                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
3423             }
3424             
3425             if (this.menu) {
3426                 
3427                 cfg.cn[0].html += " <span class='caret'></span>";
3428              
3429             }
3430             
3431             if (this.badge !== '') {
3432                  
3433                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
3434             }
3435         }
3436         
3437         
3438         
3439         return cfg;
3440     },
3441     initEvents: function() {
3442        // Roo.log('init events?');
3443        // Roo.log(this.el.dom);
3444         if (typeof (this.menu) != 'undefined') {
3445             this.menu.parentType = this.xtype;
3446             this.menu.triggerEl = this.el;
3447             this.addxtype(Roo.apply({}, this.menu));
3448         }
3449
3450        
3451         this.el.select('a',true).on('click', this.onClick, this);
3452         // at this point parent should be available..
3453         this.parent().register(this);
3454     },
3455     
3456     onClick : function(e)
3457     {
3458          
3459         if(this.preventDefault){
3460             e.preventDefault();
3461         }
3462         if (this.disabled) {
3463             return;
3464         }
3465         Roo.log("fire event clicked");
3466         if(this.fireEvent('click', this, e) === false){
3467             return;
3468         };
3469         
3470         if (['tabs','pills'].indexOf(this.parent().type)!==-1) {
3471             if (typeof(this.parent().setActiveItem) !== 'undefined') {
3472                 this.parent().setActiveItem(this);
3473             }
3474             
3475             
3476             
3477         } 
3478     },
3479     
3480     isActive: function () {
3481         return this.active
3482     },
3483     setActive : function(state, fire)
3484     {
3485         this.active = state;
3486         if (!state ) {
3487             this.el.removeClass('active');
3488         } else if (!this.el.hasClass('active')) {
3489             this.el.addClass('active');
3490         }
3491         if (fire) {
3492             this.fireEvent('changed', this, state);
3493         }
3494         
3495         
3496     },
3497      // this should not be here...
3498     setDisabled : function(state)
3499     {
3500         this.disabled = state;
3501         if (!state ) {
3502             this.el.removeClass('disabled');
3503         } else if (!this.el.hasClass('disabled')) {
3504             this.el.addClass('disabled');
3505         }
3506         
3507     }
3508 });
3509  
3510
3511  /*
3512  * - LGPL
3513  *
3514  * sidebar item
3515  *
3516  *  li
3517  *    <span> icon </span>
3518  *    <span> text </span>
3519  *    <span>badge </span>
3520  */
3521
3522 /**
3523  * @class Roo.bootstrap.NavSidebarItem
3524  * @extends Roo.bootstrap.NavItem
3525  * Bootstrap Navbar.NavSidebarItem class
3526  * @constructor
3527  * Create a new Navbar Button
3528  * @param {Object} config The config object
3529  */
3530 Roo.bootstrap.NavSidebarItem = function(config){
3531     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
3532     this.addEvents({
3533         // raw events
3534         /**
3535          * @event click
3536          * The raw click event for the entire grid.
3537          * @param {Roo.EventObject} e
3538          */
3539         "click" : true,
3540          /**
3541             * @event changed
3542             * Fires when the active item active state changes
3543             * @param {Roo.bootstrap.NavSidebarItem} this
3544             * @param {boolean} state the new state
3545              
3546          */
3547         'changed': true
3548     });
3549    
3550 };
3551
3552 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
3553     
3554     
3555     getAutoCreate : function(){
3556         
3557         
3558         var a = {
3559                 tag: 'a',
3560                 href : this.href || '#',
3561                 cls: '',
3562                 html : '',
3563                 cn : []
3564         };
3565         var cfg = {
3566             tag: 'li',
3567             cls: '',
3568             cn: [ a ]
3569         }
3570         var span = {
3571             tag: 'span',
3572             html : this.html || ''
3573         }
3574         
3575         
3576         if (this.active) {
3577             cfg.cls += ' active';
3578         }
3579         
3580         // left icon..
3581         if (this.glyphicon || this.icon) {
3582             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
3583             a.cn.push({ tag : 'i', cls : c }) ;
3584         }
3585         // html..
3586         a.cn.push(span);
3587         // then badge..
3588         if (this.badge !== '') {
3589             a.cn.push({ tag: 'span',  cls : 'badge pull-right ' + (this.badgecls || ''), html: this.badge }); 
3590         }
3591         // fi
3592         if (this.menu) {
3593             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
3594             a.cls += 'dropdown-toggle treeview' ;
3595             
3596         }
3597         
3598         
3599         
3600         return cfg;
3601          
3602            
3603     }
3604    
3605      
3606  
3607 });
3608  
3609
3610  /*
3611  * - LGPL
3612  *
3613  * row
3614  * 
3615  */
3616
3617 /**
3618  * @class Roo.bootstrap.Row
3619  * @extends Roo.bootstrap.Component
3620  * Bootstrap Row class (contains columns...)
3621  * 
3622  * @constructor
3623  * Create a new Row
3624  * @param {Object} config The config object
3625  */
3626
3627 Roo.bootstrap.Row = function(config){
3628     Roo.bootstrap.Row.superclass.constructor.call(this, config);
3629 };
3630
3631 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
3632     
3633     getAutoCreate : function(){
3634        return {
3635             cls: 'row clearfix'
3636        };
3637     }
3638     
3639     
3640 });
3641
3642  
3643
3644  /*
3645  * - LGPL
3646  *
3647  * element
3648  * 
3649  */
3650
3651 /**
3652  * @class Roo.bootstrap.Element
3653  * @extends Roo.bootstrap.Component
3654  * Bootstrap Element class
3655  * @cfg {String} html contents of the element
3656  * @cfg {String} tag tag of the element
3657  * @cfg {String} cls class of the element
3658  * 
3659  * @constructor
3660  * Create a new Element
3661  * @param {Object} config The config object
3662  */
3663
3664 Roo.bootstrap.Element = function(config){
3665     Roo.bootstrap.Element.superclass.constructor.call(this, config);
3666 };
3667
3668 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
3669     
3670     tag: 'div',
3671     cls: '',
3672     html: '',
3673      
3674     
3675     getAutoCreate : function(){
3676         
3677         var cfg = {
3678             tag: this.tag,
3679             cls: this.cls,
3680             html: this.html
3681         }
3682         
3683         
3684         
3685         return cfg;
3686     }
3687    
3688 });
3689
3690  
3691
3692  /*
3693  * - LGPL
3694  *
3695  * pagination
3696  * 
3697  */
3698
3699 /**
3700  * @class Roo.bootstrap.Pagination
3701  * @extends Roo.bootstrap.Component
3702  * Bootstrap Pagination class
3703  * @cfg {String} size xs | sm | md | lg
3704  * @cfg {Boolean} inverse false | true
3705  * 
3706  * @constructor
3707  * Create a new Pagination
3708  * @param {Object} config The config object
3709  */
3710
3711 Roo.bootstrap.Pagination = function(config){
3712     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
3713 };
3714
3715 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
3716     
3717     cls: false,
3718     size: false,
3719     inverse: false,
3720     
3721     getAutoCreate : function(){
3722         var cfg = {
3723             tag: 'ul',
3724                 cls: 'pagination'
3725         };
3726         if (this.inverse) {
3727             cfg.cls += ' inverse';
3728         }
3729         if (this.html) {
3730             cfg.html=this.html;
3731         }
3732         if (this.cls) {
3733             cfg.cls += " " + this.cls;
3734         }
3735         return cfg;
3736     }
3737    
3738 });
3739
3740  
3741
3742  /*
3743  * - LGPL
3744  *
3745  * Pagination item
3746  * 
3747  */
3748
3749
3750 /**
3751  * @class Roo.bootstrap.PaginationItem
3752  * @extends Roo.bootstrap.Component
3753  * Bootstrap PaginationItem class
3754  * @cfg {String} html text
3755  * @cfg {String} href the link
3756  * @cfg {Boolean} preventDefault (true | false) default true
3757  * @cfg {Boolean} active (true | false) default false
3758  * 
3759  * 
3760  * @constructor
3761  * Create a new PaginationItem
3762  * @param {Object} config The config object
3763  */
3764
3765
3766 Roo.bootstrap.PaginationItem = function(config){
3767     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
3768     this.addEvents({
3769         // raw events
3770         /**
3771          * @event click
3772          * The raw click event for the entire grid.
3773          * @param {Roo.EventObject} e
3774          */
3775         "click" : true
3776     });
3777 };
3778
3779 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
3780     
3781     href : false,
3782     html : false,
3783     preventDefault: true,
3784     active : false,
3785     cls : false,
3786     
3787     getAutoCreate : function(){
3788         var cfg= {
3789             tag: 'li',
3790             cn: [
3791                 {
3792                     tag : 'a',
3793                     href : this.href ? this.href : '#',
3794                     html : this.html ? this.html : ''
3795                 }
3796             ]
3797         };
3798         
3799         if(this.cls){
3800             cfg.cls = this.cls;
3801         }
3802         
3803         if(this.active){
3804             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
3805         }
3806         
3807         return cfg;
3808     },
3809     
3810     initEvents: function() {
3811         
3812         this.el.on('click', this.onClick, this);
3813         
3814     },
3815     onClick : function(e)
3816     {
3817         Roo.log('PaginationItem on click ');
3818         if(this.preventDefault){
3819             e.preventDefault();
3820         }
3821         
3822         this.fireEvent('click', this, e);
3823     }
3824    
3825 });
3826
3827  
3828
3829  /*
3830  * - LGPL
3831  *
3832  * slider
3833  * 
3834  */
3835
3836
3837 /**
3838  * @class Roo.bootstrap.Slider
3839  * @extends Roo.bootstrap.Component
3840  * Bootstrap Slider class
3841  *    
3842  * @constructor
3843  * Create a new Slider
3844  * @param {Object} config The config object
3845  */
3846
3847 Roo.bootstrap.Slider = function(config){
3848     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
3849 };
3850
3851 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
3852     
3853     getAutoCreate : function(){
3854         
3855         var cfg = {
3856             tag: 'div',
3857             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
3858             cn: [
3859                 {
3860                     tag: 'a',
3861                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
3862                 }
3863             ]
3864         }
3865         
3866         return cfg;
3867     }
3868    
3869 });
3870
3871  /*
3872  * Based on:
3873  * Ext JS Library 1.1.1
3874  * Copyright(c) 2006-2007, Ext JS, LLC.
3875  *
3876  * Originally Released Under LGPL - original licence link has changed is not relivant.
3877  *
3878  * Fork - LGPL
3879  * <script type="text/javascript">
3880  */
3881  
3882
3883 /**
3884  * @class Roo.grid.ColumnModel
3885  * @extends Roo.util.Observable
3886  * This is the default implementation of a ColumnModel used by the Grid. It defines
3887  * the columns in the grid.
3888  * <br>Usage:<br>
3889  <pre><code>
3890  var colModel = new Roo.grid.ColumnModel([
3891         {header: "Ticker", width: 60, sortable: true, locked: true},
3892         {header: "Company Name", width: 150, sortable: true},
3893         {header: "Market Cap.", width: 100, sortable: true},
3894         {header: "$ Sales", width: 100, sortable: true, renderer: money},
3895         {header: "Employees", width: 100, sortable: true, resizable: false}
3896  ]);
3897  </code></pre>
3898  * <p>
3899  
3900  * The config options listed for this class are options which may appear in each
3901  * individual column definition.
3902  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
3903  * @constructor
3904  * @param {Object} config An Array of column config objects. See this class's
3905  * config objects for details.
3906 */
3907 Roo.grid.ColumnModel = function(config){
3908         /**
3909      * The config passed into the constructor
3910      */
3911     this.config = config;
3912     this.lookup = {};
3913
3914     // if no id, create one
3915     // if the column does not have a dataIndex mapping,
3916     // map it to the order it is in the config
3917     for(var i = 0, len = config.length; i < len; i++){
3918         var c = config[i];
3919         if(typeof c.dataIndex == "undefined"){
3920             c.dataIndex = i;
3921         }
3922         if(typeof c.renderer == "string"){
3923             c.renderer = Roo.util.Format[c.renderer];
3924         }
3925         if(typeof c.id == "undefined"){
3926             c.id = Roo.id();
3927         }
3928         if(c.editor && c.editor.xtype){
3929             c.editor  = Roo.factory(c.editor, Roo.grid);
3930         }
3931         if(c.editor && c.editor.isFormField){
3932             c.editor = new Roo.grid.GridEditor(c.editor);
3933         }
3934         this.lookup[c.id] = c;
3935     }
3936
3937     /**
3938      * The width of columns which have no width specified (defaults to 100)
3939      * @type Number
3940      */
3941     this.defaultWidth = 100;
3942
3943     /**
3944      * Default sortable of columns which have no sortable specified (defaults to false)
3945      * @type Boolean
3946      */
3947     this.defaultSortable = false;
3948
3949     this.addEvents({
3950         /**
3951              * @event widthchange
3952              * Fires when the width of a column changes.
3953              * @param {ColumnModel} this
3954              * @param {Number} columnIndex The column index
3955              * @param {Number} newWidth The new width
3956              */
3957             "widthchange": true,
3958         /**
3959              * @event headerchange
3960              * Fires when the text of a header changes.
3961              * @param {ColumnModel} this
3962              * @param {Number} columnIndex The column index
3963              * @param {Number} newText The new header text
3964              */
3965             "headerchange": true,
3966         /**
3967              * @event hiddenchange
3968              * Fires when a column is hidden or "unhidden".
3969              * @param {ColumnModel} this
3970              * @param {Number} columnIndex The column index
3971              * @param {Boolean} hidden true if hidden, false otherwise
3972              */
3973             "hiddenchange": true,
3974             /**
3975          * @event columnmoved
3976          * Fires when a column is moved.
3977          * @param {ColumnModel} this
3978          * @param {Number} oldIndex
3979          * @param {Number} newIndex
3980          */
3981         "columnmoved" : true,
3982         /**
3983          * @event columlockchange
3984          * Fires when a column's locked state is changed
3985          * @param {ColumnModel} this
3986          * @param {Number} colIndex
3987          * @param {Boolean} locked true if locked
3988          */
3989         "columnlockchange" : true
3990     });
3991     Roo.grid.ColumnModel.superclass.constructor.call(this);
3992 };
3993 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
3994     /**
3995      * @cfg {String} header The header text to display in the Grid view.
3996      */
3997     /**
3998      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
3999      * {@link Roo.data.Record} definition from which to draw the column's value. If not
4000      * specified, the column's index is used as an index into the Record's data Array.
4001      */
4002     /**
4003      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4004      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4005      */
4006     /**
4007      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4008      * Defaults to the value of the {@link #defaultSortable} property.
4009      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4010      */
4011     /**
4012      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
4013      */
4014     /**
4015      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
4016      */
4017     /**
4018      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4019      */
4020     /**
4021      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4022      */
4023     /**
4024      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4025      * given the cell's data value. See {@link #setRenderer}. If not specified, the
4026      * default renderer uses the raw data value.
4027      */
4028        /**
4029      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
4030      */
4031     /**
4032      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
4033      */
4034
4035     /**
4036      * Returns the id of the column at the specified index.
4037      * @param {Number} index The column index
4038      * @return {String} the id
4039      */
4040     getColumnId : function(index){
4041         return this.config[index].id;
4042     },
4043
4044     /**
4045      * Returns the column for a specified id.
4046      * @param {String} id The column id
4047      * @return {Object} the column
4048      */
4049     getColumnById : function(id){
4050         return this.lookup[id];
4051     },
4052
4053     
4054     /**
4055      * Returns the column for a specified dataIndex.
4056      * @param {String} dataIndex The column dataIndex
4057      * @return {Object|Boolean} the column or false if not found
4058      */
4059     getColumnByDataIndex: function(dataIndex){
4060         var index = this.findColumnIndex(dataIndex);
4061         return index > -1 ? this.config[index] : false;
4062     },
4063     
4064     /**
4065      * Returns the index for a specified column id.
4066      * @param {String} id The column id
4067      * @return {Number} the index, or -1 if not found
4068      */
4069     getIndexById : function(id){
4070         for(var i = 0, len = this.config.length; i < len; i++){
4071             if(this.config[i].id == id){
4072                 return i;
4073             }
4074         }
4075         return -1;
4076     },
4077     
4078     /**
4079      * Returns the index for a specified column dataIndex.
4080      * @param {String} dataIndex The column dataIndex
4081      * @return {Number} the index, or -1 if not found
4082      */
4083     
4084     findColumnIndex : function(dataIndex){
4085         for(var i = 0, len = this.config.length; i < len; i++){
4086             if(this.config[i].dataIndex == dataIndex){
4087                 return i;
4088             }
4089         }
4090         return -1;
4091     },
4092     
4093     
4094     moveColumn : function(oldIndex, newIndex){
4095         var c = this.config[oldIndex];
4096         this.config.splice(oldIndex, 1);
4097         this.config.splice(newIndex, 0, c);
4098         this.dataMap = null;
4099         this.fireEvent("columnmoved", this, oldIndex, newIndex);
4100     },
4101
4102     isLocked : function(colIndex){
4103         return this.config[colIndex].locked === true;
4104     },
4105
4106     setLocked : function(colIndex, value, suppressEvent){
4107         if(this.isLocked(colIndex) == value){
4108             return;
4109         }
4110         this.config[colIndex].locked = value;
4111         if(!suppressEvent){
4112             this.fireEvent("columnlockchange", this, colIndex, value);
4113         }
4114     },
4115
4116     getTotalLockedWidth : function(){
4117         var totalWidth = 0;
4118         for(var i = 0; i < this.config.length; i++){
4119             if(this.isLocked(i) && !this.isHidden(i)){
4120                 this.totalWidth += this.getColumnWidth(i);
4121             }
4122         }
4123         return totalWidth;
4124     },
4125
4126     getLockedCount : function(){
4127         for(var i = 0, len = this.config.length; i < len; i++){
4128             if(!this.isLocked(i)){
4129                 return i;
4130             }
4131         }
4132     },
4133
4134     /**
4135      * Returns the number of columns.
4136      * @return {Number}
4137      */
4138     getColumnCount : function(visibleOnly){
4139         if(visibleOnly === true){
4140             var c = 0;
4141             for(var i = 0, len = this.config.length; i < len; i++){
4142                 if(!this.isHidden(i)){
4143                     c++;
4144                 }
4145             }
4146             return c;
4147         }
4148         return this.config.length;
4149     },
4150
4151     /**
4152      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
4153      * @param {Function} fn
4154      * @param {Object} scope (optional)
4155      * @return {Array} result
4156      */
4157     getColumnsBy : function(fn, scope){
4158         var r = [];
4159         for(var i = 0, len = this.config.length; i < len; i++){
4160             var c = this.config[i];
4161             if(fn.call(scope||this, c, i) === true){
4162                 r[r.length] = c;
4163             }
4164         }
4165         return r;
4166     },
4167
4168     /**
4169      * Returns true if the specified column is sortable.
4170      * @param {Number} col The column index
4171      * @return {Boolean}
4172      */
4173     isSortable : function(col){
4174         if(typeof this.config[col].sortable == "undefined"){
4175             return this.defaultSortable;
4176         }
4177         return this.config[col].sortable;
4178     },
4179
4180     /**
4181      * Returns the rendering (formatting) function defined for the column.
4182      * @param {Number} col The column index.
4183      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
4184      */
4185     getRenderer : function(col){
4186         if(!this.config[col].renderer){
4187             return Roo.grid.ColumnModel.defaultRenderer;
4188         }
4189         return this.config[col].renderer;
4190     },
4191
4192     /**
4193      * Sets the rendering (formatting) function for a column.
4194      * @param {Number} col The column index
4195      * @param {Function} fn The function to use to process the cell's raw data
4196      * to return HTML markup for the grid view. The render function is called with
4197      * the following parameters:<ul>
4198      * <li>Data value.</li>
4199      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
4200      * <li>css A CSS style string to apply to the table cell.</li>
4201      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
4202      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
4203      * <li>Row index</li>
4204      * <li>Column index</li>
4205      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
4206      */
4207     setRenderer : function(col, fn){
4208         this.config[col].renderer = fn;
4209     },
4210
4211     /**
4212      * Returns the width for the specified column.
4213      * @param {Number} col The column index
4214      * @return {Number}
4215      */
4216     getColumnWidth : function(col){
4217         return this.config[col].width * 1 || this.defaultWidth;
4218     },
4219
4220     /**
4221      * Sets the width for a column.
4222      * @param {Number} col The column index
4223      * @param {Number} width The new width
4224      */
4225     setColumnWidth : function(col, width, suppressEvent){
4226         this.config[col].width = width;
4227         this.totalWidth = null;
4228         if(!suppressEvent){
4229              this.fireEvent("widthchange", this, col, width);
4230         }
4231     },
4232
4233     /**
4234      * Returns the total width of all columns.
4235      * @param {Boolean} includeHidden True to include hidden column widths
4236      * @return {Number}
4237      */
4238     getTotalWidth : function(includeHidden){
4239         if(!this.totalWidth){
4240             this.totalWidth = 0;
4241             for(var i = 0, len = this.config.length; i < len; i++){
4242                 if(includeHidden || !this.isHidden(i)){
4243                     this.totalWidth += this.getColumnWidth(i);
4244                 }
4245             }
4246         }
4247         return this.totalWidth;
4248     },
4249
4250     /**
4251      * Returns the header for the specified column.
4252      * @param {Number} col The column index
4253      * @return {String}
4254      */
4255     getColumnHeader : function(col){
4256         return this.config[col].header;
4257     },
4258
4259     /**
4260      * Sets the header for a column.
4261      * @param {Number} col The column index
4262      * @param {String} header The new header
4263      */
4264     setColumnHeader : function(col, header){
4265         this.config[col].header = header;
4266         this.fireEvent("headerchange", this, col, header);
4267     },
4268
4269     /**
4270      * Returns the tooltip for the specified column.
4271      * @param {Number} col The column index
4272      * @return {String}
4273      */
4274     getColumnTooltip : function(col){
4275             return this.config[col].tooltip;
4276     },
4277     /**
4278      * Sets the tooltip for a column.
4279      * @param {Number} col The column index
4280      * @param {String} tooltip The new tooltip
4281      */
4282     setColumnTooltip : function(col, tooltip){
4283             this.config[col].tooltip = tooltip;
4284     },
4285
4286     /**
4287      * Returns the dataIndex for the specified column.
4288      * @param {Number} col The column index
4289      * @return {Number}
4290      */
4291     getDataIndex : function(col){
4292         return this.config[col].dataIndex;
4293     },
4294
4295     /**
4296      * Sets the dataIndex for a column.
4297      * @param {Number} col The column index
4298      * @param {Number} dataIndex The new dataIndex
4299      */
4300     setDataIndex : function(col, dataIndex){
4301         this.config[col].dataIndex = dataIndex;
4302     },
4303
4304     
4305     
4306     /**
4307      * Returns true if the cell is editable.
4308      * @param {Number} colIndex The column index
4309      * @param {Number} rowIndex The row index
4310      * @return {Boolean}
4311      */
4312     isCellEditable : function(colIndex, rowIndex){
4313         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
4314     },
4315
4316     /**
4317      * Returns the editor defined for the cell/column.
4318      * return false or null to disable editing.
4319      * @param {Number} colIndex The column index
4320      * @param {Number} rowIndex The row index
4321      * @return {Object}
4322      */
4323     getCellEditor : function(colIndex, rowIndex){
4324         return this.config[colIndex].editor;
4325     },
4326
4327     /**
4328      * Sets if a column is editable.
4329      * @param {Number} col The column index
4330      * @param {Boolean} editable True if the column is editable
4331      */
4332     setEditable : function(col, editable){
4333         this.config[col].editable = editable;
4334     },
4335
4336
4337     /**
4338      * Returns true if the column is hidden.
4339      * @param {Number} colIndex The column index
4340      * @return {Boolean}
4341      */
4342     isHidden : function(colIndex){
4343         return this.config[colIndex].hidden;
4344     },
4345
4346
4347     /**
4348      * Returns true if the column width cannot be changed
4349      */
4350     isFixed : function(colIndex){
4351         return this.config[colIndex].fixed;
4352     },
4353
4354     /**
4355      * Returns true if the column can be resized
4356      * @return {Boolean}
4357      */
4358     isResizable : function(colIndex){
4359         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
4360     },
4361     /**
4362      * Sets if a column is hidden.
4363      * @param {Number} colIndex The column index
4364      * @param {Boolean} hidden True if the column is hidden
4365      */
4366     setHidden : function(colIndex, hidden){
4367         this.config[colIndex].hidden = hidden;
4368         this.totalWidth = null;
4369         this.fireEvent("hiddenchange", this, colIndex, hidden);
4370     },
4371
4372     /**
4373      * Sets the editor for a column.
4374      * @param {Number} col The column index
4375      * @param {Object} editor The editor object
4376      */
4377     setEditor : function(col, editor){
4378         this.config[col].editor = editor;
4379     }
4380 });
4381
4382 Roo.grid.ColumnModel.defaultRenderer = function(value){
4383         if(typeof value == "string" && value.length < 1){
4384             return "&#160;";
4385         }
4386         return value;
4387 };
4388
4389 // Alias for backwards compatibility
4390 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
4391 /*
4392  * Based on:
4393  * Ext JS Library 1.1.1
4394  * Copyright(c) 2006-2007, Ext JS, LLC.
4395  *
4396  * Originally Released Under LGPL - original licence link has changed is not relivant.
4397  *
4398  * Fork - LGPL
4399  * <script type="text/javascript">
4400  */
4401  
4402 /**
4403  * @class Roo.LoadMask
4404  * A simple utility class for generically masking elements while loading data.  If the element being masked has
4405  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
4406  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
4407  * element's UpdateManager load indicator and will be destroyed after the initial load.
4408  * @constructor
4409  * Create a new LoadMask
4410  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
4411  * @param {Object} config The config object
4412  */
4413 Roo.LoadMask = function(el, config){
4414     this.el = Roo.get(el);
4415     Roo.apply(this, config);
4416     if(this.store){
4417         this.store.on('beforeload', this.onBeforeLoad, this);
4418         this.store.on('load', this.onLoad, this);
4419         this.store.on('loadexception', this.onLoadException, this);
4420         this.removeMask = false;
4421     }else{
4422         var um = this.el.getUpdateManager();
4423         um.showLoadIndicator = false; // disable the default indicator
4424         um.on('beforeupdate', this.onBeforeLoad, this);
4425         um.on('update', this.onLoad, this);
4426         um.on('failure', this.onLoad, this);
4427         this.removeMask = true;
4428     }
4429 };
4430
4431 Roo.LoadMask.prototype = {
4432     /**
4433      * @cfg {Boolean} removeMask
4434      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
4435      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
4436      */
4437     /**
4438      * @cfg {String} msg
4439      * The text to display in a centered loading message box (defaults to 'Loading...')
4440      */
4441     msg : 'Loading...',
4442     /**
4443      * @cfg {String} msgCls
4444      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
4445      */
4446     msgCls : 'x-mask-loading',
4447
4448     /**
4449      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
4450      * @type Boolean
4451      */
4452     disabled: false,
4453
4454     /**
4455      * Disables the mask to prevent it from being displayed
4456      */
4457     disable : function(){
4458        this.disabled = true;
4459     },
4460
4461     /**
4462      * Enables the mask so that it can be displayed
4463      */
4464     enable : function(){
4465         this.disabled = false;
4466     },
4467     
4468     onLoadException : function()
4469     {
4470         Roo.log(arguments);
4471         
4472         if (typeof(arguments[3]) != 'undefined') {
4473             Roo.MessageBox.alert("Error loading",arguments[3]);
4474         } 
4475         /*
4476         try {
4477             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
4478                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
4479             }   
4480         } catch(e) {
4481             
4482         }
4483         */
4484     
4485         
4486         
4487         this.el.unmask(this.removeMask);
4488     },
4489     // private
4490     onLoad : function()
4491     {
4492         this.el.unmask(this.removeMask);
4493     },
4494
4495     // private
4496     onBeforeLoad : function(){
4497         if(!this.disabled){
4498             this.el.mask(this.msg, this.msgCls);
4499         }
4500     },
4501
4502     // private
4503     destroy : function(){
4504         if(this.store){
4505             this.store.un('beforeload', this.onBeforeLoad, this);
4506             this.store.un('load', this.onLoad, this);
4507             this.store.un('loadexception', this.onLoadException, this);
4508         }else{
4509             var um = this.el.getUpdateManager();
4510             um.un('beforeupdate', this.onBeforeLoad, this);
4511             um.un('update', this.onLoad, this);
4512             um.un('failure', this.onLoad, this);
4513         }
4514     }
4515 };/*
4516  * - LGPL
4517  *
4518  * table
4519  * 
4520  */
4521
4522 /**
4523  * @class Roo.bootstrap.Table
4524  * @extends Roo.bootstrap.Component
4525  * Bootstrap Table class
4526  * @cfg {String} cls table class
4527  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
4528  * @cfg {String} bgcolor Specifies the background color for a table
4529  * @cfg {Number} border Specifies whether the table cells should have borders or not
4530  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
4531  * @cfg {Number} cellspacing Specifies the space between cells
4532  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
4533  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
4534  * @cfg {String} sortable Specifies that the table should be sortable
4535  * @cfg {String} summary Specifies a summary of the content of a table
4536  * @cfg {Number} width Specifies the width of a table
4537  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
4538  * 
4539  * @cfg {boolean} striped Should the rows be alternative striped
4540  * @cfg {boolean} bordered Add borders to the table
4541  * @cfg {boolean} hover Add hover highlighting
4542  * @cfg {boolean} condensed Format condensed
4543  * @cfg {boolean} responsive Format condensed
4544  * @cfg {Boolean} loadMask (true|false) default false
4545  * @cfg {Boolean} tfoot (true|false) generate tfoot, default true
4546  * @cfg {Boolean} thead (true|false) generate thead, default true
4547  * @cfg {Boolean} RowSelection (true|false) default false
4548  * @cfg {Boolean} CellSelection (true|false) default false
4549  *
4550  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
4551  
4552  * 
4553  * @constructor
4554  * Create a new Table
4555  * @param {Object} config The config object
4556  */
4557
4558 Roo.bootstrap.Table = function(config){
4559     Roo.bootstrap.Table.superclass.constructor.call(this, config);
4560     
4561     if (this.sm) {
4562         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
4563         this.sm = this.selModel;
4564         this.sm.xmodule = this.xmodule || false;
4565     }
4566     if (this.cm && typeof(this.cm.config) == 'undefined') {
4567         this.colModel = new Roo.grid.ColumnModel(this.cm);
4568         this.cm = this.colModel;
4569         this.cm.xmodule = this.xmodule || false;
4570     }
4571     if (this.store) {
4572         this.store= Roo.factory(this.store, Roo.data);
4573         this.ds = this.store;
4574         this.ds.xmodule = this.xmodule || false;
4575          
4576     }
4577     if (this.footer && this.store) {
4578         this.footer.dataSource = this.ds;
4579         this.footer = Roo.factory(this.footer);
4580     }
4581     
4582     /** @private */
4583     this.addEvents({
4584         /**
4585          * @event cellclick
4586          * Fires when a cell is clicked
4587          * @param {Roo.bootstrap.Table} this
4588          * @param {Roo.Element} el
4589          * @param {Number} rowIndex
4590          * @param {Number} columnIndex
4591          * @param {Roo.EventObject} e
4592          */
4593         "cellclick" : true,
4594         /**
4595          * @event celldblclick
4596          * Fires when a cell is double clicked
4597          * @param {Roo.bootstrap.Table} this
4598          * @param {Roo.Element} el
4599          * @param {Number} rowIndex
4600          * @param {Number} columnIndex
4601          * @param {Roo.EventObject} e
4602          */
4603         "celldblclick" : true,
4604         /**
4605          * @event rowclick
4606          * Fires when a row is clicked
4607          * @param {Roo.bootstrap.Table} this
4608          * @param {Roo.Element} el
4609          * @param {Number} rowIndex
4610          * @param {Roo.EventObject} e
4611          */
4612         "rowclick" : true,
4613         /**
4614          * @event rowdblclick
4615          * Fires when a row is double clicked
4616          * @param {Roo.bootstrap.Table} this
4617          * @param {Roo.Element} el
4618          * @param {Number} rowIndex
4619          * @param {Roo.EventObject} e
4620          */
4621         "rowdblclick" : true,
4622         /**
4623          * @event mouseover
4624          * Fires when a mouseover occur
4625          * @param {Roo.bootstrap.Table} this
4626          * @param {Roo.Element} el
4627          * @param {Number} rowIndex
4628          * @param {Number} columnIndex
4629          * @param {Roo.EventObject} e
4630          */
4631         "mouseover" : true,
4632         /**
4633          * @event mouseout
4634          * Fires when a mouseout occur
4635          * @param {Roo.bootstrap.Table} this
4636          * @param {Roo.Element} el
4637          * @param {Number} rowIndex
4638          * @param {Number} columnIndex
4639          * @param {Roo.EventObject} e
4640          */
4641         "mouseout" : true,
4642         /**
4643          * @event rowclass
4644          * Fires when a row is rendered, so you can change add a style to it.
4645          * @param {Roo.bootstrap.Table} this
4646          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
4647          */
4648         'rowclass' : true,
4649         /**
4650          * @event afterload
4651          * Fires when record have been loaded
4652          * @param {Roo.bootstrap.Table} this
4653          * @param {Object} records  store records
4654          */
4655         'afterload' : true
4656         
4657     });
4658 };
4659
4660 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
4661     
4662     cls: false,
4663     align: false,
4664     bgcolor: false,
4665     border: false,
4666     cellpadding: false,
4667     cellspacing: false,
4668     frame: false,
4669     rules: false,
4670     sortable: false,
4671     summary: false,
4672     width: false,
4673     striped : false,
4674     bordered: false,
4675     hover:  false,
4676     condensed : false,
4677     responsive : false,
4678     sm : false,
4679     cm : false,
4680     store : false,
4681     loadMask : false,
4682     tfoot : true,
4683     thead : true,
4684     RowSelection : false,
4685     CellSelection : false,
4686     layout : false,
4687     
4688     
4689     getAutoCreate : function(){
4690         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
4691         
4692         cfg = {
4693             tag: 'table',
4694             cls : 'table',
4695             cn : []
4696         }
4697             
4698         if (this.striped) {
4699             cfg.cls += ' table-striped';
4700         }
4701         
4702         if (this.hover) {
4703             cfg.cls += ' table-hover';
4704         }
4705         if (this.bordered) {
4706             cfg.cls += ' table-bordered';
4707         }
4708         if (this.condensed) {
4709             cfg.cls += ' table-condensed';
4710         }
4711         if (this.responsive) {
4712             cfg.cls += ' table-responsive';
4713         }
4714         
4715         if (this.cls) {
4716             cfg.cls+=  ' ' +this.cls;
4717         }
4718         
4719         // this lot should be simplifed...
4720         
4721         if (this.align) {
4722             cfg.align=this.align;
4723         }
4724         if (this.bgcolor) {
4725             cfg.bgcolor=this.bgcolor;
4726         }
4727         if (this.border) {
4728             cfg.border=this.border;
4729         }
4730         if (this.cellpadding) {
4731             cfg.cellpadding=this.cellpadding;
4732         }
4733         if (this.cellspacing) {
4734             cfg.cellspacing=this.cellspacing;
4735         }
4736         if (this.frame) {
4737             cfg.frame=this.frame;
4738         }
4739         if (this.rules) {
4740             cfg.rules=this.rules;
4741         }
4742         if (this.sortable) {
4743             cfg.sortable=this.sortable;
4744         }
4745         if (this.summary) {
4746             cfg.summary=this.summary;
4747         }
4748         if (this.width) {
4749             cfg.width=this.width;
4750         }
4751         if (this.layout) {
4752             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
4753         }
4754         
4755         if(this.store || this.cm){
4756             if(this.thead){
4757                 cfg.cn.push(this.renderHeader());
4758             }
4759             
4760             cfg.cn.push(this.renderBody());
4761             
4762             if(this.tfoot){
4763                 cfg.cn.push(this.renderFooter());
4764             }
4765             
4766             cfg.cls+=  ' TableGrid';
4767         }
4768         
4769         return { cn : [ cfg ] };
4770     },
4771     
4772     initEvents : function()
4773     {   
4774         if(!this.store || !this.cm){
4775             return;
4776         }
4777         
4778         Roo.log('initEvents with ds!!!!');
4779         
4780         var _this = this;
4781         
4782         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
4783             e.on('click', _this.sort, _this);
4784         });
4785         
4786         this.el.on("click", this.onClick, this);
4787         this.el.on("dblclick", this.onDblClick, this);
4788         
4789         this.parent().el.setStyle('position', 'relative');
4790         if (this.footer) {
4791             this.footer.parentId = this.id;
4792             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
4793         }
4794         
4795         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
4796         
4797         this.store.on('load', this.onLoad, this);
4798         this.store.on('beforeload', this.onBeforeLoad, this);
4799         
4800     },
4801     
4802     onMouseover : function(e, el)
4803     {
4804         var cell = Roo.get(el);
4805         
4806         if(!cell){
4807             return;
4808         }
4809         
4810         if(e.getTarget().nodeName.toLowerCase() != 'td'){
4811             cell = cell.findParent('td', false, true);
4812         }
4813         
4814         var row = cell.findParent('tr', false, true);
4815         var cellIndex = cell.dom.cellIndex;
4816         var rowIndex = row.dom.rowIndex - 1; // start from 0
4817         
4818         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
4819         
4820     },
4821     
4822     onMouseout : function(e, el)
4823     {
4824         var cell = Roo.get(el);
4825         
4826         if(!cell){
4827             return;
4828         }
4829         
4830         if(e.getTarget().nodeName.toLowerCase() != 'td'){
4831             cell = cell.findParent('td', false, true);
4832         }
4833         
4834         var row = cell.findParent('tr', false, true);
4835         var cellIndex = cell.dom.cellIndex;
4836         var rowIndex = row.dom.rowIndex - 1; // start from 0
4837         
4838         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
4839         
4840     },
4841     
4842     onClick : function(e, el)
4843     {
4844         var cell = Roo.get(el);
4845         
4846         if(!cell || !this.CellSelection || !this.RowSelection){
4847             return;
4848         }
4849         
4850         
4851         if(e.getTarget().nodeName.toLowerCase() != 'td'){
4852             cell = cell.findParent('td', false, true);
4853         }
4854         
4855         var row = cell.findParent('tr', false, true);
4856         var cellIndex = cell.dom.cellIndex;
4857         var rowIndex = row.dom.rowIndex - 1;
4858         
4859         if(this.CellSelection){
4860             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
4861         }
4862         
4863         if(this.RowSelection){
4864             this.fireEvent('rowclick', this, row, rowIndex, e);
4865         }
4866         
4867         
4868     },
4869     
4870     onDblClick : function(e,el)
4871     {
4872         var cell = Roo.get(el);
4873         
4874         if(!cell || !this.CellSelection || !this.RowSelection){
4875             return;
4876         }
4877         
4878         if(e.getTarget().nodeName.toLowerCase() != 'td'){
4879             cell = cell.findParent('td', false, true);
4880         }
4881         
4882         var row = cell.findParent('tr', false, true);
4883         var cellIndex = cell.dom.cellIndex;
4884         var rowIndex = row.dom.rowIndex - 1;
4885         
4886         if(this.CellSelection){
4887             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
4888         }
4889         
4890         if(this.RowSelection){
4891             this.fireEvent('rowdblclick', this, row, rowIndex, e);
4892         }
4893     },
4894     
4895     sort : function(e,el)
4896     {
4897         var col = Roo.get(el)
4898         
4899         if(!col.hasClass('sortable')){
4900             return;
4901         }
4902         
4903         var sort = col.attr('sort');
4904         var dir = 'ASC';
4905         
4906         if(col.hasClass('glyphicon-arrow-up')){
4907             dir = 'DESC';
4908         }
4909         
4910         this.store.sortInfo = {field : sort, direction : dir};
4911         
4912         if (this.footer) {
4913             Roo.log("calling footer first");
4914             this.footer.onClick('first');
4915         } else {
4916         
4917             this.store.load({ params : { start : 0 } });
4918         }
4919     },
4920     
4921     renderHeader : function()
4922     {
4923         var header = {
4924             tag: 'thead',
4925             cn : []
4926         };
4927         
4928         var cm = this.cm;
4929         
4930         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
4931             
4932             var config = cm.config[i];
4933                     
4934             var c = {
4935                 tag: 'th',
4936                 style : '',
4937                 html: cm.getColumnHeader(i)
4938             };
4939             
4940             if(typeof(config.hidden) != 'undefined' && config.hidden){
4941                 c.style += ' display:none;';
4942             }
4943             
4944             if(typeof(config.dataIndex) != 'undefined'){
4945                 c.sort = config.dataIndex;
4946             }
4947             
4948             if(typeof(config.sortable) != 'undefined' && config.sortable){
4949                 c.cls = 'sortable';
4950             }
4951             
4952 //            if(typeof(config.align) != 'undefined' && config.align.length){
4953 //                c.style += ' text-align:' + config.align + ';';
4954 //            }
4955             
4956             if(typeof(config.width) != 'undefined'){
4957                 c.style += ' width:' + config.width + 'px;';
4958             }
4959             
4960             header.cn.push(c)
4961         }
4962         
4963         return header;
4964     },
4965     
4966     renderBody : function()
4967     {
4968         var body = {
4969             tag: 'tbody',
4970             cn : [
4971                 {
4972                     tag: 'tr',
4973                     cn : [
4974                         {
4975                             tag : 'td',
4976                             colspan :  this.cm.getColumnCount()
4977                         }
4978                     ]
4979                 }
4980             ]
4981         };
4982         
4983         return body;
4984     },
4985     
4986     renderFooter : function()
4987     {
4988         var footer = {
4989             tag: 'tfoot',
4990             cn : [
4991                 {
4992                     tag: 'tr',
4993                     cn : [
4994                         {
4995                             tag : 'td',
4996                             colspan :  this.cm.getColumnCount()
4997                         }
4998                     ]
4999                 }
5000             ]
5001         };
5002         
5003         return footer;
5004     },
5005     
5006     onLoad : function()
5007     {
5008         Roo.log('ds onload');
5009         this.clear();
5010         
5011         var _this = this;
5012         var cm = this.cm;
5013         
5014         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5015             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
5016             
5017             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
5018                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
5019             }
5020             
5021             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
5022                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
5023             }
5024         });
5025         
5026         var tbody = this.el.select('tbody', true).first();
5027         
5028         var renders = [];
5029                     
5030         if(this.store.getCount() > 0){
5031             this.store.data.each(function(d,rowIndex){
5032                 var row = {
5033                     tag : 'tr',
5034                     cn : []
5035                 };
5036                 
5037                 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5038                     var config = cm.config[i];
5039                     
5040                     var renderer = cm.getRenderer(i);
5041                     var value = '';
5042                     var id = Roo.id();
5043                     
5044                     if(typeof(renderer) !== 'undefined'){
5045                         value = renderer(d.data[cm.getDataIndex(i)], false, d);
5046                     }
5047                     
5048                     if(typeof(value) === 'object'){
5049                         renders.push({
5050                             container : id,
5051                             cfg : value 
5052                         })
5053                     }
5054                     
5055                     var rowcfg = {
5056                         record: d,
5057                         rowIndex : rowIndex,
5058                         colIndex : i,
5059                         rowClass : ''
5060                     }
5061
5062                     _this.fireEvent('rowclass', this, rowcfg);
5063                     
5064                     var td = {
5065                         tag: 'td',
5066                         id: id,
5067                         cls : rowcfg.rowClass,
5068                         style: '',
5069                         html: (typeof(value) === 'object') ? '' : value
5070                     };
5071                     
5072                     if(typeof(config.hidden) != 'undefined' && config.hidden){
5073                         td.style += ' display:none;';
5074                     }
5075                     
5076                     if(typeof(config.align) != 'undefined' && config.align.length){
5077                         td.style += ' text-align:' + config.align + ';';
5078                     }
5079                     
5080                     if(typeof(config.width) != 'undefined'){
5081                         td.style += ' width:' +  config.width + 'px;';
5082                     }
5083                     
5084                     
5085                     row.cn.push(td);
5086                    
5087                 }
5088                 
5089                 tbody.createChild(row);
5090                 
5091             });
5092         }
5093         
5094         
5095         if(renders.length){
5096             var _this = this;
5097             Roo.each(renders, function(r){
5098                 _this.renderColumn(r);
5099             })
5100         }
5101         
5102         Roo.each(this.el.select('tbody td', true).elements, function(e){
5103             e.on('mouseover', _this.onMouseover, _this);
5104         });
5105         
5106         Roo.each(this.el.select('tbody td', true).elements, function(e){
5107             e.on('mouseout', _this.onMouseout, _this);
5108         });
5109
5110         this.fireEvent('afterload', this, this.store.data);
5111         //if(this.loadMask){
5112         //    this.maskEl.hide();
5113         //}
5114     },
5115     
5116     onBeforeLoad : function()
5117     {
5118         //Roo.log('ds onBeforeLoad');
5119         
5120         //this.clear();
5121         
5122         //if(this.loadMask){
5123         //    this.maskEl.show();
5124         //}
5125     },
5126     
5127     clear : function()
5128     {
5129         this.el.select('tbody', true).first().dom.innerHTML = '';
5130     },
5131     
5132     getSelectionModel : function(){
5133         if(!this.selModel){
5134             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
5135         }
5136         return this.selModel;
5137     },
5138     
5139     renderColumn : function(r)
5140     {
5141         var _this = this;
5142         
5143         var t = r.cfg.render(r.container);
5144         
5145         if(r.cfg.cn){
5146             Roo.each(r.cfg.cn, function(c){
5147                 var child = {
5148                     container: t.getChildContainer(),
5149                     cfg: c
5150                 }
5151                 _this.renderColumn(child);
5152             })
5153         }
5154     }
5155    
5156 });
5157
5158  
5159
5160  /*
5161  * - LGPL
5162  *
5163  * table cell
5164  * 
5165  */
5166
5167 /**
5168  * @class Roo.bootstrap.TableCell
5169  * @extends Roo.bootstrap.Component
5170  * Bootstrap TableCell class
5171  * @cfg {String} html cell contain text
5172  * @cfg {String} cls cell class
5173  * @cfg {String} tag cell tag (td|th) default td
5174  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
5175  * @cfg {String} align Aligns the content in a cell
5176  * @cfg {String} axis Categorizes cells
5177  * @cfg {String} bgcolor Specifies the background color of a cell
5178  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5179  * @cfg {Number} colspan Specifies the number of columns a cell should span
5180  * @cfg {String} headers Specifies one or more header cells a cell is related to
5181  * @cfg {Number} height Sets the height of a cell
5182  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
5183  * @cfg {Number} rowspan Sets the number of rows a cell should span
5184  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
5185  * @cfg {String} valign Vertical aligns the content in a cell
5186  * @cfg {Number} width Specifies the width of a cell
5187  * 
5188  * @constructor
5189  * Create a new TableCell
5190  * @param {Object} config The config object
5191  */
5192
5193 Roo.bootstrap.TableCell = function(config){
5194     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
5195 };
5196
5197 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
5198     
5199     html: false,
5200     cls: false,
5201     tag: false,
5202     abbr: false,
5203     align: false,
5204     axis: false,
5205     bgcolor: false,
5206     charoff: false,
5207     colspan: false,
5208     headers: false,
5209     height: false,
5210     nowrap: false,
5211     rowspan: false,
5212     scope: false,
5213     valign: false,
5214     width: false,
5215     
5216     
5217     getAutoCreate : function(){
5218         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
5219         
5220         cfg = {
5221             tag: 'td'
5222         }
5223         
5224         if(this.tag){
5225             cfg.tag = this.tag;
5226         }
5227         
5228         if (this.html) {
5229             cfg.html=this.html
5230         }
5231         if (this.cls) {
5232             cfg.cls=this.cls
5233         }
5234         if (this.abbr) {
5235             cfg.abbr=this.abbr
5236         }
5237         if (this.align) {
5238             cfg.align=this.align
5239         }
5240         if (this.axis) {
5241             cfg.axis=this.axis
5242         }
5243         if (this.bgcolor) {
5244             cfg.bgcolor=this.bgcolor
5245         }
5246         if (this.charoff) {
5247             cfg.charoff=this.charoff
5248         }
5249         if (this.colspan) {
5250             cfg.colspan=this.colspan
5251         }
5252         if (this.headers) {
5253             cfg.headers=this.headers
5254         }
5255         if (this.height) {
5256             cfg.height=this.height
5257         }
5258         if (this.nowrap) {
5259             cfg.nowrap=this.nowrap
5260         }
5261         if (this.rowspan) {
5262             cfg.rowspan=this.rowspan
5263         }
5264         if (this.scope) {
5265             cfg.scope=this.scope
5266         }
5267         if (this.valign) {
5268             cfg.valign=this.valign
5269         }
5270         if (this.width) {
5271             cfg.width=this.width
5272         }
5273         
5274         
5275         return cfg;
5276     }
5277    
5278 });
5279
5280  
5281
5282  /*
5283  * - LGPL
5284  *
5285  * table row
5286  * 
5287  */
5288
5289 /**
5290  * @class Roo.bootstrap.TableRow
5291  * @extends Roo.bootstrap.Component
5292  * Bootstrap TableRow class
5293  * @cfg {String} cls row class
5294  * @cfg {String} align Aligns the content in a table row
5295  * @cfg {String} bgcolor Specifies a background color for a table row
5296  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5297  * @cfg {String} valign Vertical aligns the content in a table row
5298  * 
5299  * @constructor
5300  * Create a new TableRow
5301  * @param {Object} config The config object
5302  */
5303
5304 Roo.bootstrap.TableRow = function(config){
5305     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
5306 };
5307
5308 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
5309     
5310     cls: false,
5311     align: false,
5312     bgcolor: false,
5313     charoff: false,
5314     valign: false,
5315     
5316     getAutoCreate : function(){
5317         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
5318         
5319         cfg = {
5320             tag: 'tr'
5321         }
5322             
5323         if(this.cls){
5324             cfg.cls = this.cls;
5325         }
5326         if(this.align){
5327             cfg.align = this.align;
5328         }
5329         if(this.bgcolor){
5330             cfg.bgcolor = this.bgcolor;
5331         }
5332         if(this.charoff){
5333             cfg.charoff = this.charoff;
5334         }
5335         if(this.valign){
5336             cfg.valign = this.valign;
5337         }
5338         
5339         return cfg;
5340     }
5341    
5342 });
5343
5344  
5345
5346  /*
5347  * - LGPL
5348  *
5349  * table body
5350  * 
5351  */
5352
5353 /**
5354  * @class Roo.bootstrap.TableBody
5355  * @extends Roo.bootstrap.Component
5356  * Bootstrap TableBody class
5357  * @cfg {String} cls element class
5358  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
5359  * @cfg {String} align Aligns the content inside the element
5360  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
5361  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
5362  * 
5363  * @constructor
5364  * Create a new TableBody
5365  * @param {Object} config The config object
5366  */
5367
5368 Roo.bootstrap.TableBody = function(config){
5369     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
5370 };
5371
5372 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
5373     
5374     cls: false,
5375     tag: false,
5376     align: false,
5377     charoff: false,
5378     valign: false,
5379     
5380     getAutoCreate : function(){
5381         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
5382         
5383         cfg = {
5384             tag: 'tbody'
5385         }
5386             
5387         if (this.cls) {
5388             cfg.cls=this.cls
5389         }
5390         if(this.tag){
5391             cfg.tag = this.tag;
5392         }
5393         
5394         if(this.align){
5395             cfg.align = this.align;
5396         }
5397         if(this.charoff){
5398             cfg.charoff = this.charoff;
5399         }
5400         if(this.valign){
5401             cfg.valign = this.valign;
5402         }
5403         
5404         return cfg;
5405     }
5406     
5407     
5408 //    initEvents : function()
5409 //    {
5410 //        
5411 //        if(!this.store){
5412 //            return;
5413 //        }
5414 //        
5415 //        this.store = Roo.factory(this.store, Roo.data);
5416 //        this.store.on('load', this.onLoad, this);
5417 //        
5418 //        this.store.load();
5419 //        
5420 //    },
5421 //    
5422 //    onLoad: function () 
5423 //    {   
5424 //        this.fireEvent('load', this);
5425 //    }
5426 //    
5427 //   
5428 });
5429
5430  
5431
5432  /*
5433  * Based on:
5434  * Ext JS Library 1.1.1
5435  * Copyright(c) 2006-2007, Ext JS, LLC.
5436  *
5437  * Originally Released Under LGPL - original licence link has changed is not relivant.
5438  *
5439  * Fork - LGPL
5440  * <script type="text/javascript">
5441  */
5442
5443 // as we use this in bootstrap.
5444 Roo.namespace('Roo.form');
5445  /**
5446  * @class Roo.form.Action
5447  * Internal Class used to handle form actions
5448  * @constructor
5449  * @param {Roo.form.BasicForm} el The form element or its id
5450  * @param {Object} config Configuration options
5451  */
5452
5453  
5454  
5455 // define the action interface
5456 Roo.form.Action = function(form, options){
5457     this.form = form;
5458     this.options = options || {};
5459 };
5460 /**
5461  * Client Validation Failed
5462  * @const 
5463  */
5464 Roo.form.Action.CLIENT_INVALID = 'client';
5465 /**
5466  * Server Validation Failed
5467  * @const 
5468  */
5469 Roo.form.Action.SERVER_INVALID = 'server';
5470  /**
5471  * Connect to Server Failed
5472  * @const 
5473  */
5474 Roo.form.Action.CONNECT_FAILURE = 'connect';
5475 /**
5476  * Reading Data from Server Failed
5477  * @const 
5478  */
5479 Roo.form.Action.LOAD_FAILURE = 'load';
5480
5481 Roo.form.Action.prototype = {
5482     type : 'default',
5483     failureType : undefined,
5484     response : undefined,
5485     result : undefined,
5486
5487     // interface method
5488     run : function(options){
5489
5490     },
5491
5492     // interface method
5493     success : function(response){
5494
5495     },
5496
5497     // interface method
5498     handleResponse : function(response){
5499
5500     },
5501
5502     // default connection failure
5503     failure : function(response){
5504         
5505         this.response = response;
5506         this.failureType = Roo.form.Action.CONNECT_FAILURE;
5507         this.form.afterAction(this, false);
5508     },
5509
5510     processResponse : function(response){
5511         this.response = response;
5512         if(!response.responseText){
5513             return true;
5514         }
5515         this.result = this.handleResponse(response);
5516         return this.result;
5517     },
5518
5519     // utility functions used internally
5520     getUrl : function(appendParams){
5521         var url = this.options.url || this.form.url || this.form.el.dom.action;
5522         if(appendParams){
5523             var p = this.getParams();
5524             if(p){
5525                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
5526             }
5527         }
5528         return url;
5529     },
5530
5531     getMethod : function(){
5532         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
5533     },
5534
5535     getParams : function(){
5536         var bp = this.form.baseParams;
5537         var p = this.options.params;
5538         if(p){
5539             if(typeof p == "object"){
5540                 p = Roo.urlEncode(Roo.applyIf(p, bp));
5541             }else if(typeof p == 'string' && bp){
5542                 p += '&' + Roo.urlEncode(bp);
5543             }
5544         }else if(bp){
5545             p = Roo.urlEncode(bp);
5546         }
5547         return p;
5548     },
5549
5550     createCallback : function(){
5551         return {
5552             success: this.success,
5553             failure: this.failure,
5554             scope: this,
5555             timeout: (this.form.timeout*1000),
5556             upload: this.form.fileUpload ? this.success : undefined
5557         };
5558     }
5559 };
5560
5561 Roo.form.Action.Submit = function(form, options){
5562     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
5563 };
5564
5565 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
5566     type : 'submit',
5567
5568     haveProgress : false,
5569     uploadComplete : false,
5570     
5571     // uploadProgress indicator.
5572     uploadProgress : function()
5573     {
5574         if (!this.form.progressUrl) {
5575             return;
5576         }
5577         
5578         if (!this.haveProgress) {
5579             Roo.MessageBox.progress("Uploading", "Uploading");
5580         }
5581         if (this.uploadComplete) {
5582            Roo.MessageBox.hide();
5583            return;
5584         }
5585         
5586         this.haveProgress = true;
5587    
5588         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
5589         
5590         var c = new Roo.data.Connection();
5591         c.request({
5592             url : this.form.progressUrl,
5593             params: {
5594                 id : uid
5595             },
5596             method: 'GET',
5597             success : function(req){
5598                //console.log(data);
5599                 var rdata = false;
5600                 var edata;
5601                 try  {
5602                    rdata = Roo.decode(req.responseText)
5603                 } catch (e) {
5604                     Roo.log("Invalid data from server..");
5605                     Roo.log(edata);
5606                     return;
5607                 }
5608                 if (!rdata || !rdata.success) {
5609                     Roo.log(rdata);
5610                     Roo.MessageBox.alert(Roo.encode(rdata));
5611                     return;
5612                 }
5613                 var data = rdata.data;
5614                 
5615                 if (this.uploadComplete) {
5616                    Roo.MessageBox.hide();
5617                    return;
5618                 }
5619                    
5620                 if (data){
5621                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
5622                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
5623                     );
5624                 }
5625                 this.uploadProgress.defer(2000,this);
5626             },
5627        
5628             failure: function(data) {
5629                 Roo.log('progress url failed ');
5630                 Roo.log(data);
5631             },
5632             scope : this
5633         });
5634            
5635     },
5636     
5637     
5638     run : function()
5639     {
5640         // run get Values on the form, so it syncs any secondary forms.
5641         this.form.getValues();
5642         
5643         var o = this.options;
5644         var method = this.getMethod();
5645         var isPost = method == 'POST';
5646         if(o.clientValidation === false || this.form.isValid()){
5647             
5648             if (this.form.progressUrl) {
5649                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
5650                     (new Date() * 1) + '' + Math.random());
5651                     
5652             } 
5653             
5654             
5655             Roo.Ajax.request(Roo.apply(this.createCallback(), {
5656                 form:this.form.el.dom,
5657                 url:this.getUrl(!isPost),
5658                 method: method,
5659                 params:isPost ? this.getParams() : null,
5660                 isUpload: this.form.fileUpload
5661             }));
5662             
5663             this.uploadProgress();
5664
5665         }else if (o.clientValidation !== false){ // client validation failed
5666             this.failureType = Roo.form.Action.CLIENT_INVALID;
5667             this.form.afterAction(this, false);
5668         }
5669     },
5670
5671     success : function(response)
5672     {
5673         this.uploadComplete= true;
5674         if (this.haveProgress) {
5675             Roo.MessageBox.hide();
5676         }
5677         
5678         
5679         var result = this.processResponse(response);
5680         if(result === true || result.success){
5681             this.form.afterAction(this, true);
5682             return;
5683         }
5684         if(result.errors){
5685             this.form.markInvalid(result.errors);
5686             this.failureType = Roo.form.Action.SERVER_INVALID;
5687         }
5688         this.form.afterAction(this, false);
5689     },
5690     failure : function(response)
5691     {
5692         this.uploadComplete= true;
5693         if (this.haveProgress) {
5694             Roo.MessageBox.hide();
5695         }
5696         
5697         this.response = response;
5698         this.failureType = Roo.form.Action.CONNECT_FAILURE;
5699         this.form.afterAction(this, false);
5700     },
5701     
5702     handleResponse : function(response){
5703         if(this.form.errorReader){
5704             var rs = this.form.errorReader.read(response);
5705             var errors = [];
5706             if(rs.records){
5707                 for(var i = 0, len = rs.records.length; i < len; i++) {
5708                     var r = rs.records[i];
5709                     errors[i] = r.data;
5710                 }
5711             }
5712             if(errors.length < 1){
5713                 errors = null;
5714             }
5715             return {
5716                 success : rs.success,
5717                 errors : errors
5718             };
5719         }
5720         var ret = false;
5721         try {
5722             ret = Roo.decode(response.responseText);
5723         } catch (e) {
5724             ret = {
5725                 success: false,
5726                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
5727                 errors : []
5728             };
5729         }
5730         return ret;
5731         
5732     }
5733 });
5734
5735
5736 Roo.form.Action.Load = function(form, options){
5737     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
5738     this.reader = this.form.reader;
5739 };
5740
5741 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
5742     type : 'load',
5743
5744     run : function(){
5745         
5746         Roo.Ajax.request(Roo.apply(
5747                 this.createCallback(), {
5748                     method:this.getMethod(),
5749                     url:this.getUrl(false),
5750                     params:this.getParams()
5751         }));
5752     },
5753
5754     success : function(response){
5755         
5756         var result = this.processResponse(response);
5757         if(result === true || !result.success || !result.data){
5758             this.failureType = Roo.form.Action.LOAD_FAILURE;
5759             this.form.afterAction(this, false);
5760             return;
5761         }
5762         this.form.clearInvalid();
5763         this.form.setValues(result.data);
5764         this.form.afterAction(this, true);
5765     },
5766
5767     handleResponse : function(response){
5768         if(this.form.reader){
5769             var rs = this.form.reader.read(response);
5770             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
5771             return {
5772                 success : rs.success,
5773                 data : data
5774             };
5775         }
5776         return Roo.decode(response.responseText);
5777     }
5778 });
5779
5780 Roo.form.Action.ACTION_TYPES = {
5781     'load' : Roo.form.Action.Load,
5782     'submit' : Roo.form.Action.Submit
5783 };/*
5784  * - LGPL
5785  *
5786  * form
5787  * 
5788  */
5789
5790 /**
5791  * @class Roo.bootstrap.Form
5792  * @extends Roo.bootstrap.Component
5793  * Bootstrap Form class
5794  * @cfg {String} method  GET | POST (default POST)
5795  * @cfg {String} labelAlign top | left (default top)
5796   * @cfg {String} align left  | right - for navbars
5797
5798  * 
5799  * @constructor
5800  * Create a new Form
5801  * @param {Object} config The config object
5802  */
5803
5804
5805 Roo.bootstrap.Form = function(config){
5806     Roo.bootstrap.Form.superclass.constructor.call(this, config);
5807     this.addEvents({
5808         /**
5809          * @event clientvalidation
5810          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
5811          * @param {Form} this
5812          * @param {Boolean} valid true if the form has passed client-side validation
5813          */
5814         clientvalidation: true,
5815         /**
5816          * @event beforeaction
5817          * Fires before any action is performed. Return false to cancel the action.
5818          * @param {Form} this
5819          * @param {Action} action The action to be performed
5820          */
5821         beforeaction: true,
5822         /**
5823          * @event actionfailed
5824          * Fires when an action fails.
5825          * @param {Form} this
5826          * @param {Action} action The action that failed
5827          */
5828         actionfailed : true,
5829         /**
5830          * @event actioncomplete
5831          * Fires when an action is completed.
5832          * @param {Form} this
5833          * @param {Action} action The action that completed
5834          */
5835         actioncomplete : true
5836     });
5837     
5838 };
5839
5840 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
5841       
5842      /**
5843      * @cfg {String} method
5844      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
5845      */
5846     method : 'POST',
5847     /**
5848      * @cfg {String} url
5849      * The URL to use for form actions if one isn't supplied in the action options.
5850      */
5851     /**
5852      * @cfg {Boolean} fileUpload
5853      * Set to true if this form is a file upload.
5854      */
5855      
5856     /**
5857      * @cfg {Object} baseParams
5858      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
5859      */
5860       
5861     /**
5862      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
5863      */
5864     timeout: 30,
5865     /**
5866      * @cfg {Sting} align (left|right) for navbar forms
5867      */
5868     align : 'left',
5869
5870     // private
5871     activeAction : null,
5872  
5873     /**
5874      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
5875      * element by passing it or its id or mask the form itself by passing in true.
5876      * @type Mixed
5877      */
5878     waitMsgTarget : false,
5879     
5880      
5881     
5882     /**
5883      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
5884      * element by passing it or its id or mask the form itself by passing in true.
5885      * @type Mixed
5886      */
5887     
5888     getAutoCreate : function(){
5889         
5890         var cfg = {
5891             tag: 'form',
5892             method : this.method || 'POST',
5893             id : this.id || Roo.id(),
5894             cls : ''
5895         }
5896         if (this.parent().xtype.match(/^Nav/)) {
5897             cfg.cls = 'navbar-form navbar-' + this.align;
5898             
5899         }
5900         
5901         if (this.labelAlign == 'left' ) {
5902             cfg.cls += ' form-horizontal';
5903         }
5904         
5905         
5906         return cfg;
5907     },
5908     initEvents : function()
5909     {
5910         this.el.on('submit', this.onSubmit, this);
5911         // this was added as random key presses on the form where triggering form submit.
5912         this.el.on('keypress', function(e) {
5913             if (e.getCharCode() != 13) {
5914                 return true;
5915             }
5916             // we might need to allow it for textareas.. and some other items.
5917             // check e.getTarget().
5918             
5919             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
5920                 return true;
5921             }
5922         
5923             Roo.log("keypress blocked");
5924             
5925             e.preventDefault();
5926             return false;
5927         });
5928         
5929     },
5930     // private
5931     onSubmit : function(e){
5932         e.stopEvent();
5933     },
5934     
5935      /**
5936      * Returns true if client-side validation on the form is successful.
5937      * @return Boolean
5938      */
5939     isValid : function(){
5940         var items = this.getItems();
5941         var valid = true;
5942         items.each(function(f){
5943            if(!f.validate()){
5944                valid = false;
5945                
5946            }
5947         });
5948         return valid;
5949     },
5950     /**
5951      * Returns true if any fields in this form have changed since their original load.
5952      * @return Boolean
5953      */
5954     isDirty : function(){
5955         var dirty = false;
5956         var items = this.getItems();
5957         items.each(function(f){
5958            if(f.isDirty()){
5959                dirty = true;
5960                return false;
5961            }
5962            return true;
5963         });
5964         return dirty;
5965     },
5966      /**
5967      * Performs a predefined action (submit or load) or custom actions you define on this form.
5968      * @param {String} actionName The name of the action type
5969      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
5970      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
5971      * accept other config options):
5972      * <pre>
5973 Property          Type             Description
5974 ----------------  ---------------  ----------------------------------------------------------------------------------
5975 url               String           The url for the action (defaults to the form's url)
5976 method            String           The form method to use (defaults to the form's method, or POST if not defined)
5977 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
5978 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
5979                                    validate the form on the client (defaults to false)
5980      * </pre>
5981      * @return {BasicForm} this
5982      */
5983     doAction : function(action, options){
5984         if(typeof action == 'string'){
5985             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
5986         }
5987         if(this.fireEvent('beforeaction', this, action) !== false){
5988             this.beforeAction(action);
5989             action.run.defer(100, action);
5990         }
5991         return this;
5992     },
5993     
5994     // private
5995     beforeAction : function(action){
5996         var o = action.options;
5997         
5998         // not really supported yet.. ??
5999         
6000         //if(this.waitMsgTarget === true){
6001             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6002         //}else if(this.waitMsgTarget){
6003         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
6004         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
6005         //}else {
6006         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
6007        // }
6008          
6009     },
6010
6011     // private
6012     afterAction : function(action, success){
6013         this.activeAction = null;
6014         var o = action.options;
6015         
6016         //if(this.waitMsgTarget === true){
6017             this.el.unmask();
6018         //}else if(this.waitMsgTarget){
6019         //    this.waitMsgTarget.unmask();
6020         //}else{
6021         //    Roo.MessageBox.updateProgress(1);
6022         //    Roo.MessageBox.hide();
6023        // }
6024         // 
6025         if(success){
6026             if(o.reset){
6027                 this.reset();
6028             }
6029             Roo.callback(o.success, o.scope, [this, action]);
6030             this.fireEvent('actioncomplete', this, action);
6031             
6032         }else{
6033             
6034             // failure condition..
6035             // we have a scenario where updates need confirming.
6036             // eg. if a locking scenario exists..
6037             // we look for { errors : { needs_confirm : true }} in the response.
6038             if (
6039                 (typeof(action.result) != 'undefined')  &&
6040                 (typeof(action.result.errors) != 'undefined')  &&
6041                 (typeof(action.result.errors.needs_confirm) != 'undefined')
6042            ){
6043                 var _t = this;
6044                 Roo.log("not supported yet");
6045                  /*
6046                 
6047                 Roo.MessageBox.confirm(
6048                     "Change requires confirmation",
6049                     action.result.errorMsg,
6050                     function(r) {
6051                         if (r != 'yes') {
6052                             return;
6053                         }
6054                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
6055                     }
6056                     
6057                 );
6058                 */
6059                 
6060                 
6061                 return;
6062             }
6063             
6064             Roo.callback(o.failure, o.scope, [this, action]);
6065             // show an error message if no failed handler is set..
6066             if (!this.hasListener('actionfailed')) {
6067                 Roo.log("need to add dialog support");
6068                 /*
6069                 Roo.MessageBox.alert("Error",
6070                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
6071                         action.result.errorMsg :
6072                         "Saving Failed, please check your entries or try again"
6073                 );
6074                 */
6075             }
6076             
6077             this.fireEvent('actionfailed', this, action);
6078         }
6079         
6080     },
6081     /**
6082      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
6083      * @param {String} id The value to search for
6084      * @return Field
6085      */
6086     findField : function(id){
6087         var items = this.getItems();
6088         var field = items.get(id);
6089         if(!field){
6090              items.each(function(f){
6091                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
6092                     field = f;
6093                     return false;
6094                 }
6095                 return true;
6096             });
6097         }
6098         return field || null;
6099     },
6100      /**
6101      * Mark fields in this form invalid in bulk.
6102      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
6103      * @return {BasicForm} this
6104      */
6105     markInvalid : function(errors){
6106         if(errors instanceof Array){
6107             for(var i = 0, len = errors.length; i < len; i++){
6108                 var fieldError = errors[i];
6109                 var f = this.findField(fieldError.id);
6110                 if(f){
6111                     f.markInvalid(fieldError.msg);
6112                 }
6113             }
6114         }else{
6115             var field, id;
6116             for(id in errors){
6117                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
6118                     field.markInvalid(errors[id]);
6119                 }
6120             }
6121         }
6122         //Roo.each(this.childForms || [], function (f) {
6123         //    f.markInvalid(errors);
6124         //});
6125         
6126         return this;
6127     },
6128
6129     /**
6130      * Set values for fields in this form in bulk.
6131      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
6132      * @return {BasicForm} this
6133      */
6134     setValues : function(values){
6135         if(values instanceof Array){ // array of objects
6136             for(var i = 0, len = values.length; i < len; i++){
6137                 var v = values[i];
6138                 var f = this.findField(v.id);
6139                 if(f){
6140                     f.setValue(v.value);
6141                     if(this.trackResetOnLoad){
6142                         f.originalValue = f.getValue();
6143                     }
6144                 }
6145             }
6146         }else{ // object hash
6147             var field, id;
6148             for(id in values){
6149                 if(typeof values[id] != 'function' && (field = this.findField(id))){
6150                     
6151                     if (field.setFromData && 
6152                         field.valueField && 
6153                         field.displayField &&
6154                         // combos' with local stores can 
6155                         // be queried via setValue()
6156                         // to set their value..
6157                         (field.store && !field.store.isLocal)
6158                         ) {
6159                         // it's a combo
6160                         var sd = { };
6161                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
6162                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
6163                         field.setFromData(sd);
6164                         
6165                     } else {
6166                         field.setValue(values[id]);
6167                     }
6168                     
6169                     
6170                     if(this.trackResetOnLoad){
6171                         field.originalValue = field.getValue();
6172                     }
6173                 }
6174             }
6175         }
6176          
6177         //Roo.each(this.childForms || [], function (f) {
6178         //    f.setValues(values);
6179         //});
6180                 
6181         return this;
6182     },
6183
6184     /**
6185      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
6186      * they are returned as an array.
6187      * @param {Boolean} asString
6188      * @return {Object}
6189      */
6190     getValues : function(asString){
6191         //if (this.childForms) {
6192             // copy values from the child forms
6193         //    Roo.each(this.childForms, function (f) {
6194         //        this.setValues(f.getValues());
6195         //    }, this);
6196         //}
6197         
6198         
6199         
6200         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
6201         if(asString === true){
6202             return fs;
6203         }
6204         return Roo.urlDecode(fs);
6205     },
6206     
6207     /**
6208      * Returns the fields in this form as an object with key/value pairs. 
6209      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
6210      * @return {Object}
6211      */
6212     getFieldValues : function(with_hidden)
6213     {
6214         var items = this.getItems();
6215         var ret = {};
6216         items.each(function(f){
6217             if (!f.getName()) {
6218                 return;
6219             }
6220             var v = f.getValue();
6221             if (f.inputType =='radio') {
6222                 if (typeof(ret[f.getName()]) == 'undefined') {
6223                     ret[f.getName()] = ''; // empty..
6224                 }
6225                 
6226                 if (!f.el.dom.checked) {
6227                     return;
6228                     
6229                 }
6230                 v = f.el.dom.value;
6231                 
6232             }
6233             
6234             // not sure if this supported any more..
6235             if ((typeof(v) == 'object') && f.getRawValue) {
6236                 v = f.getRawValue() ; // dates..
6237             }
6238             // combo boxes where name != hiddenName...
6239             if (f.name != f.getName()) {
6240                 ret[f.name] = f.getRawValue();
6241             }
6242             ret[f.getName()] = v;
6243         });
6244         
6245         return ret;
6246     },
6247
6248     /**
6249      * Clears all invalid messages in this form.
6250      * @return {BasicForm} this
6251      */
6252     clearInvalid : function(){
6253         var items = this.getItems();
6254         
6255         items.each(function(f){
6256            f.clearInvalid();
6257         });
6258         
6259         
6260         
6261         return this;
6262     },
6263
6264     /**
6265      * Resets this form.
6266      * @return {BasicForm} this
6267      */
6268     reset : function(){
6269         var items = this.getItems();
6270         items.each(function(f){
6271             f.reset();
6272         });
6273         
6274         Roo.each(this.childForms || [], function (f) {
6275             f.reset();
6276         });
6277        
6278         
6279         return this;
6280     },
6281     getItems : function()
6282     {
6283         var r=new Roo.util.MixedCollection(false, function(o){
6284             return o.id || (o.id = Roo.id());
6285         });
6286         var iter = function(el) {
6287             if (el.inputEl) {
6288                 r.add(el);
6289             }
6290             if (!el.items) {
6291                 return;
6292             }
6293             Roo.each(el.items,function(e) {
6294                 iter(e);
6295             });
6296             
6297             
6298         };
6299         iter(this);
6300         return r;
6301         
6302         
6303         
6304         
6305     }
6306     
6307 });
6308
6309  
6310 /*
6311  * Based on:
6312  * Ext JS Library 1.1.1
6313  * Copyright(c) 2006-2007, Ext JS, LLC.
6314  *
6315  * Originally Released Under LGPL - original licence link has changed is not relivant.
6316  *
6317  * Fork - LGPL
6318  * <script type="text/javascript">
6319  */
6320 /**
6321  * @class Roo.form.VTypes
6322  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
6323  * @singleton
6324  */
6325 Roo.form.VTypes = function(){
6326     // closure these in so they are only created once.
6327     var alpha = /^[a-zA-Z_]+$/;
6328     var alphanum = /^[a-zA-Z0-9_]+$/;
6329     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
6330     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
6331
6332     // All these messages and functions are configurable
6333     return {
6334         /**
6335          * The function used to validate email addresses
6336          * @param {String} value The email address
6337          */
6338         'email' : function(v){
6339             return email.test(v);
6340         },
6341         /**
6342          * The error text to display when the email validation function returns false
6343          * @type String
6344          */
6345         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
6346         /**
6347          * The keystroke filter mask to be applied on email input
6348          * @type RegExp
6349          */
6350         'emailMask' : /[a-z0-9_\.\-@]/i,
6351
6352         /**
6353          * The function used to validate URLs
6354          * @param {String} value The URL
6355          */
6356         'url' : function(v){
6357             return url.test(v);
6358         },
6359         /**
6360          * The error text to display when the url validation function returns false
6361          * @type String
6362          */
6363         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
6364         
6365         /**
6366          * The function used to validate alpha values
6367          * @param {String} value The value
6368          */
6369         'alpha' : function(v){
6370             return alpha.test(v);
6371         },
6372         /**
6373          * The error text to display when the alpha validation function returns false
6374          * @type String
6375          */
6376         'alphaText' : 'This field should only contain letters and _',
6377         /**
6378          * The keystroke filter mask to be applied on alpha input
6379          * @type RegExp
6380          */
6381         'alphaMask' : /[a-z_]/i,
6382
6383         /**
6384          * The function used to validate alphanumeric values
6385          * @param {String} value The value
6386          */
6387         'alphanum' : function(v){
6388             return alphanum.test(v);
6389         },
6390         /**
6391          * The error text to display when the alphanumeric validation function returns false
6392          * @type String
6393          */
6394         'alphanumText' : 'This field should only contain letters, numbers and _',
6395         /**
6396          * The keystroke filter mask to be applied on alphanumeric input
6397          * @type RegExp
6398          */
6399         'alphanumMask' : /[a-z0-9_]/i
6400     };
6401 }();/*
6402  * - LGPL
6403  *
6404  * Input
6405  * 
6406  */
6407
6408 /**
6409  * @class Roo.bootstrap.Input
6410  * @extends Roo.bootstrap.Component
6411  * Bootstrap Input class
6412  * @cfg {Boolean} disabled is it disabled
6413  * @cfg {String} fieldLabel - the label associated
6414  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
6415  * @cfg {String} name name of the input
6416  * @cfg {string} fieldLabel - the label associated
6417  * @cfg {string}  inputType - input / file submit ...
6418  * @cfg {string} placeholder - placeholder to put in text.
6419  * @cfg {string}  before - input group add on before
6420  * @cfg {string} after - input group add on after
6421  * @cfg {string} size - (lg|sm) or leave empty..
6422  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
6423  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
6424  * @cfg {Number} md colspan out of 12 for computer-sized screens
6425  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
6426  * @cfg {string} value default value of the input
6427  * @cfg {Number} labelWidth set the width of label (0-12)
6428  * @cfg {String} labelAlign (top|left)
6429  * @cfg {Boolean} readOnly Specifies that the field should be read-only
6430  * @cfg {String} align (left|center|right) Default left
6431  * 
6432  * 
6433  * @constructor
6434  * Create a new Input
6435  * @param {Object} config The config object
6436  */
6437
6438 Roo.bootstrap.Input = function(config){
6439     Roo.bootstrap.Input.superclass.constructor.call(this, config);
6440    
6441         this.addEvents({
6442             /**
6443              * @event focus
6444              * Fires when this field receives input focus.
6445              * @param {Roo.form.Field} this
6446              */
6447             focus : true,
6448             /**
6449              * @event blur
6450              * Fires when this field loses input focus.
6451              * @param {Roo.form.Field} this
6452              */
6453             blur : true,
6454             /**
6455              * @event specialkey
6456              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
6457              * {@link Roo.EventObject#getKey} to determine which key was pressed.
6458              * @param {Roo.form.Field} this
6459              * @param {Roo.EventObject} e The event object
6460              */
6461             specialkey : true,
6462             /**
6463              * @event change
6464              * Fires just before the field blurs if the field value has changed.
6465              * @param {Roo.form.Field} this
6466              * @param {Mixed} newValue The new value
6467              * @param {Mixed} oldValue The original value
6468              */
6469             change : true,
6470             /**
6471              * @event invalid
6472              * Fires after the field has been marked as invalid.
6473              * @param {Roo.form.Field} this
6474              * @param {String} msg The validation message
6475              */
6476             invalid : true,
6477             /**
6478              * @event valid
6479              * Fires after the field has been validated with no errors.
6480              * @param {Roo.form.Field} this
6481              */
6482             valid : true,
6483              /**
6484              * @event keyup
6485              * Fires after the key up
6486              * @param {Roo.form.Field} this
6487              * @param {Roo.EventObject}  e The event Object
6488              */
6489             keyup : true
6490         });
6491 };
6492
6493 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
6494      /**
6495      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
6496       automatic validation (defaults to "keyup").
6497      */
6498     validationEvent : "keyup",
6499      /**
6500      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
6501      */
6502     validateOnBlur : true,
6503     /**
6504      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
6505      */
6506     validationDelay : 250,
6507      /**
6508      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
6509      */
6510     focusClass : "x-form-focus",  // not needed???
6511     
6512        
6513     /**
6514      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
6515      */
6516     invalidClass : "has-error",
6517     
6518     /**
6519      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
6520      */
6521     selectOnFocus : false,
6522     
6523      /**
6524      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
6525      */
6526     maskRe : null,
6527        /**
6528      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
6529      */
6530     vtype : null,
6531     
6532       /**
6533      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
6534      */
6535     disableKeyFilter : false,
6536     
6537        /**
6538      * @cfg {Boolean} disabled True to disable the field (defaults to false).
6539      */
6540     disabled : false,
6541      /**
6542      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
6543      */
6544     allowBlank : true,
6545     /**
6546      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
6547      */
6548     blankText : "This field is required",
6549     
6550      /**
6551      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
6552      */
6553     minLength : 0,
6554     /**
6555      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
6556      */
6557     maxLength : Number.MAX_VALUE,
6558     /**
6559      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
6560      */
6561     minLengthText : "The minimum length for this field is {0}",
6562     /**
6563      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
6564      */
6565     maxLengthText : "The maximum length for this field is {0}",
6566   
6567     
6568     /**
6569      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
6570      * If available, this function will be called only after the basic validators all return true, and will be passed the
6571      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
6572      */
6573     validator : null,
6574     /**
6575      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
6576      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
6577      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
6578      */
6579     regex : null,
6580     /**
6581      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
6582      */
6583     regexText : "",
6584     
6585     
6586     
6587     fieldLabel : '',
6588     inputType : 'text',
6589     
6590     name : false,
6591     placeholder: false,
6592     before : false,
6593     after : false,
6594     size : false,
6595     // private
6596     hasFocus : false,
6597     preventMark: false,
6598     isFormField : true,
6599     value : '',
6600     labelWidth : 2,
6601     labelAlign : false,
6602     readOnly : false,
6603     align : false,
6604     
6605     parentLabelAlign : function()
6606     {
6607         var parent = this;
6608         while (parent.parent()) {
6609             parent = parent.parent();
6610             if (typeof(parent.labelAlign) !='undefined') {
6611                 return parent.labelAlign;
6612             }
6613         }
6614         return 'left';
6615         
6616     },
6617     
6618     getAutoCreate : function(){
6619         
6620         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
6621         
6622         var id = Roo.id();
6623         
6624         var cfg = {};
6625         
6626         if(this.inputType != 'hidden'){
6627             cfg.cls = 'form-group' //input-group
6628         }
6629         
6630         var input =  {
6631             tag: 'input',
6632             id : id,
6633             type : this.inputType,
6634             value : this.value,
6635             cls : 'form-control',
6636             placeholder : this.placeholder || ''
6637             
6638         };
6639         
6640         if(this.align){
6641             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
6642         }
6643         
6644         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
6645             input.maxLength = this.maxLength;
6646         }
6647         
6648         if (this.disabled) {
6649             input.disabled=true;
6650         }
6651         
6652         if (this.readOnly) {
6653             input.readonly=true;
6654         }
6655         
6656         if (this.name) {
6657             input.name = this.name;
6658         }
6659         if (this.size) {
6660             input.cls += ' input-' + this.size;
6661         }
6662         var settings=this;
6663         ['xs','sm','md','lg'].map(function(size){
6664             if (settings[size]) {
6665                 cfg.cls += ' col-' + size + '-' + settings[size];
6666             }
6667         });
6668         
6669         var inputblock = input;
6670         
6671         if (this.before || this.after) {
6672             
6673             inputblock = {
6674                 cls : 'input-group',
6675                 cn :  [] 
6676             };
6677             if (this.before && typeof(this.before) == 'string') {
6678                 
6679                 inputblock.cn.push({
6680                     tag :'span',
6681                     cls : 'roo-input-before input-group-addon',
6682                     html : this.before
6683                 });
6684             }
6685             if (this.before && typeof(this.before) == 'object') {
6686                 this.before = Roo.factory(this.before);
6687                 Roo.log(this.before);
6688                 inputblock.cn.push({
6689                     tag :'span',
6690                     cls : 'roo-input-before input-group-' +
6691                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
6692                 });
6693             }
6694             
6695             inputblock.cn.push(input);
6696             
6697             if (this.after && typeof(this.after) == 'string') {
6698                 inputblock.cn.push({
6699                     tag :'span',
6700                     cls : 'roo-input-after input-group-addon',
6701                     html : this.after
6702                 });
6703             }
6704             if (this.after && typeof(this.after) == 'object') {
6705                 this.after = Roo.factory(this.after);
6706                 Roo.log(this.after);
6707                 inputblock.cn.push({
6708                     tag :'span',
6709                     cls : 'roo-input-after input-group-' +
6710                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
6711                 });
6712             }
6713         };
6714         
6715         if (align ==='left' && this.fieldLabel.length) {
6716                 Roo.log("left and has label");
6717                 cfg.cn = [
6718                     
6719                     {
6720                         tag: 'label',
6721                         'for' :  id,
6722                         cls : 'control-label col-sm-' + this.labelWidth,
6723                         html : this.fieldLabel
6724                         
6725                     },
6726                     {
6727                         cls : "col-sm-" + (12 - this.labelWidth), 
6728                         cn: [
6729                             inputblock
6730                         ]
6731                     }
6732                     
6733                 ];
6734         } else if ( this.fieldLabel.length) {
6735                 Roo.log(" label");
6736                  cfg.cn = [
6737                    
6738                     {
6739                         tag: 'label',
6740                         //cls : 'input-group-addon',
6741                         html : this.fieldLabel
6742                         
6743                     },
6744                     
6745                     inputblock
6746                     
6747                 ];
6748
6749         } else {
6750             
6751                 Roo.log(" no label && no align");
6752                 cfg.cn = [
6753                     
6754                         inputblock
6755                     
6756                 ];
6757                 
6758                 
6759         };
6760         Roo.log('input-parentType: ' + this.parentType);
6761         
6762         if (this.parentType === 'Navbar' &&  this.parent().bar) {
6763            cfg.cls += ' navbar-form';
6764            Roo.log(cfg);
6765         }
6766         
6767         return cfg;
6768         
6769     },
6770     /**
6771      * return the real input element.
6772      */
6773     inputEl: function ()
6774     {
6775         return this.el.select('input.form-control',true).first();
6776     },
6777     setDisabled : function(v)
6778     {
6779         var i  = this.inputEl().dom;
6780         if (!v) {
6781             i.removeAttribute('disabled');
6782             return;
6783             
6784         }
6785         i.setAttribute('disabled','true');
6786     },
6787     initEvents : function()
6788     {
6789         
6790         this.inputEl().on("keydown" , this.fireKey,  this);
6791         this.inputEl().on("focus", this.onFocus,  this);
6792         this.inputEl().on("blur", this.onBlur,  this);
6793         
6794         this.inputEl().relayEvent('keyup', this);
6795
6796         // reference to original value for reset
6797         this.originalValue = this.getValue();
6798         //Roo.form.TextField.superclass.initEvents.call(this);
6799         if(this.validationEvent == 'keyup'){
6800             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
6801             this.inputEl().on('keyup', this.filterValidation, this);
6802         }
6803         else if(this.validationEvent !== false){
6804             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
6805         }
6806         
6807         if(this.selectOnFocus){
6808             this.on("focus", this.preFocus, this);
6809             
6810         }
6811         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
6812             this.inputEl().on("keypress", this.filterKeys, this);
6813         }
6814        /* if(this.grow){
6815             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
6816             this.el.on("click", this.autoSize,  this);
6817         }
6818         */
6819         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
6820             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
6821         }
6822         
6823         if (typeof(this.before) == 'object') {
6824             this.before.render(this.el.select('.roo-input-before',true).first());
6825         }
6826         if (typeof(this.after) == 'object') {
6827             this.after.render(this.el.select('.roo-input-after',true).first());
6828         }
6829         
6830         
6831     },
6832     filterValidation : function(e){
6833         if(!e.isNavKeyPress()){
6834             this.validationTask.delay(this.validationDelay);
6835         }
6836     },
6837      /**
6838      * Validates the field value
6839      * @return {Boolean} True if the value is valid, else false
6840      */
6841     validate : function(){
6842         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
6843         if(this.disabled || this.validateValue(this.getRawValue())){
6844             this.clearInvalid();
6845             return true;
6846         }
6847         return false;
6848     },
6849     
6850     
6851     /**
6852      * Validates a value according to the field's validation rules and marks the field as invalid
6853      * if the validation fails
6854      * @param {Mixed} value The value to validate
6855      * @return {Boolean} True if the value is valid, else false
6856      */
6857     validateValue : function(value){
6858         if(value.length < 1)  { // if it's blank
6859              if(this.allowBlank){
6860                 this.clearInvalid();
6861                 return true;
6862              }else{
6863                 this.markInvalid(this.blankText);
6864                 return false;
6865              }
6866         }
6867         if(value.length < this.minLength){
6868             this.markInvalid(String.format(this.minLengthText, this.minLength));
6869             return false;
6870         }
6871         if(value.length > this.maxLength){
6872             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
6873             return false;
6874         }
6875         if(this.vtype){
6876             var vt = Roo.form.VTypes;
6877             if(!vt[this.vtype](value, this)){
6878                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
6879                 return false;
6880             }
6881         }
6882         if(typeof this.validator == "function"){
6883             var msg = this.validator(value);
6884             if(msg !== true){
6885                 this.markInvalid(msg);
6886                 return false;
6887             }
6888         }
6889         if(this.regex && !this.regex.test(value)){
6890             this.markInvalid(this.regexText);
6891             return false;
6892         }
6893         return true;
6894     },
6895
6896     
6897     
6898      // private
6899     fireKey : function(e){
6900         //Roo.log('field ' + e.getKey());
6901         if(e.isNavKeyPress()){
6902             this.fireEvent("specialkey", this, e);
6903         }
6904     },
6905     focus : function (selectText){
6906         if(this.rendered){
6907             this.inputEl().focus();
6908             if(selectText === true){
6909                 this.inputEl().dom.select();
6910             }
6911         }
6912         return this;
6913     } ,
6914     
6915     onFocus : function(){
6916         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
6917            // this.el.addClass(this.focusClass);
6918         }
6919         if(!this.hasFocus){
6920             this.hasFocus = true;
6921             this.startValue = this.getValue();
6922             this.fireEvent("focus", this);
6923         }
6924     },
6925     
6926     beforeBlur : Roo.emptyFn,
6927
6928     
6929     // private
6930     onBlur : function(){
6931         this.beforeBlur();
6932         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
6933             //this.el.removeClass(this.focusClass);
6934         }
6935         this.hasFocus = false;
6936         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
6937             this.validate();
6938         }
6939         var v = this.getValue();
6940         if(String(v) !== String(this.startValue)){
6941             this.fireEvent('change', this, v, this.startValue);
6942         }
6943         this.fireEvent("blur", this);
6944     },
6945     
6946     /**
6947      * Resets the current field value to the originally loaded value and clears any validation messages
6948      */
6949     reset : function(){
6950         this.setValue(this.originalValue);
6951         this.clearInvalid();
6952     },
6953      /**
6954      * Returns the name of the field
6955      * @return {Mixed} name The name field
6956      */
6957     getName: function(){
6958         return this.name;
6959     },
6960      /**
6961      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
6962      * @return {Mixed} value The field value
6963      */
6964     getValue : function(){
6965         return this.inputEl().getValue();
6966     },
6967     /**
6968      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
6969      * @return {Mixed} value The field value
6970      */
6971     getRawValue : function(){
6972         var v = this.inputEl().getValue();
6973         
6974         return v;
6975     },
6976     
6977     /**
6978      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
6979      * @param {Mixed} value The value to set
6980      */
6981     setRawValue : function(v){
6982         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
6983     },
6984     
6985     selectText : function(start, end){
6986         var v = this.getRawValue();
6987         if(v.length > 0){
6988             start = start === undefined ? 0 : start;
6989             end = end === undefined ? v.length : end;
6990             var d = this.inputEl().dom;
6991             if(d.setSelectionRange){
6992                 d.setSelectionRange(start, end);
6993             }else if(d.createTextRange){
6994                 var range = d.createTextRange();
6995                 range.moveStart("character", start);
6996                 range.moveEnd("character", v.length-end);
6997                 range.select();
6998             }
6999         }
7000     },
7001     
7002     /**
7003      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
7004      * @param {Mixed} value The value to set
7005      */
7006     setValue : function(v){
7007         this.value = v;
7008         if(this.rendered){
7009             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7010             this.validate();
7011         }
7012     },
7013     
7014     /*
7015     processValue : function(value){
7016         if(this.stripCharsRe){
7017             var newValue = value.replace(this.stripCharsRe, '');
7018             if(newValue !== value){
7019                 this.setRawValue(newValue);
7020                 return newValue;
7021             }
7022         }
7023         return value;
7024     },
7025   */
7026     preFocus : function(){
7027         
7028         if(this.selectOnFocus){
7029             this.inputEl().dom.select();
7030         }
7031     },
7032     filterKeys : function(e){
7033         var k = e.getKey();
7034         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
7035             return;
7036         }
7037         var c = e.getCharCode(), cc = String.fromCharCode(c);
7038         if(Roo.isIE && (e.isSpecialKey() || !cc)){
7039             return;
7040         }
7041         if(!this.maskRe.test(cc)){
7042             e.stopEvent();
7043         }
7044     },
7045      /**
7046      * Clear any invalid styles/messages for this field
7047      */
7048     clearInvalid : function(){
7049         
7050         if(!this.el || this.preventMark){ // not rendered
7051             return;
7052         }
7053         this.el.removeClass(this.invalidClass);
7054         /*
7055         switch(this.msgTarget){
7056             case 'qtip':
7057                 this.el.dom.qtip = '';
7058                 break;
7059             case 'title':
7060                 this.el.dom.title = '';
7061                 break;
7062             case 'under':
7063                 if(this.errorEl){
7064                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
7065                 }
7066                 break;
7067             case 'side':
7068                 if(this.errorIcon){
7069                     this.errorIcon.dom.qtip = '';
7070                     this.errorIcon.hide();
7071                     this.un('resize', this.alignErrorIcon, this);
7072                 }
7073                 break;
7074             default:
7075                 var t = Roo.getDom(this.msgTarget);
7076                 t.innerHTML = '';
7077                 t.style.display = 'none';
7078                 break;
7079         }
7080         */
7081         this.fireEvent('valid', this);
7082     },
7083      /**
7084      * Mark this field as invalid
7085      * @param {String} msg The validation message
7086      */
7087     markInvalid : function(msg){
7088         if(!this.el  || this.preventMark){ // not rendered
7089             return;
7090         }
7091         this.el.addClass(this.invalidClass);
7092         /*
7093         msg = msg || this.invalidText;
7094         switch(this.msgTarget){
7095             case 'qtip':
7096                 this.el.dom.qtip = msg;
7097                 this.el.dom.qclass = 'x-form-invalid-tip';
7098                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
7099                     Roo.QuickTips.enable();
7100                 }
7101                 break;
7102             case 'title':
7103                 this.el.dom.title = msg;
7104                 break;
7105             case 'under':
7106                 if(!this.errorEl){
7107                     var elp = this.el.findParent('.x-form-element', 5, true);
7108                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
7109                     this.errorEl.setWidth(elp.getWidth(true)-20);
7110                 }
7111                 this.errorEl.update(msg);
7112                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
7113                 break;
7114             case 'side':
7115                 if(!this.errorIcon){
7116                     var elp = this.el.findParent('.x-form-element', 5, true);
7117                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
7118                 }
7119                 this.alignErrorIcon();
7120                 this.errorIcon.dom.qtip = msg;
7121                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
7122                 this.errorIcon.show();
7123                 this.on('resize', this.alignErrorIcon, this);
7124                 break;
7125             default:
7126                 var t = Roo.getDom(this.msgTarget);
7127                 t.innerHTML = msg;
7128                 t.style.display = this.msgDisplay;
7129                 break;
7130         }
7131         */
7132         this.fireEvent('invalid', this, msg);
7133     },
7134     // private
7135     SafariOnKeyDown : function(event)
7136     {
7137         // this is a workaround for a password hang bug on chrome/ webkit.
7138         
7139         var isSelectAll = false;
7140         
7141         if(this.inputEl().dom.selectionEnd > 0){
7142             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
7143         }
7144         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
7145             event.preventDefault();
7146             this.setValue('');
7147             return;
7148         }
7149         
7150         if(isSelectAll){ // backspace and delete key
7151             
7152             event.preventDefault();
7153             // this is very hacky as keydown always get's upper case.
7154             //
7155             var cc = String.fromCharCode(event.getCharCode());
7156             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
7157             
7158         }
7159     },
7160     adjustWidth : function(tag, w){
7161         tag = tag.toLowerCase();
7162         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
7163             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
7164                 if(tag == 'input'){
7165                     return w + 2;
7166                 }
7167                 if(tag == 'textarea'){
7168                     return w-2;
7169                 }
7170             }else if(Roo.isOpera){
7171                 if(tag == 'input'){
7172                     return w + 2;
7173                 }
7174                 if(tag == 'textarea'){
7175                     return w-2;
7176                 }
7177             }
7178         }
7179         return w;
7180     }
7181     
7182 });
7183
7184  
7185 /*
7186  * - LGPL
7187  *
7188  * Input
7189  * 
7190  */
7191
7192 /**
7193  * @class Roo.bootstrap.TextArea
7194  * @extends Roo.bootstrap.Input
7195  * Bootstrap TextArea class
7196  * @cfg {Number} cols Specifies the visible width of a text area
7197  * @cfg {Number} rows Specifies the visible number of lines in a text area
7198  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
7199  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
7200  * @cfg {string} html text
7201  * 
7202  * @constructor
7203  * Create a new TextArea
7204  * @param {Object} config The config object
7205  */
7206
7207 Roo.bootstrap.TextArea = function(config){
7208     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
7209    
7210 };
7211
7212 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
7213      
7214     cols : false,
7215     rows : 5,
7216     readOnly : false,
7217     warp : 'soft',
7218     resize : false,
7219     value: false,
7220     html: false,
7221     
7222     getAutoCreate : function(){
7223         
7224         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7225         
7226         var id = Roo.id();
7227         
7228         var cfg = {};
7229         
7230         var input =  {
7231             tag: 'textarea',
7232             id : id,
7233             warp : this.warp,
7234             rows : this.rows,
7235             value : this.value || '',
7236             html: this.html || '',
7237             cls : 'form-control',
7238             placeholder : this.placeholder || '' 
7239             
7240         };
7241         
7242         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7243             input.maxLength = this.maxLength;
7244         }
7245         
7246         if(this.resize){
7247             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
7248         }
7249         
7250         if(this.cols){
7251             input.cols = this.cols;
7252         }
7253         
7254         if (this.readOnly) {
7255             input.readonly = true;
7256         }
7257         
7258         if (this.name) {
7259             input.name = this.name;
7260         }
7261         
7262         if (this.size) {
7263             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
7264         }
7265         
7266         var settings=this;
7267         ['xs','sm','md','lg'].map(function(size){
7268             if (settings[size]) {
7269                 cfg.cls += ' col-' + size + '-' + settings[size];
7270             }
7271         });
7272         
7273         var inputblock = input;
7274         
7275         if (this.before || this.after) {
7276             
7277             inputblock = {
7278                 cls : 'input-group',
7279                 cn :  [] 
7280             };
7281             if (this.before) {
7282                 inputblock.cn.push({
7283                     tag :'span',
7284                     cls : 'input-group-addon',
7285                     html : this.before
7286                 });
7287             }
7288             inputblock.cn.push(input);
7289             if (this.after) {
7290                 inputblock.cn.push({
7291                     tag :'span',
7292                     cls : 'input-group-addon',
7293                     html : this.after
7294                 });
7295             }
7296             
7297         }
7298         
7299         if (align ==='left' && this.fieldLabel.length) {
7300                 Roo.log("left and has label");
7301                 cfg.cn = [
7302                     
7303                     {
7304                         tag: 'label',
7305                         'for' :  id,
7306                         cls : 'control-label col-sm-' + this.labelWidth,
7307                         html : this.fieldLabel
7308                         
7309                     },
7310                     {
7311                         cls : "col-sm-" + (12 - this.labelWidth), 
7312                         cn: [
7313                             inputblock
7314                         ]
7315                     }
7316                     
7317                 ];
7318         } else if ( this.fieldLabel.length) {
7319                 Roo.log(" label");
7320                  cfg.cn = [
7321                    
7322                     {
7323                         tag: 'label',
7324                         //cls : 'input-group-addon',
7325                         html : this.fieldLabel
7326                         
7327                     },
7328                     
7329                     inputblock
7330                     
7331                 ];
7332
7333         } else {
7334             
7335                    Roo.log(" no label && no align");
7336                 cfg.cn = [
7337                     
7338                         inputblock
7339                     
7340                 ];
7341                 
7342                 
7343         }
7344         
7345         if (this.disabled) {
7346             input.disabled=true;
7347         }
7348         
7349         return cfg;
7350         
7351     },
7352     /**
7353      * return the real textarea element.
7354      */
7355     inputEl: function ()
7356     {
7357         return this.el.select('textarea.form-control',true).first();
7358     }
7359 });
7360
7361  
7362 /*
7363  * - LGPL
7364  *
7365  * trigger field - base class for combo..
7366  * 
7367  */
7368  
7369 /**
7370  * @class Roo.bootstrap.TriggerField
7371  * @extends Roo.bootstrap.Input
7372  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
7373  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
7374  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
7375  * for which you can provide a custom implementation.  For example:
7376  * <pre><code>
7377 var trigger = new Roo.bootstrap.TriggerField();
7378 trigger.onTriggerClick = myTriggerFn;
7379 trigger.applyTo('my-field');
7380 </code></pre>
7381  *
7382  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
7383  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
7384  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
7385  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
7386  * @constructor
7387  * Create a new TriggerField.
7388  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
7389  * to the base TextField)
7390  */
7391 Roo.bootstrap.TriggerField = function(config){
7392     this.mimicing = false;
7393     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
7394 };
7395
7396 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
7397     /**
7398      * @cfg {String} triggerClass A CSS class to apply to the trigger
7399      */
7400      /**
7401      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
7402      */
7403     hideTrigger:false,
7404
7405     /** @cfg {Boolean} grow @hide */
7406     /** @cfg {Number} growMin @hide */
7407     /** @cfg {Number} growMax @hide */
7408
7409     /**
7410      * @hide 
7411      * @method
7412      */
7413     autoSize: Roo.emptyFn,
7414     // private
7415     monitorTab : true,
7416     // private
7417     deferHeight : true,
7418
7419     
7420     actionMode : 'wrap',
7421     
7422     
7423     
7424     getAutoCreate : function(){
7425        
7426         var parent = this.parent();
7427         
7428         var align = this.labelAlign || this.parentLabelAlign();
7429         
7430         var id = Roo.id();
7431         
7432         var cfg = {
7433             cls: 'form-group' //input-group
7434         };
7435         
7436         
7437         var input =  {
7438             tag: 'input',
7439             id : id,
7440             type : this.inputType,
7441             cls : 'form-control',
7442             autocomplete: 'off',
7443             placeholder : this.placeholder || '' 
7444             
7445         };
7446         if (this.name) {
7447             input.name = this.name;
7448         }
7449         if (this.size) {
7450             input.cls += ' input-' + this.size;
7451         }
7452         
7453         if (this.disabled) {
7454             input.disabled=true;
7455         }
7456         
7457         var inputblock = input;
7458         
7459         if (this.before || this.after) {
7460             
7461             inputblock = {
7462                 cls : 'input-group',
7463                 cn :  [] 
7464             };
7465             if (this.before) {
7466                 inputblock.cn.push({
7467                     tag :'span',
7468                     cls : 'input-group-addon',
7469                     html : this.before
7470                 });
7471             }
7472             inputblock.cn.push(input);
7473             if (this.after) {
7474                 inputblock.cn.push({
7475                     tag :'span',
7476                     cls : 'input-group-addon',
7477                     html : this.after
7478                 });
7479             }
7480             
7481         };
7482         
7483         var box = {
7484             tag: 'div',
7485             cn: [
7486                 {
7487                     tag: 'input',
7488                     type : 'hidden',
7489                     cls: 'form-hidden-field'
7490                 },
7491                 inputblock
7492             ]
7493             
7494         };
7495         
7496         if(this.multiple){
7497             Roo.log('multiple');
7498             
7499             box = {
7500                 tag: 'div',
7501                 cn: [
7502                     {
7503                         tag: 'input',
7504                         type : 'hidden',
7505                         cls: 'form-hidden-field'
7506                     },
7507                     {
7508                         tag: 'ul',
7509                         cls: 'select2-choices',
7510                         cn:[
7511                             {
7512                                 tag: 'li',
7513                                 cls: 'select2-search-field',
7514                                 cn: [
7515
7516                                     inputblock
7517                                 ]
7518                             }
7519                         ]
7520                     }
7521                 ]
7522             }
7523         };
7524         
7525         var combobox = {
7526             cls: 'select2-container input-group',
7527             cn: [
7528                 box,
7529                 {
7530                     tag: 'ul',
7531                     cls: 'typeahead typeahead-long dropdown-menu',
7532                     style: 'display:none'
7533                 }
7534             ]
7535         };
7536         
7537         if(!this.multiple){
7538             combobox.cn.push({
7539                 tag :'span',
7540                 cls : 'input-group-addon btn dropdown-toggle',
7541                 cn : [
7542                     {
7543                         tag: 'span',
7544                         cls: 'caret'
7545                     },
7546                     {
7547                         tag: 'span',
7548                         cls: 'combobox-clear',
7549                         cn  : [
7550                             {
7551                                 tag : 'i',
7552                                 cls: 'icon-remove'
7553                             }
7554                         ]
7555                     }
7556                 ]
7557
7558             })
7559         }
7560         
7561         if(this.multiple){
7562             combobox.cls += ' select2-container-multi';
7563         }
7564         
7565         if (align ==='left' && this.fieldLabel.length) {
7566             
7567                 Roo.log("left and has label");
7568                 cfg.cn = [
7569                     
7570                     {
7571                         tag: 'label',
7572                         'for' :  id,
7573                         cls : 'control-label col-sm-' + this.labelWidth,
7574                         html : this.fieldLabel
7575                         
7576                     },
7577                     {
7578                         cls : "col-sm-" + (12 - this.labelWidth), 
7579                         cn: [
7580                             combobox
7581                         ]
7582                     }
7583                     
7584                 ];
7585         } else if ( this.fieldLabel.length) {
7586                 Roo.log(" label");
7587                  cfg.cn = [
7588                    
7589                     {
7590                         tag: 'label',
7591                         //cls : 'input-group-addon',
7592                         html : this.fieldLabel
7593                         
7594                     },
7595                     
7596                     combobox
7597                     
7598                 ];
7599
7600         } else {
7601             
7602                 Roo.log(" no label && no align");
7603                 cfg = combobox
7604                      
7605                 
7606         }
7607          
7608         var settings=this;
7609         ['xs','sm','md','lg'].map(function(size){
7610             if (settings[size]) {
7611                 cfg.cls += ' col-' + size + '-' + settings[size];
7612             }
7613         });
7614         
7615         return cfg;
7616         
7617     },
7618     
7619     
7620     
7621     // private
7622     onResize : function(w, h){
7623 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
7624 //        if(typeof w == 'number'){
7625 //            var x = w - this.trigger.getWidth();
7626 //            this.inputEl().setWidth(this.adjustWidth('input', x));
7627 //            this.trigger.setStyle('left', x+'px');
7628 //        }
7629     },
7630
7631     // private
7632     adjustSize : Roo.BoxComponent.prototype.adjustSize,
7633
7634     // private
7635     getResizeEl : function(){
7636         return this.inputEl();
7637     },
7638
7639     // private
7640     getPositionEl : function(){
7641         return this.inputEl();
7642     },
7643
7644     // private
7645     alignErrorIcon : function(){
7646         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
7647     },
7648
7649     // private
7650     initEvents : function(){
7651         
7652         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
7653         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
7654         if(!this.multiple){
7655             this.trigger = this.el.select('span.dropdown-toggle',true).first();
7656             if(this.hideTrigger){
7657                 this.trigger.setDisplayed(false);
7658             }
7659             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
7660         }
7661         
7662         if(this.multiple){
7663             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
7664         }
7665         
7666         //this.trigger.addClassOnOver('x-form-trigger-over');
7667         //this.trigger.addClassOnClick('x-form-trigger-click');
7668         
7669         //if(!this.width){
7670         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
7671         //}
7672     },
7673
7674     // private
7675     initTrigger : function(){
7676        
7677     },
7678
7679     // private
7680     onDestroy : function(){
7681         if(this.trigger){
7682             this.trigger.removeAllListeners();
7683           //  this.trigger.remove();
7684         }
7685         //if(this.wrap){
7686         //    this.wrap.remove();
7687         //}
7688         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
7689     },
7690
7691     // private
7692     onFocus : function(){
7693         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
7694         /*
7695         if(!this.mimicing){
7696             this.wrap.addClass('x-trigger-wrap-focus');
7697             this.mimicing = true;
7698             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
7699             if(this.monitorTab){
7700                 this.el.on("keydown", this.checkTab, this);
7701             }
7702         }
7703         */
7704     },
7705
7706     // private
7707     checkTab : function(e){
7708         if(e.getKey() == e.TAB){
7709             this.triggerBlur();
7710         }
7711     },
7712
7713     // private
7714     onBlur : function(){
7715         // do nothing
7716     },
7717
7718     // private
7719     mimicBlur : function(e, t){
7720         /*
7721         if(!this.wrap.contains(t) && this.validateBlur()){
7722             this.triggerBlur();
7723         }
7724         */
7725     },
7726
7727     // private
7728     triggerBlur : function(){
7729         this.mimicing = false;
7730         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
7731         if(this.monitorTab){
7732             this.el.un("keydown", this.checkTab, this);
7733         }
7734         //this.wrap.removeClass('x-trigger-wrap-focus');
7735         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
7736     },
7737
7738     // private
7739     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
7740     validateBlur : function(e, t){
7741         return true;
7742     },
7743
7744     // private
7745     onDisable : function(){
7746         this.inputEl().dom.disabled = true;
7747         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
7748         //if(this.wrap){
7749         //    this.wrap.addClass('x-item-disabled');
7750         //}
7751     },
7752
7753     // private
7754     onEnable : function(){
7755         this.inputEl().dom.disabled = false;
7756         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
7757         //if(this.wrap){
7758         //    this.el.removeClass('x-item-disabled');
7759         //}
7760     },
7761
7762     // private
7763     onShow : function(){
7764         var ae = this.getActionEl();
7765         
7766         if(ae){
7767             ae.dom.style.display = '';
7768             ae.dom.style.visibility = 'visible';
7769         }
7770     },
7771
7772     // private
7773     
7774     onHide : function(){
7775         var ae = this.getActionEl();
7776         ae.dom.style.display = 'none';
7777     },
7778
7779     /**
7780      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
7781      * by an implementing function.
7782      * @method
7783      * @param {EventObject} e
7784      */
7785     onTriggerClick : Roo.emptyFn
7786 });
7787  /*
7788  * Based on:
7789  * Ext JS Library 1.1.1
7790  * Copyright(c) 2006-2007, Ext JS, LLC.
7791  *
7792  * Originally Released Under LGPL - original licence link has changed is not relivant.
7793  *
7794  * Fork - LGPL
7795  * <script type="text/javascript">
7796  */
7797
7798
7799 /**
7800  * @class Roo.data.SortTypes
7801  * @singleton
7802  * Defines the default sorting (casting?) comparison functions used when sorting data.
7803  */
7804 Roo.data.SortTypes = {
7805     /**
7806      * Default sort that does nothing
7807      * @param {Mixed} s The value being converted
7808      * @return {Mixed} The comparison value
7809      */
7810     none : function(s){
7811         return s;
7812     },
7813     
7814     /**
7815      * The regular expression used to strip tags
7816      * @type {RegExp}
7817      * @property
7818      */
7819     stripTagsRE : /<\/?[^>]+>/gi,
7820     
7821     /**
7822      * Strips all HTML tags to sort on text only
7823      * @param {Mixed} s The value being converted
7824      * @return {String} The comparison value
7825      */
7826     asText : function(s){
7827         return String(s).replace(this.stripTagsRE, "");
7828     },
7829     
7830     /**
7831      * Strips all HTML tags to sort on text only - Case insensitive
7832      * @param {Mixed} s The value being converted
7833      * @return {String} The comparison value
7834      */
7835     asUCText : function(s){
7836         return String(s).toUpperCase().replace(this.stripTagsRE, "");
7837     },
7838     
7839     /**
7840      * Case insensitive string
7841      * @param {Mixed} s The value being converted
7842      * @return {String} The comparison value
7843      */
7844     asUCString : function(s) {
7845         return String(s).toUpperCase();
7846     },
7847     
7848     /**
7849      * Date sorting
7850      * @param {Mixed} s The value being converted
7851      * @return {Number} The comparison value
7852      */
7853     asDate : function(s) {
7854         if(!s){
7855             return 0;
7856         }
7857         if(s instanceof Date){
7858             return s.getTime();
7859         }
7860         return Date.parse(String(s));
7861     },
7862     
7863     /**
7864      * Float sorting
7865      * @param {Mixed} s The value being converted
7866      * @return {Float} The comparison value
7867      */
7868     asFloat : function(s) {
7869         var val = parseFloat(String(s).replace(/,/g, ""));
7870         if(isNaN(val)) val = 0;
7871         return val;
7872     },
7873     
7874     /**
7875      * Integer sorting
7876      * @param {Mixed} s The value being converted
7877      * @return {Number} The comparison value
7878      */
7879     asInt : function(s) {
7880         var val = parseInt(String(s).replace(/,/g, ""));
7881         if(isNaN(val)) val = 0;
7882         return val;
7883     }
7884 };/*
7885  * Based on:
7886  * Ext JS Library 1.1.1
7887  * Copyright(c) 2006-2007, Ext JS, LLC.
7888  *
7889  * Originally Released Under LGPL - original licence link has changed is not relivant.
7890  *
7891  * Fork - LGPL
7892  * <script type="text/javascript">
7893  */
7894
7895 /**
7896 * @class Roo.data.Record
7897  * Instances of this class encapsulate both record <em>definition</em> information, and record
7898  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
7899  * to access Records cached in an {@link Roo.data.Store} object.<br>
7900  * <p>
7901  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
7902  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
7903  * objects.<br>
7904  * <p>
7905  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
7906  * @constructor
7907  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
7908  * {@link #create}. The parameters are the same.
7909  * @param {Array} data An associative Array of data values keyed by the field name.
7910  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
7911  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
7912  * not specified an integer id is generated.
7913  */
7914 Roo.data.Record = function(data, id){
7915     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
7916     this.data = data;
7917 };
7918
7919 /**
7920  * Generate a constructor for a specific record layout.
7921  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
7922  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
7923  * Each field definition object may contain the following properties: <ul>
7924  * <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,
7925  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
7926  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
7927  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
7928  * is being used, then this is a string containing the javascript expression to reference the data relative to 
7929  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
7930  * to the data item relative to the record element. If the mapping expression is the same as the field name,
7931  * this may be omitted.</p></li>
7932  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
7933  * <ul><li>auto (Default, implies no conversion)</li>
7934  * <li>string</li>
7935  * <li>int</li>
7936  * <li>float</li>
7937  * <li>boolean</li>
7938  * <li>date</li></ul></p></li>
7939  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
7940  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
7941  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
7942  * by the Reader into an object that will be stored in the Record. It is passed the
7943  * following parameters:<ul>
7944  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
7945  * </ul></p></li>
7946  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
7947  * </ul>
7948  * <br>usage:<br><pre><code>
7949 var TopicRecord = Roo.data.Record.create(
7950     {name: 'title', mapping: 'topic_title'},
7951     {name: 'author', mapping: 'username'},
7952     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
7953     {name: 'lastPost', mapping: 'post_time', type: 'date'},
7954     {name: 'lastPoster', mapping: 'user2'},
7955     {name: 'excerpt', mapping: 'post_text'}
7956 );
7957
7958 var myNewRecord = new TopicRecord({
7959     title: 'Do my job please',
7960     author: 'noobie',
7961     totalPosts: 1,
7962     lastPost: new Date(),
7963     lastPoster: 'Animal',
7964     excerpt: 'No way dude!'
7965 });
7966 myStore.add(myNewRecord);
7967 </code></pre>
7968  * @method create
7969  * @static
7970  */
7971 Roo.data.Record.create = function(o){
7972     var f = function(){
7973         f.superclass.constructor.apply(this, arguments);
7974     };
7975     Roo.extend(f, Roo.data.Record);
7976     var p = f.prototype;
7977     p.fields = new Roo.util.MixedCollection(false, function(field){
7978         return field.name;
7979     });
7980     for(var i = 0, len = o.length; i < len; i++){
7981         p.fields.add(new Roo.data.Field(o[i]));
7982     }
7983     f.getField = function(name){
7984         return p.fields.get(name);  
7985     };
7986     return f;
7987 };
7988
7989 Roo.data.Record.AUTO_ID = 1000;
7990 Roo.data.Record.EDIT = 'edit';
7991 Roo.data.Record.REJECT = 'reject';
7992 Roo.data.Record.COMMIT = 'commit';
7993
7994 Roo.data.Record.prototype = {
7995     /**
7996      * Readonly flag - true if this record has been modified.
7997      * @type Boolean
7998      */
7999     dirty : false,
8000     editing : false,
8001     error: null,
8002     modified: null,
8003
8004     // private
8005     join : function(store){
8006         this.store = store;
8007     },
8008
8009     /**
8010      * Set the named field to the specified value.
8011      * @param {String} name The name of the field to set.
8012      * @param {Object} value The value to set the field to.
8013      */
8014     set : function(name, value){
8015         if(this.data[name] == value){
8016             return;
8017         }
8018         this.dirty = true;
8019         if(!this.modified){
8020             this.modified = {};
8021         }
8022         if(typeof this.modified[name] == 'undefined'){
8023             this.modified[name] = this.data[name];
8024         }
8025         this.data[name] = value;
8026         if(!this.editing && this.store){
8027             this.store.afterEdit(this);
8028         }       
8029     },
8030
8031     /**
8032      * Get the value of the named field.
8033      * @param {String} name The name of the field to get the value of.
8034      * @return {Object} The value of the field.
8035      */
8036     get : function(name){
8037         return this.data[name]; 
8038     },
8039
8040     // private
8041     beginEdit : function(){
8042         this.editing = true;
8043         this.modified = {}; 
8044     },
8045
8046     // private
8047     cancelEdit : function(){
8048         this.editing = false;
8049         delete this.modified;
8050     },
8051
8052     // private
8053     endEdit : function(){
8054         this.editing = false;
8055         if(this.dirty && this.store){
8056             this.store.afterEdit(this);
8057         }
8058     },
8059
8060     /**
8061      * Usually called by the {@link Roo.data.Store} which owns the Record.
8062      * Rejects all changes made to the Record since either creation, or the last commit operation.
8063      * Modified fields are reverted to their original values.
8064      * <p>
8065      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8066      * of reject operations.
8067      */
8068     reject : function(){
8069         var m = this.modified;
8070         for(var n in m){
8071             if(typeof m[n] != "function"){
8072                 this.data[n] = m[n];
8073             }
8074         }
8075         this.dirty = false;
8076         delete this.modified;
8077         this.editing = false;
8078         if(this.store){
8079             this.store.afterReject(this);
8080         }
8081     },
8082
8083     /**
8084      * Usually called by the {@link Roo.data.Store} which owns the Record.
8085      * Commits all changes made to the Record since either creation, or the last commit operation.
8086      * <p>
8087      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8088      * of commit operations.
8089      */
8090     commit : function(){
8091         this.dirty = false;
8092         delete this.modified;
8093         this.editing = false;
8094         if(this.store){
8095             this.store.afterCommit(this);
8096         }
8097     },
8098
8099     // private
8100     hasError : function(){
8101         return this.error != null;
8102     },
8103
8104     // private
8105     clearError : function(){
8106         this.error = null;
8107     },
8108
8109     /**
8110      * Creates a copy of this record.
8111      * @param {String} id (optional) A new record id if you don't want to use this record's id
8112      * @return {Record}
8113      */
8114     copy : function(newId) {
8115         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
8116     }
8117 };/*
8118  * Based on:
8119  * Ext JS Library 1.1.1
8120  * Copyright(c) 2006-2007, Ext JS, LLC.
8121  *
8122  * Originally Released Under LGPL - original licence link has changed is not relivant.
8123  *
8124  * Fork - LGPL
8125  * <script type="text/javascript">
8126  */
8127
8128
8129
8130 /**
8131  * @class Roo.data.Store
8132  * @extends Roo.util.Observable
8133  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
8134  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
8135  * <p>
8136  * 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
8137  * has no knowledge of the format of the data returned by the Proxy.<br>
8138  * <p>
8139  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
8140  * instances from the data object. These records are cached and made available through accessor functions.
8141  * @constructor
8142  * Creates a new Store.
8143  * @param {Object} config A config object containing the objects needed for the Store to access data,
8144  * and read the data into Records.
8145  */
8146 Roo.data.Store = function(config){
8147     this.data = new Roo.util.MixedCollection(false);
8148     this.data.getKey = function(o){
8149         return o.id;
8150     };
8151     this.baseParams = {};
8152     // private
8153     this.paramNames = {
8154         "start" : "start",
8155         "limit" : "limit",
8156         "sort" : "sort",
8157         "dir" : "dir",
8158         "multisort" : "_multisort"
8159     };
8160
8161     if(config && config.data){
8162         this.inlineData = config.data;
8163         delete config.data;
8164     }
8165
8166     Roo.apply(this, config);
8167     
8168     if(this.reader){ // reader passed
8169         this.reader = Roo.factory(this.reader, Roo.data);
8170         this.reader.xmodule = this.xmodule || false;
8171         if(!this.recordType){
8172             this.recordType = this.reader.recordType;
8173         }
8174         if(this.reader.onMetaChange){
8175             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
8176         }
8177     }
8178
8179     if(this.recordType){
8180         this.fields = this.recordType.prototype.fields;
8181     }
8182     this.modified = [];
8183
8184     this.addEvents({
8185         /**
8186          * @event datachanged
8187          * Fires when the data cache has changed, and a widget which is using this Store
8188          * as a Record cache should refresh its view.
8189          * @param {Store} this
8190          */
8191         datachanged : true,
8192         /**
8193          * @event metachange
8194          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
8195          * @param {Store} this
8196          * @param {Object} meta The JSON metadata
8197          */
8198         metachange : true,
8199         /**
8200          * @event add
8201          * Fires when Records have been added to the Store
8202          * @param {Store} this
8203          * @param {Roo.data.Record[]} records The array of Records added
8204          * @param {Number} index The index at which the record(s) were added
8205          */
8206         add : true,
8207         /**
8208          * @event remove
8209          * Fires when a Record has been removed from the Store
8210          * @param {Store} this
8211          * @param {Roo.data.Record} record The Record that was removed
8212          * @param {Number} index The index at which the record was removed
8213          */
8214         remove : true,
8215         /**
8216          * @event update
8217          * Fires when a Record has been updated
8218          * @param {Store} this
8219          * @param {Roo.data.Record} record The Record that was updated
8220          * @param {String} operation The update operation being performed.  Value may be one of:
8221          * <pre><code>
8222  Roo.data.Record.EDIT
8223  Roo.data.Record.REJECT
8224  Roo.data.Record.COMMIT
8225          * </code></pre>
8226          */
8227         update : true,
8228         /**
8229          * @event clear
8230          * Fires when the data cache has been cleared.
8231          * @param {Store} this
8232          */
8233         clear : true,
8234         /**
8235          * @event beforeload
8236          * Fires before a request is made for a new data object.  If the beforeload handler returns false
8237          * the load action will be canceled.
8238          * @param {Store} this
8239          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8240          */
8241         beforeload : true,
8242         /**
8243          * @event beforeloadadd
8244          * Fires after a new set of Records has been loaded.
8245          * @param {Store} this
8246          * @param {Roo.data.Record[]} records The Records that were loaded
8247          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8248          */
8249         beforeloadadd : true,
8250         /**
8251          * @event load
8252          * Fires after a new set of Records has been loaded, before they are added to the store.
8253          * @param {Store} this
8254          * @param {Roo.data.Record[]} records The Records that were loaded
8255          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8256          * @params {Object} return from reader
8257          */
8258         load : true,
8259         /**
8260          * @event loadexception
8261          * Fires if an exception occurs in the Proxy during loading.
8262          * Called with the signature of the Proxy's "loadexception" event.
8263          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
8264          * 
8265          * @param {Proxy} 
8266          * @param {Object} return from JsonData.reader() - success, totalRecords, records
8267          * @param {Object} load options 
8268          * @param {Object} jsonData from your request (normally this contains the Exception)
8269          */
8270         loadexception : true
8271     });
8272     
8273     if(this.proxy){
8274         this.proxy = Roo.factory(this.proxy, Roo.data);
8275         this.proxy.xmodule = this.xmodule || false;
8276         this.relayEvents(this.proxy,  ["loadexception"]);
8277     }
8278     this.sortToggle = {};
8279     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
8280
8281     Roo.data.Store.superclass.constructor.call(this);
8282
8283     if(this.inlineData){
8284         this.loadData(this.inlineData);
8285         delete this.inlineData;
8286     }
8287 };
8288
8289 Roo.extend(Roo.data.Store, Roo.util.Observable, {
8290      /**
8291     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
8292     * without a remote query - used by combo/forms at present.
8293     */
8294     
8295     /**
8296     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
8297     */
8298     /**
8299     * @cfg {Array} data Inline data to be loaded when the store is initialized.
8300     */
8301     /**
8302     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
8303     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
8304     */
8305     /**
8306     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
8307     * on any HTTP request
8308     */
8309     /**
8310     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
8311     */
8312     /**
8313     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
8314     */
8315     multiSort: false,
8316     /**
8317     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
8318     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
8319     */
8320     remoteSort : false,
8321
8322     /**
8323     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
8324      * loaded or when a record is removed. (defaults to false).
8325     */
8326     pruneModifiedRecords : false,
8327
8328     // private
8329     lastOptions : null,
8330
8331     /**
8332      * Add Records to the Store and fires the add event.
8333      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8334      */
8335     add : function(records){
8336         records = [].concat(records);
8337         for(var i = 0, len = records.length; i < len; i++){
8338             records[i].join(this);
8339         }
8340         var index = this.data.length;
8341         this.data.addAll(records);
8342         this.fireEvent("add", this, records, index);
8343     },
8344
8345     /**
8346      * Remove a Record from the Store and fires the remove event.
8347      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
8348      */
8349     remove : function(record){
8350         var index = this.data.indexOf(record);
8351         this.data.removeAt(index);
8352         if(this.pruneModifiedRecords){
8353             this.modified.remove(record);
8354         }
8355         this.fireEvent("remove", this, record, index);
8356     },
8357
8358     /**
8359      * Remove all Records from the Store and fires the clear event.
8360      */
8361     removeAll : function(){
8362         this.data.clear();
8363         if(this.pruneModifiedRecords){
8364             this.modified = [];
8365         }
8366         this.fireEvent("clear", this);
8367     },
8368
8369     /**
8370      * Inserts Records to the Store at the given index and fires the add event.
8371      * @param {Number} index The start index at which to insert the passed Records.
8372      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8373      */
8374     insert : function(index, records){
8375         records = [].concat(records);
8376         for(var i = 0, len = records.length; i < len; i++){
8377             this.data.insert(index, records[i]);
8378             records[i].join(this);
8379         }
8380         this.fireEvent("add", this, records, index);
8381     },
8382
8383     /**
8384      * Get the index within the cache of the passed Record.
8385      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
8386      * @return {Number} The index of the passed Record. Returns -1 if not found.
8387      */
8388     indexOf : function(record){
8389         return this.data.indexOf(record);
8390     },
8391
8392     /**
8393      * Get the index within the cache of the Record with the passed id.
8394      * @param {String} id The id of the Record to find.
8395      * @return {Number} The index of the Record. Returns -1 if not found.
8396      */
8397     indexOfId : function(id){
8398         return this.data.indexOfKey(id);
8399     },
8400
8401     /**
8402      * Get the Record with the specified id.
8403      * @param {String} id The id of the Record to find.
8404      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
8405      */
8406     getById : function(id){
8407         return this.data.key(id);
8408     },
8409
8410     /**
8411      * Get the Record at the specified index.
8412      * @param {Number} index The index of the Record to find.
8413      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
8414      */
8415     getAt : function(index){
8416         return this.data.itemAt(index);
8417     },
8418
8419     /**
8420      * Returns a range of Records between specified indices.
8421      * @param {Number} startIndex (optional) The starting index (defaults to 0)
8422      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
8423      * @return {Roo.data.Record[]} An array of Records
8424      */
8425     getRange : function(start, end){
8426         return this.data.getRange(start, end);
8427     },
8428
8429     // private
8430     storeOptions : function(o){
8431         o = Roo.apply({}, o);
8432         delete o.callback;
8433         delete o.scope;
8434         this.lastOptions = o;
8435     },
8436
8437     /**
8438      * Loads the Record cache from the configured Proxy using the configured Reader.
8439      * <p>
8440      * If using remote paging, then the first load call must specify the <em>start</em>
8441      * and <em>limit</em> properties in the options.params property to establish the initial
8442      * position within the dataset, and the number of Records to cache on each read from the Proxy.
8443      * <p>
8444      * <strong>It is important to note that for remote data sources, loading is asynchronous,
8445      * and this call will return before the new data has been loaded. Perform any post-processing
8446      * in a callback function, or in a "load" event handler.</strong>
8447      * <p>
8448      * @param {Object} options An object containing properties which control loading options:<ul>
8449      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
8450      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
8451      * passed the following arguments:<ul>
8452      * <li>r : Roo.data.Record[]</li>
8453      * <li>options: Options object from the load call</li>
8454      * <li>success: Boolean success indicator</li></ul></li>
8455      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
8456      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
8457      * </ul>
8458      */
8459     load : function(options){
8460         options = options || {};
8461         if(this.fireEvent("beforeload", this, options) !== false){
8462             this.storeOptions(options);
8463             var p = Roo.apply(options.params || {}, this.baseParams);
8464             // if meta was not loaded from remote source.. try requesting it.
8465             if (!this.reader.metaFromRemote) {
8466                 p._requestMeta = 1;
8467             }
8468             if(this.sortInfo && this.remoteSort){
8469                 var pn = this.paramNames;
8470                 p[pn["sort"]] = this.sortInfo.field;
8471                 p[pn["dir"]] = this.sortInfo.direction;
8472             }
8473             if (this.multiSort) {
8474                 var pn = this.paramNames;
8475                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
8476             }
8477             
8478             this.proxy.load(p, this.reader, this.loadRecords, this, options);
8479         }
8480     },
8481
8482     /**
8483      * Reloads the Record cache from the configured Proxy using the configured Reader and
8484      * the options from the last load operation performed.
8485      * @param {Object} options (optional) An object containing properties which may override the options
8486      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
8487      * the most recently used options are reused).
8488      */
8489     reload : function(options){
8490         this.load(Roo.applyIf(options||{}, this.lastOptions));
8491     },
8492
8493     // private
8494     // Called as a callback by the Reader during a load operation.
8495     loadRecords : function(o, options, success){
8496         if(!o || success === false){
8497             if(success !== false){
8498                 this.fireEvent("load", this, [], options, o);
8499             }
8500             if(options.callback){
8501                 options.callback.call(options.scope || this, [], options, false);
8502             }
8503             return;
8504         }
8505         // if data returned failure - throw an exception.
8506         if (o.success === false) {
8507             // show a message if no listener is registered.
8508             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
8509                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
8510             }
8511             // loadmask wil be hooked into this..
8512             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
8513             return;
8514         }
8515         var r = o.records, t = o.totalRecords || r.length;
8516         
8517         this.fireEvent("beforeloadadd", this, r, options, o);
8518         
8519         if(!options || options.add !== true){
8520             if(this.pruneModifiedRecords){
8521                 this.modified = [];
8522             }
8523             for(var i = 0, len = r.length; i < len; i++){
8524                 r[i].join(this);
8525             }
8526             if(this.snapshot){
8527                 this.data = this.snapshot;
8528                 delete this.snapshot;
8529             }
8530             this.data.clear();
8531             this.data.addAll(r);
8532             this.totalLength = t;
8533             this.applySort();
8534             this.fireEvent("datachanged", this);
8535         }else{
8536             this.totalLength = Math.max(t, this.data.length+r.length);
8537             this.add(r);
8538         }
8539         this.fireEvent("load", this, r, options, o);
8540         if(options.callback){
8541             options.callback.call(options.scope || this, r, options, true);
8542         }
8543     },
8544
8545
8546     /**
8547      * Loads data from a passed data block. A Reader which understands the format of the data
8548      * must have been configured in the constructor.
8549      * @param {Object} data The data block from which to read the Records.  The format of the data expected
8550      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
8551      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
8552      */
8553     loadData : function(o, append){
8554         var r = this.reader.readRecords(o);
8555         this.loadRecords(r, {add: append}, true);
8556     },
8557
8558     /**
8559      * Gets the number of cached records.
8560      * <p>
8561      * <em>If using paging, this may not be the total size of the dataset. If the data object
8562      * used by the Reader contains the dataset size, then the getTotalCount() function returns
8563      * the data set size</em>
8564      */
8565     getCount : function(){
8566         return this.data.length || 0;
8567     },
8568
8569     /**
8570      * Gets the total number of records in the dataset as returned by the server.
8571      * <p>
8572      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
8573      * the dataset size</em>
8574      */
8575     getTotalCount : function(){
8576         return this.totalLength || 0;
8577     },
8578
8579     /**
8580      * Returns the sort state of the Store as an object with two properties:
8581      * <pre><code>
8582  field {String} The name of the field by which the Records are sorted
8583  direction {String} The sort order, "ASC" or "DESC"
8584      * </code></pre>
8585      */
8586     getSortState : function(){
8587         return this.sortInfo;
8588     },
8589
8590     // private
8591     applySort : function(){
8592         if(this.sortInfo && !this.remoteSort){
8593             var s = this.sortInfo, f = s.field;
8594             var st = this.fields.get(f).sortType;
8595             var fn = function(r1, r2){
8596                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
8597                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
8598             };
8599             this.data.sort(s.direction, fn);
8600             if(this.snapshot && this.snapshot != this.data){
8601                 this.snapshot.sort(s.direction, fn);
8602             }
8603         }
8604     },
8605
8606     /**
8607      * Sets the default sort column and order to be used by the next load operation.
8608      * @param {String} fieldName The name of the field to sort by.
8609      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
8610      */
8611     setDefaultSort : function(field, dir){
8612         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
8613     },
8614
8615     /**
8616      * Sort the Records.
8617      * If remote sorting is used, the sort is performed on the server, and the cache is
8618      * reloaded. If local sorting is used, the cache is sorted internally.
8619      * @param {String} fieldName The name of the field to sort by.
8620      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
8621      */
8622     sort : function(fieldName, dir){
8623         var f = this.fields.get(fieldName);
8624         if(!dir){
8625             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
8626             
8627             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
8628                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
8629             }else{
8630                 dir = f.sortDir;
8631             }
8632         }
8633         this.sortToggle[f.name] = dir;
8634         this.sortInfo = {field: f.name, direction: dir};
8635         if(!this.remoteSort){
8636             this.applySort();
8637             this.fireEvent("datachanged", this);
8638         }else{
8639             this.load(this.lastOptions);
8640         }
8641     },
8642
8643     /**
8644      * Calls the specified function for each of the Records in the cache.
8645      * @param {Function} fn The function to call. The Record is passed as the first parameter.
8646      * Returning <em>false</em> aborts and exits the iteration.
8647      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
8648      */
8649     each : function(fn, scope){
8650         this.data.each(fn, scope);
8651     },
8652
8653     /**
8654      * Gets all records modified since the last commit.  Modified records are persisted across load operations
8655      * (e.g., during paging).
8656      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
8657      */
8658     getModifiedRecords : function(){
8659         return this.modified;
8660     },
8661
8662     // private
8663     createFilterFn : function(property, value, anyMatch){
8664         if(!value.exec){ // not a regex
8665             value = String(value);
8666             if(value.length == 0){
8667                 return false;
8668             }
8669             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
8670         }
8671         return function(r){
8672             return value.test(r.data[property]);
8673         };
8674     },
8675
8676     /**
8677      * Sums the value of <i>property</i> for each record between start and end and returns the result.
8678      * @param {String} property A field on your records
8679      * @param {Number} start The record index to start at (defaults to 0)
8680      * @param {Number} end The last record index to include (defaults to length - 1)
8681      * @return {Number} The sum
8682      */
8683     sum : function(property, start, end){
8684         var rs = this.data.items, v = 0;
8685         start = start || 0;
8686         end = (end || end === 0) ? end : rs.length-1;
8687
8688         for(var i = start; i <= end; i++){
8689             v += (rs[i].data[property] || 0);
8690         }
8691         return v;
8692     },
8693
8694     /**
8695      * Filter the records by a specified property.
8696      * @param {String} field A field on your records
8697      * @param {String/RegExp} value Either a string that the field
8698      * should start with or a RegExp to test against the field
8699      * @param {Boolean} anyMatch True to match any part not just the beginning
8700      */
8701     filter : function(property, value, anyMatch){
8702         var fn = this.createFilterFn(property, value, anyMatch);
8703         return fn ? this.filterBy(fn) : this.clearFilter();
8704     },
8705
8706     /**
8707      * Filter by a function. The specified function will be called with each
8708      * record in this data source. If the function returns true the record is included,
8709      * otherwise it is filtered.
8710      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
8711      * @param {Object} scope (optional) The scope of the function (defaults to this)
8712      */
8713     filterBy : function(fn, scope){
8714         this.snapshot = this.snapshot || this.data;
8715         this.data = this.queryBy(fn, scope||this);
8716         this.fireEvent("datachanged", this);
8717     },
8718
8719     /**
8720      * Query the records by a specified property.
8721      * @param {String} field A field on your records
8722      * @param {String/RegExp} value Either a string that the field
8723      * should start with or a RegExp to test against the field
8724      * @param {Boolean} anyMatch True to match any part not just the beginning
8725      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
8726      */
8727     query : function(property, value, anyMatch){
8728         var fn = this.createFilterFn(property, value, anyMatch);
8729         return fn ? this.queryBy(fn) : this.data.clone();
8730     },
8731
8732     /**
8733      * Query by a function. The specified function will be called with each
8734      * record in this data source. If the function returns true the record is included
8735      * in the results.
8736      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
8737      * @param {Object} scope (optional) The scope of the function (defaults to this)
8738       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
8739      **/
8740     queryBy : function(fn, scope){
8741         var data = this.snapshot || this.data;
8742         return data.filterBy(fn, scope||this);
8743     },
8744
8745     /**
8746      * Collects unique values for a particular dataIndex from this store.
8747      * @param {String} dataIndex The property to collect
8748      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
8749      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
8750      * @return {Array} An array of the unique values
8751      **/
8752     collect : function(dataIndex, allowNull, bypassFilter){
8753         var d = (bypassFilter === true && this.snapshot) ?
8754                 this.snapshot.items : this.data.items;
8755         var v, sv, r = [], l = {};
8756         for(var i = 0, len = d.length; i < len; i++){
8757             v = d[i].data[dataIndex];
8758             sv = String(v);
8759             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
8760                 l[sv] = true;
8761                 r[r.length] = v;
8762             }
8763         }
8764         return r;
8765     },
8766
8767     /**
8768      * Revert to a view of the Record cache with no filtering applied.
8769      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
8770      */
8771     clearFilter : function(suppressEvent){
8772         if(this.snapshot && this.snapshot != this.data){
8773             this.data = this.snapshot;
8774             delete this.snapshot;
8775             if(suppressEvent !== true){
8776                 this.fireEvent("datachanged", this);
8777             }
8778         }
8779     },
8780
8781     // private
8782     afterEdit : function(record){
8783         if(this.modified.indexOf(record) == -1){
8784             this.modified.push(record);
8785         }
8786         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
8787     },
8788     
8789     // private
8790     afterReject : function(record){
8791         this.modified.remove(record);
8792         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
8793     },
8794
8795     // private
8796     afterCommit : function(record){
8797         this.modified.remove(record);
8798         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
8799     },
8800
8801     /**
8802      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
8803      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
8804      */
8805     commitChanges : function(){
8806         var m = this.modified.slice(0);
8807         this.modified = [];
8808         for(var i = 0, len = m.length; i < len; i++){
8809             m[i].commit();
8810         }
8811     },
8812
8813     /**
8814      * Cancel outstanding changes on all changed records.
8815      */
8816     rejectChanges : function(){
8817         var m = this.modified.slice(0);
8818         this.modified = [];
8819         for(var i = 0, len = m.length; i < len; i++){
8820             m[i].reject();
8821         }
8822     },
8823
8824     onMetaChange : function(meta, rtype, o){
8825         this.recordType = rtype;
8826         this.fields = rtype.prototype.fields;
8827         delete this.snapshot;
8828         this.sortInfo = meta.sortInfo || this.sortInfo;
8829         this.modified = [];
8830         this.fireEvent('metachange', this, this.reader.meta);
8831     },
8832     
8833     moveIndex : function(data, type)
8834     {
8835         var index = this.indexOf(data);
8836         
8837         var newIndex = index + type;
8838         
8839         this.remove(data);
8840         
8841         this.insert(newIndex, data);
8842         
8843     }
8844 });/*
8845  * Based on:
8846  * Ext JS Library 1.1.1
8847  * Copyright(c) 2006-2007, Ext JS, LLC.
8848  *
8849  * Originally Released Under LGPL - original licence link has changed is not relivant.
8850  *
8851  * Fork - LGPL
8852  * <script type="text/javascript">
8853  */
8854
8855 /**
8856  * @class Roo.data.SimpleStore
8857  * @extends Roo.data.Store
8858  * Small helper class to make creating Stores from Array data easier.
8859  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
8860  * @cfg {Array} fields An array of field definition objects, or field name strings.
8861  * @cfg {Array} data The multi-dimensional array of data
8862  * @constructor
8863  * @param {Object} config
8864  */
8865 Roo.data.SimpleStore = function(config){
8866     Roo.data.SimpleStore.superclass.constructor.call(this, {
8867         isLocal : true,
8868         reader: new Roo.data.ArrayReader({
8869                 id: config.id
8870             },
8871             Roo.data.Record.create(config.fields)
8872         ),
8873         proxy : new Roo.data.MemoryProxy(config.data)
8874     });
8875     this.load();
8876 };
8877 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
8878  * Based on:
8879  * Ext JS Library 1.1.1
8880  * Copyright(c) 2006-2007, Ext JS, LLC.
8881  *
8882  * Originally Released Under LGPL - original licence link has changed is not relivant.
8883  *
8884  * Fork - LGPL
8885  * <script type="text/javascript">
8886  */
8887
8888 /**
8889 /**
8890  * @extends Roo.data.Store
8891  * @class Roo.data.JsonStore
8892  * Small helper class to make creating Stores for JSON data easier. <br/>
8893 <pre><code>
8894 var store = new Roo.data.JsonStore({
8895     url: 'get-images.php',
8896     root: 'images',
8897     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
8898 });
8899 </code></pre>
8900  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
8901  * JsonReader and HttpProxy (unless inline data is provided).</b>
8902  * @cfg {Array} fields An array of field definition objects, or field name strings.
8903  * @constructor
8904  * @param {Object} config
8905  */
8906 Roo.data.JsonStore = function(c){
8907     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
8908         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
8909         reader: new Roo.data.JsonReader(c, c.fields)
8910     }));
8911 };
8912 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
8913  * Based on:
8914  * Ext JS Library 1.1.1
8915  * Copyright(c) 2006-2007, Ext JS, LLC.
8916  *
8917  * Originally Released Under LGPL - original licence link has changed is not relivant.
8918  *
8919  * Fork - LGPL
8920  * <script type="text/javascript">
8921  */
8922
8923  
8924 Roo.data.Field = function(config){
8925     if(typeof config == "string"){
8926         config = {name: config};
8927     }
8928     Roo.apply(this, config);
8929     
8930     if(!this.type){
8931         this.type = "auto";
8932     }
8933     
8934     var st = Roo.data.SortTypes;
8935     // named sortTypes are supported, here we look them up
8936     if(typeof this.sortType == "string"){
8937         this.sortType = st[this.sortType];
8938     }
8939     
8940     // set default sortType for strings and dates
8941     if(!this.sortType){
8942         switch(this.type){
8943             case "string":
8944                 this.sortType = st.asUCString;
8945                 break;
8946             case "date":
8947                 this.sortType = st.asDate;
8948                 break;
8949             default:
8950                 this.sortType = st.none;
8951         }
8952     }
8953
8954     // define once
8955     var stripRe = /[\$,%]/g;
8956
8957     // prebuilt conversion function for this field, instead of
8958     // switching every time we're reading a value
8959     if(!this.convert){
8960         var cv, dateFormat = this.dateFormat;
8961         switch(this.type){
8962             case "":
8963             case "auto":
8964             case undefined:
8965                 cv = function(v){ return v; };
8966                 break;
8967             case "string":
8968                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
8969                 break;
8970             case "int":
8971                 cv = function(v){
8972                     return v !== undefined && v !== null && v !== '' ?
8973                            parseInt(String(v).replace(stripRe, ""), 10) : '';
8974                     };
8975                 break;
8976             case "float":
8977                 cv = function(v){
8978                     return v !== undefined && v !== null && v !== '' ?
8979                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
8980                     };
8981                 break;
8982             case "bool":
8983             case "boolean":
8984                 cv = function(v){ return v === true || v === "true" || v == 1; };
8985                 break;
8986             case "date":
8987                 cv = function(v){
8988                     if(!v){
8989                         return '';
8990                     }
8991                     if(v instanceof Date){
8992                         return v;
8993                     }
8994                     if(dateFormat){
8995                         if(dateFormat == "timestamp"){
8996                             return new Date(v*1000);
8997                         }
8998                         return Date.parseDate(v, dateFormat);
8999                     }
9000                     var parsed = Date.parse(v);
9001                     return parsed ? new Date(parsed) : null;
9002                 };
9003              break;
9004             
9005         }
9006         this.convert = cv;
9007     }
9008 };
9009
9010 Roo.data.Field.prototype = {
9011     dateFormat: null,
9012     defaultValue: "",
9013     mapping: null,
9014     sortType : null,
9015     sortDir : "ASC"
9016 };/*
9017  * Based on:
9018  * Ext JS Library 1.1.1
9019  * Copyright(c) 2006-2007, Ext JS, LLC.
9020  *
9021  * Originally Released Under LGPL - original licence link has changed is not relivant.
9022  *
9023  * Fork - LGPL
9024  * <script type="text/javascript">
9025  */
9026  
9027 // Base class for reading structured data from a data source.  This class is intended to be
9028 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
9029
9030 /**
9031  * @class Roo.data.DataReader
9032  * Base class for reading structured data from a data source.  This class is intended to be
9033  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
9034  */
9035
9036 Roo.data.DataReader = function(meta, recordType){
9037     
9038     this.meta = meta;
9039     
9040     this.recordType = recordType instanceof Array ? 
9041         Roo.data.Record.create(recordType) : recordType;
9042 };
9043
9044 Roo.data.DataReader.prototype = {
9045      /**
9046      * Create an empty record
9047      * @param {Object} data (optional) - overlay some values
9048      * @return {Roo.data.Record} record created.
9049      */
9050     newRow :  function(d) {
9051         var da =  {};
9052         this.recordType.prototype.fields.each(function(c) {
9053             switch( c.type) {
9054                 case 'int' : da[c.name] = 0; break;
9055                 case 'date' : da[c.name] = new Date(); break;
9056                 case 'float' : da[c.name] = 0.0; break;
9057                 case 'boolean' : da[c.name] = false; break;
9058                 default : da[c.name] = ""; break;
9059             }
9060             
9061         });
9062         return new this.recordType(Roo.apply(da, d));
9063     }
9064     
9065 };/*
9066  * Based on:
9067  * Ext JS Library 1.1.1
9068  * Copyright(c) 2006-2007, Ext JS, LLC.
9069  *
9070  * Originally Released Under LGPL - original licence link has changed is not relivant.
9071  *
9072  * Fork - LGPL
9073  * <script type="text/javascript">
9074  */
9075
9076 /**
9077  * @class Roo.data.DataProxy
9078  * @extends Roo.data.Observable
9079  * This class is an abstract base class for implementations which provide retrieval of
9080  * unformatted data objects.<br>
9081  * <p>
9082  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
9083  * (of the appropriate type which knows how to parse the data object) to provide a block of
9084  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
9085  * <p>
9086  * Custom implementations must implement the load method as described in
9087  * {@link Roo.data.HttpProxy#load}.
9088  */
9089 Roo.data.DataProxy = function(){
9090     this.addEvents({
9091         /**
9092          * @event beforeload
9093          * Fires before a network request is made to retrieve a data object.
9094          * @param {Object} This DataProxy object.
9095          * @param {Object} params The params parameter to the load function.
9096          */
9097         beforeload : true,
9098         /**
9099          * @event load
9100          * Fires before the load method's callback is called.
9101          * @param {Object} This DataProxy object.
9102          * @param {Object} o The data object.
9103          * @param {Object} arg The callback argument object passed to the load function.
9104          */
9105         load : true,
9106         /**
9107          * @event loadexception
9108          * Fires if an Exception occurs during data retrieval.
9109          * @param {Object} This DataProxy object.
9110          * @param {Object} o The data object.
9111          * @param {Object} arg The callback argument object passed to the load function.
9112          * @param {Object} e The Exception.
9113          */
9114         loadexception : true
9115     });
9116     Roo.data.DataProxy.superclass.constructor.call(this);
9117 };
9118
9119 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
9120
9121     /**
9122      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
9123      */
9124 /*
9125  * Based on:
9126  * Ext JS Library 1.1.1
9127  * Copyright(c) 2006-2007, Ext JS, LLC.
9128  *
9129  * Originally Released Under LGPL - original licence link has changed is not relivant.
9130  *
9131  * Fork - LGPL
9132  * <script type="text/javascript">
9133  */
9134 /**
9135  * @class Roo.data.MemoryProxy
9136  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
9137  * to the Reader when its load method is called.
9138  * @constructor
9139  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
9140  */
9141 Roo.data.MemoryProxy = function(data){
9142     if (data.data) {
9143         data = data.data;
9144     }
9145     Roo.data.MemoryProxy.superclass.constructor.call(this);
9146     this.data = data;
9147 };
9148
9149 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
9150     /**
9151      * Load data from the requested source (in this case an in-memory
9152      * data object passed to the constructor), read the data object into
9153      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9154      * process that block using the passed callback.
9155      * @param {Object} params This parameter is not used by the MemoryProxy class.
9156      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9157      * object into a block of Roo.data.Records.
9158      * @param {Function} callback The function into which to pass the block of Roo.data.records.
9159      * The function must be passed <ul>
9160      * <li>The Record block object</li>
9161      * <li>The "arg" argument from the load function</li>
9162      * <li>A boolean success indicator</li>
9163      * </ul>
9164      * @param {Object} scope The scope in which to call the callback
9165      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9166      */
9167     load : function(params, reader, callback, scope, arg){
9168         params = params || {};
9169         var result;
9170         try {
9171             result = reader.readRecords(this.data);
9172         }catch(e){
9173             this.fireEvent("loadexception", this, arg, null, e);
9174             callback.call(scope, null, arg, false);
9175             return;
9176         }
9177         callback.call(scope, result, arg, true);
9178     },
9179     
9180     // private
9181     update : function(params, records){
9182         
9183     }
9184 });/*
9185  * Based on:
9186  * Ext JS Library 1.1.1
9187  * Copyright(c) 2006-2007, Ext JS, LLC.
9188  *
9189  * Originally Released Under LGPL - original licence link has changed is not relivant.
9190  *
9191  * Fork - LGPL
9192  * <script type="text/javascript">
9193  */
9194 /**
9195  * @class Roo.data.HttpProxy
9196  * @extends Roo.data.DataProxy
9197  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
9198  * configured to reference a certain URL.<br><br>
9199  * <p>
9200  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
9201  * from which the running page was served.<br><br>
9202  * <p>
9203  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
9204  * <p>
9205  * Be aware that to enable the browser to parse an XML document, the server must set
9206  * the Content-Type header in the HTTP response to "text/xml".
9207  * @constructor
9208  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
9209  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
9210  * will be used to make the request.
9211  */
9212 Roo.data.HttpProxy = function(conn){
9213     Roo.data.HttpProxy.superclass.constructor.call(this);
9214     // is conn a conn config or a real conn?
9215     this.conn = conn;
9216     this.useAjax = !conn || !conn.events;
9217   
9218 };
9219
9220 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
9221     // thse are take from connection...
9222     
9223     /**
9224      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
9225      */
9226     /**
9227      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
9228      * extra parameters to each request made by this object. (defaults to undefined)
9229      */
9230     /**
9231      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
9232      *  to each request made by this object. (defaults to undefined)
9233      */
9234     /**
9235      * @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)
9236      */
9237     /**
9238      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
9239      */
9240      /**
9241      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
9242      * @type Boolean
9243      */
9244   
9245
9246     /**
9247      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
9248      * @type Boolean
9249      */
9250     /**
9251      * Return the {@link Roo.data.Connection} object being used by this Proxy.
9252      * @return {Connection} The Connection object. This object may be used to subscribe to events on
9253      * a finer-grained basis than the DataProxy events.
9254      */
9255     getConnection : function(){
9256         return this.useAjax ? Roo.Ajax : this.conn;
9257     },
9258
9259     /**
9260      * Load data from the configured {@link Roo.data.Connection}, read the data object into
9261      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
9262      * process that block using the passed callback.
9263      * @param {Object} params An object containing properties which are to be used as HTTP parameters
9264      * for the request to the remote server.
9265      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9266      * object into a block of Roo.data.Records.
9267      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9268      * The function must be passed <ul>
9269      * <li>The Record block object</li>
9270      * <li>The "arg" argument from the load function</li>
9271      * <li>A boolean success indicator</li>
9272      * </ul>
9273      * @param {Object} scope The scope in which to call the callback
9274      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9275      */
9276     load : function(params, reader, callback, scope, arg){
9277         if(this.fireEvent("beforeload", this, params) !== false){
9278             var  o = {
9279                 params : params || {},
9280                 request: {
9281                     callback : callback,
9282                     scope : scope,
9283                     arg : arg
9284                 },
9285                 reader: reader,
9286                 callback : this.loadResponse,
9287                 scope: this
9288             };
9289             if(this.useAjax){
9290                 Roo.applyIf(o, this.conn);
9291                 if(this.activeRequest){
9292                     Roo.Ajax.abort(this.activeRequest);
9293                 }
9294                 this.activeRequest = Roo.Ajax.request(o);
9295             }else{
9296                 this.conn.request(o);
9297             }
9298         }else{
9299             callback.call(scope||this, null, arg, false);
9300         }
9301     },
9302
9303     // private
9304     loadResponse : function(o, success, response){
9305         delete this.activeRequest;
9306         if(!success){
9307             this.fireEvent("loadexception", this, o, response);
9308             o.request.callback.call(o.request.scope, null, o.request.arg, false);
9309             return;
9310         }
9311         var result;
9312         try {
9313             result = o.reader.read(response);
9314         }catch(e){
9315             this.fireEvent("loadexception", this, o, response, e);
9316             o.request.callback.call(o.request.scope, null, o.request.arg, false);
9317             return;
9318         }
9319         
9320         this.fireEvent("load", this, o, o.request.arg);
9321         o.request.callback.call(o.request.scope, result, o.request.arg, true);
9322     },
9323
9324     // private
9325     update : function(dataSet){
9326
9327     },
9328
9329     // private
9330     updateResponse : function(dataSet){
9331
9332     }
9333 });/*
9334  * Based on:
9335  * Ext JS Library 1.1.1
9336  * Copyright(c) 2006-2007, Ext JS, LLC.
9337  *
9338  * Originally Released Under LGPL - original licence link has changed is not relivant.
9339  *
9340  * Fork - LGPL
9341  * <script type="text/javascript">
9342  */
9343
9344 /**
9345  * @class Roo.data.ScriptTagProxy
9346  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
9347  * other than the originating domain of the running page.<br><br>
9348  * <p>
9349  * <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
9350  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
9351  * <p>
9352  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
9353  * source code that is used as the source inside a &lt;script> tag.<br><br>
9354  * <p>
9355  * In order for the browser to process the returned data, the server must wrap the data object
9356  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
9357  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
9358  * depending on whether the callback name was passed:
9359  * <p>
9360  * <pre><code>
9361 boolean scriptTag = false;
9362 String cb = request.getParameter("callback");
9363 if (cb != null) {
9364     scriptTag = true;
9365     response.setContentType("text/javascript");
9366 } else {
9367     response.setContentType("application/x-json");
9368 }
9369 Writer out = response.getWriter();
9370 if (scriptTag) {
9371     out.write(cb + "(");
9372 }
9373 out.print(dataBlock.toJsonString());
9374 if (scriptTag) {
9375     out.write(");");
9376 }
9377 </pre></code>
9378  *
9379  * @constructor
9380  * @param {Object} config A configuration object.
9381  */
9382 Roo.data.ScriptTagProxy = function(config){
9383     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
9384     Roo.apply(this, config);
9385     this.head = document.getElementsByTagName("head")[0];
9386 };
9387
9388 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
9389
9390 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
9391     /**
9392      * @cfg {String} url The URL from which to request the data object.
9393      */
9394     /**
9395      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
9396      */
9397     timeout : 30000,
9398     /**
9399      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
9400      * the server the name of the callback function set up by the load call to process the returned data object.
9401      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
9402      * javascript output which calls this named function passing the data object as its only parameter.
9403      */
9404     callbackParam : "callback",
9405     /**
9406      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
9407      * name to the request.
9408      */
9409     nocache : true,
9410
9411     /**
9412      * Load data from the configured URL, read the data object into
9413      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9414      * process that block using the passed callback.
9415      * @param {Object} params An object containing properties which are to be used as HTTP parameters
9416      * for the request to the remote server.
9417      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9418      * object into a block of Roo.data.Records.
9419      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9420      * The function must be passed <ul>
9421      * <li>The Record block object</li>
9422      * <li>The "arg" argument from the load function</li>
9423      * <li>A boolean success indicator</li>
9424      * </ul>
9425      * @param {Object} scope The scope in which to call the callback
9426      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9427      */
9428     load : function(params, reader, callback, scope, arg){
9429         if(this.fireEvent("beforeload", this, params) !== false){
9430
9431             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
9432
9433             var url = this.url;
9434             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
9435             if(this.nocache){
9436                 url += "&_dc=" + (new Date().getTime());
9437             }
9438             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
9439             var trans = {
9440                 id : transId,
9441                 cb : "stcCallback"+transId,
9442                 scriptId : "stcScript"+transId,
9443                 params : params,
9444                 arg : arg,
9445                 url : url,
9446                 callback : callback,
9447                 scope : scope,
9448                 reader : reader
9449             };
9450             var conn = this;
9451
9452             window[trans.cb] = function(o){
9453                 conn.handleResponse(o, trans);
9454             };
9455
9456             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
9457
9458             if(this.autoAbort !== false){
9459                 this.abort();
9460             }
9461
9462             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
9463
9464             var script = document.createElement("script");
9465             script.setAttribute("src", url);
9466             script.setAttribute("type", "text/javascript");
9467             script.setAttribute("id", trans.scriptId);
9468             this.head.appendChild(script);
9469
9470             this.trans = trans;
9471         }else{
9472             callback.call(scope||this, null, arg, false);
9473         }
9474     },
9475
9476     // private
9477     isLoading : function(){
9478         return this.trans ? true : false;
9479     },
9480
9481     /**
9482      * Abort the current server request.
9483      */
9484     abort : function(){
9485         if(this.isLoading()){
9486             this.destroyTrans(this.trans);
9487         }
9488     },
9489
9490     // private
9491     destroyTrans : function(trans, isLoaded){
9492         this.head.removeChild(document.getElementById(trans.scriptId));
9493         clearTimeout(trans.timeoutId);
9494         if(isLoaded){
9495             window[trans.cb] = undefined;
9496             try{
9497                 delete window[trans.cb];
9498             }catch(e){}
9499         }else{
9500             // if hasn't been loaded, wait for load to remove it to prevent script error
9501             window[trans.cb] = function(){
9502                 window[trans.cb] = undefined;
9503                 try{
9504                     delete window[trans.cb];
9505                 }catch(e){}
9506             };
9507         }
9508     },
9509
9510     // private
9511     handleResponse : function(o, trans){
9512         this.trans = false;
9513         this.destroyTrans(trans, true);
9514         var result;
9515         try {
9516             result = trans.reader.readRecords(o);
9517         }catch(e){
9518             this.fireEvent("loadexception", this, o, trans.arg, e);
9519             trans.callback.call(trans.scope||window, null, trans.arg, false);
9520             return;
9521         }
9522         this.fireEvent("load", this, o, trans.arg);
9523         trans.callback.call(trans.scope||window, result, trans.arg, true);
9524     },
9525
9526     // private
9527     handleFailure : function(trans){
9528         this.trans = false;
9529         this.destroyTrans(trans, false);
9530         this.fireEvent("loadexception", this, null, trans.arg);
9531         trans.callback.call(trans.scope||window, null, trans.arg, false);
9532     }
9533 });/*
9534  * Based on:
9535  * Ext JS Library 1.1.1
9536  * Copyright(c) 2006-2007, Ext JS, LLC.
9537  *
9538  * Originally Released Under LGPL - original licence link has changed is not relivant.
9539  *
9540  * Fork - LGPL
9541  * <script type="text/javascript">
9542  */
9543
9544 /**
9545  * @class Roo.data.JsonReader
9546  * @extends Roo.data.DataReader
9547  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
9548  * based on mappings in a provided Roo.data.Record constructor.
9549  * 
9550  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
9551  * in the reply previously. 
9552  * 
9553  * <p>
9554  * Example code:
9555  * <pre><code>
9556 var RecordDef = Roo.data.Record.create([
9557     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
9558     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
9559 ]);
9560 var myReader = new Roo.data.JsonReader({
9561     totalProperty: "results",    // The property which contains the total dataset size (optional)
9562     root: "rows",                // The property which contains an Array of row objects
9563     id: "id"                     // The property within each row object that provides an ID for the record (optional)
9564 }, RecordDef);
9565 </code></pre>
9566  * <p>
9567  * This would consume a JSON file like this:
9568  * <pre><code>
9569 { 'results': 2, 'rows': [
9570     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
9571     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
9572 }
9573 </code></pre>
9574  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
9575  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
9576  * paged from the remote server.
9577  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
9578  * @cfg {String} root name of the property which contains the Array of row objects.
9579  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
9580  * @constructor
9581  * Create a new JsonReader
9582  * @param {Object} meta Metadata configuration options
9583  * @param {Object} recordType Either an Array of field definition objects,
9584  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
9585  */
9586 Roo.data.JsonReader = function(meta, recordType){
9587     
9588     meta = meta || {};
9589     // set some defaults:
9590     Roo.applyIf(meta, {
9591         totalProperty: 'total',
9592         successProperty : 'success',
9593         root : 'data',
9594         id : 'id'
9595     });
9596     
9597     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
9598 };
9599 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
9600     
9601     /**
9602      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
9603      * Used by Store query builder to append _requestMeta to params.
9604      * 
9605      */
9606     metaFromRemote : false,
9607     /**
9608      * This method is only used by a DataProxy which has retrieved data from a remote server.
9609      * @param {Object} response The XHR object which contains the JSON data in its responseText.
9610      * @return {Object} data A data block which is used by an Roo.data.Store object as
9611      * a cache of Roo.data.Records.
9612      */
9613     read : function(response){
9614         var json = response.responseText;
9615        
9616         var o = /* eval:var:o */ eval("("+json+")");
9617         if(!o) {
9618             throw {message: "JsonReader.read: Json object not found"};
9619         }
9620         
9621         if(o.metaData){
9622             
9623             delete this.ef;
9624             this.metaFromRemote = true;
9625             this.meta = o.metaData;
9626             this.recordType = Roo.data.Record.create(o.metaData.fields);
9627             this.onMetaChange(this.meta, this.recordType, o);
9628         }
9629         return this.readRecords(o);
9630     },
9631
9632     // private function a store will implement
9633     onMetaChange : function(meta, recordType, o){
9634
9635     },
9636
9637     /**
9638          * @ignore
9639          */
9640     simpleAccess: function(obj, subsc) {
9641         return obj[subsc];
9642     },
9643
9644         /**
9645          * @ignore
9646          */
9647     getJsonAccessor: function(){
9648         var re = /[\[\.]/;
9649         return function(expr) {
9650             try {
9651                 return(re.test(expr))
9652                     ? new Function("obj", "return obj." + expr)
9653                     : function(obj){
9654                         return obj[expr];
9655                     };
9656             } catch(e){}
9657             return Roo.emptyFn;
9658         };
9659     }(),
9660
9661     /**
9662      * Create a data block containing Roo.data.Records from an XML document.
9663      * @param {Object} o An object which contains an Array of row objects in the property specified
9664      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
9665      * which contains the total size of the dataset.
9666      * @return {Object} data A data block which is used by an Roo.data.Store object as
9667      * a cache of Roo.data.Records.
9668      */
9669     readRecords : function(o){
9670         /**
9671          * After any data loads, the raw JSON data is available for further custom processing.
9672          * @type Object
9673          */
9674         this.o = o;
9675         var s = this.meta, Record = this.recordType,
9676             f = Record.prototype.fields, fi = f.items, fl = f.length;
9677
9678 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
9679         if (!this.ef) {
9680             if(s.totalProperty) {
9681                     this.getTotal = this.getJsonAccessor(s.totalProperty);
9682                 }
9683                 if(s.successProperty) {
9684                     this.getSuccess = this.getJsonAccessor(s.successProperty);
9685                 }
9686                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
9687                 if (s.id) {
9688                         var g = this.getJsonAccessor(s.id);
9689                         this.getId = function(rec) {
9690                                 var r = g(rec);
9691                                 return (r === undefined || r === "") ? null : r;
9692                         };
9693                 } else {
9694                         this.getId = function(){return null;};
9695                 }
9696             this.ef = [];
9697             for(var jj = 0; jj < fl; jj++){
9698                 f = fi[jj];
9699                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
9700                 this.ef[jj] = this.getJsonAccessor(map);
9701             }
9702         }
9703
9704         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
9705         if(s.totalProperty){
9706             var vt = parseInt(this.getTotal(o), 10);
9707             if(!isNaN(vt)){
9708                 totalRecords = vt;
9709             }
9710         }
9711         if(s.successProperty){
9712             var vs = this.getSuccess(o);
9713             if(vs === false || vs === 'false'){
9714                 success = false;
9715             }
9716         }
9717         var records = [];
9718             for(var i = 0; i < c; i++){
9719                     var n = root[i];
9720                 var values = {};
9721                 var id = this.getId(n);
9722                 for(var j = 0; j < fl; j++){
9723                     f = fi[j];
9724                 var v = this.ef[j](n);
9725                 if (!f.convert) {
9726                     Roo.log('missing convert for ' + f.name);
9727                     Roo.log(f);
9728                     continue;
9729                 }
9730                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
9731                 }
9732                 var record = new Record(values, id);
9733                 record.json = n;
9734                 records[i] = record;
9735             }
9736             return {
9737             raw : o,
9738                 success : success,
9739                 records : records,
9740                 totalRecords : totalRecords
9741             };
9742     }
9743 });/*
9744  * Based on:
9745  * Ext JS Library 1.1.1
9746  * Copyright(c) 2006-2007, Ext JS, LLC.
9747  *
9748  * Originally Released Under LGPL - original licence link has changed is not relivant.
9749  *
9750  * Fork - LGPL
9751  * <script type="text/javascript">
9752  */
9753
9754 /**
9755  * @class Roo.data.ArrayReader
9756  * @extends Roo.data.DataReader
9757  * Data reader class to create an Array of Roo.data.Record objects from an Array.
9758  * Each element of that Array represents a row of data fields. The
9759  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
9760  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
9761  * <p>
9762  * Example code:.
9763  * <pre><code>
9764 var RecordDef = Roo.data.Record.create([
9765     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
9766     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
9767 ]);
9768 var myReader = new Roo.data.ArrayReader({
9769     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
9770 }, RecordDef);
9771 </code></pre>
9772  * <p>
9773  * This would consume an Array like this:
9774  * <pre><code>
9775 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
9776   </code></pre>
9777  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
9778  * @constructor
9779  * Create a new JsonReader
9780  * @param {Object} meta Metadata configuration options.
9781  * @param {Object} recordType Either an Array of field definition objects
9782  * as specified to {@link Roo.data.Record#create},
9783  * or an {@link Roo.data.Record} object
9784  * created using {@link Roo.data.Record#create}.
9785  */
9786 Roo.data.ArrayReader = function(meta, recordType){
9787     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
9788 };
9789
9790 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
9791     /**
9792      * Create a data block containing Roo.data.Records from an XML document.
9793      * @param {Object} o An Array of row objects which represents the dataset.
9794      * @return {Object} data A data block which is used by an Roo.data.Store object as
9795      * a cache of Roo.data.Records.
9796      */
9797     readRecords : function(o){
9798         var sid = this.meta ? this.meta.id : null;
9799         var recordType = this.recordType, fields = recordType.prototype.fields;
9800         var records = [];
9801         var root = o;
9802             for(var i = 0; i < root.length; i++){
9803                     var n = root[i];
9804                 var values = {};
9805                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
9806                 for(var j = 0, jlen = fields.length; j < jlen; j++){
9807                 var f = fields.items[j];
9808                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
9809                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
9810                 v = f.convert(v);
9811                 values[f.name] = v;
9812             }
9813                 var record = new recordType(values, id);
9814                 record.json = n;
9815                 records[records.length] = record;
9816             }
9817             return {
9818                 records : records,
9819                 totalRecords : records.length
9820             };
9821     }
9822 });/*
9823  * - LGPL
9824  * * 
9825  */
9826
9827 /**
9828  * @class Roo.bootstrap.ComboBox
9829  * @extends Roo.bootstrap.TriggerField
9830  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
9831  * @cfg {Boolean} append (true|false) default false
9832  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
9833  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
9834  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
9835  * @constructor
9836  * Create a new ComboBox.
9837  * @param {Object} config Configuration options
9838  */
9839 Roo.bootstrap.ComboBox = function(config){
9840     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
9841     this.addEvents({
9842         /**
9843          * @event expand
9844          * Fires when the dropdown list is expanded
9845              * @param {Roo.bootstrap.ComboBox} combo This combo box
9846              */
9847         'expand' : true,
9848         /**
9849          * @event collapse
9850          * Fires when the dropdown list is collapsed
9851              * @param {Roo.bootstrap.ComboBox} combo This combo box
9852              */
9853         'collapse' : true,
9854         /**
9855          * @event beforeselect
9856          * Fires before a list item is selected. Return false to cancel the selection.
9857              * @param {Roo.bootstrap.ComboBox} combo This combo box
9858              * @param {Roo.data.Record} record The data record returned from the underlying store
9859              * @param {Number} index The index of the selected item in the dropdown list
9860              */
9861         'beforeselect' : true,
9862         /**
9863          * @event select
9864          * Fires when a list item is selected
9865              * @param {Roo.bootstrap.ComboBox} combo This combo box
9866              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
9867              * @param {Number} index The index of the selected item in the dropdown list
9868              */
9869         'select' : true,
9870         /**
9871          * @event beforequery
9872          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
9873          * The event object passed has these properties:
9874              * @param {Roo.bootstrap.ComboBox} combo This combo box
9875              * @param {String} query The query
9876              * @param {Boolean} forceAll true to force "all" query
9877              * @param {Boolean} cancel true to cancel the query
9878              * @param {Object} e The query event object
9879              */
9880         'beforequery': true,
9881          /**
9882          * @event add
9883          * Fires when the 'add' icon is pressed (add a listener to enable add button)
9884              * @param {Roo.bootstrap.ComboBox} combo This combo box
9885              */
9886         'add' : true,
9887         /**
9888          * @event edit
9889          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
9890              * @param {Roo.bootstrap.ComboBox} combo This combo box
9891              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
9892              */
9893         'edit' : true,
9894         /**
9895          * @event remove
9896          * Fires when the remove value from the combobox array
9897              * @param {Roo.bootstrap.ComboBox} combo This combo box
9898              */
9899         'remove' : true
9900         
9901     });
9902     
9903     this.item = [];
9904     this.tickItems = [];
9905     
9906     this.selectedIndex = -1;
9907     if(this.mode == 'local'){
9908         if(config.queryDelay === undefined){
9909             this.queryDelay = 10;
9910         }
9911         if(config.minChars === undefined){
9912             this.minChars = 0;
9913         }
9914     }
9915 };
9916
9917 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
9918      
9919     /**
9920      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
9921      * rendering into an Roo.Editor, defaults to false)
9922      */
9923     /**
9924      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
9925      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
9926      */
9927     /**
9928      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
9929      */
9930     /**
9931      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
9932      * the dropdown list (defaults to undefined, with no header element)
9933      */
9934
9935      /**
9936      * @cfg {String/Roo.Template} tpl The template to use to render the output
9937      */
9938      
9939      /**
9940      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
9941      */
9942     listWidth: undefined,
9943     /**
9944      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
9945      * mode = 'remote' or 'text' if mode = 'local')
9946      */
9947     displayField: undefined,
9948     /**
9949      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
9950      * mode = 'remote' or 'value' if mode = 'local'). 
9951      * Note: use of a valueField requires the user make a selection
9952      * in order for a value to be mapped.
9953      */
9954     valueField: undefined,
9955     
9956     
9957     /**
9958      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
9959      * field's data value (defaults to the underlying DOM element's name)
9960      */
9961     hiddenName: undefined,
9962     /**
9963      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
9964      */
9965     listClass: '',
9966     /**
9967      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
9968      */
9969     selectedClass: 'active',
9970     
9971     /**
9972      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
9973      */
9974     shadow:'sides',
9975     /**
9976      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
9977      * anchor positions (defaults to 'tl-bl')
9978      */
9979     listAlign: 'tl-bl?',
9980     /**
9981      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
9982      */
9983     maxHeight: 300,
9984     /**
9985      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
9986      * query specified by the allQuery config option (defaults to 'query')
9987      */
9988     triggerAction: 'query',
9989     /**
9990      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
9991      * (defaults to 4, does not apply if editable = false)
9992      */
9993     minChars : 4,
9994     /**
9995      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
9996      * delay (typeAheadDelay) if it matches a known value (defaults to false)
9997      */
9998     typeAhead: false,
9999     /**
10000      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
10001      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
10002      */
10003     queryDelay: 500,
10004     /**
10005      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
10006      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
10007      */
10008     pageSize: 0,
10009     /**
10010      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
10011      * when editable = true (defaults to false)
10012      */
10013     selectOnFocus:false,
10014     /**
10015      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
10016      */
10017     queryParam: 'query',
10018     /**
10019      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
10020      * when mode = 'remote' (defaults to 'Loading...')
10021      */
10022     loadingText: 'Loading...',
10023     /**
10024      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
10025      */
10026     resizable: false,
10027     /**
10028      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
10029      */
10030     handleHeight : 8,
10031     /**
10032      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
10033      * traditional select (defaults to true)
10034      */
10035     editable: true,
10036     /**
10037      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
10038      */
10039     allQuery: '',
10040     /**
10041      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
10042      */
10043     mode: 'remote',
10044     /**
10045      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
10046      * listWidth has a higher value)
10047      */
10048     minListWidth : 70,
10049     /**
10050      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
10051      * allow the user to set arbitrary text into the field (defaults to false)
10052      */
10053     forceSelection:false,
10054     /**
10055      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
10056      * if typeAhead = true (defaults to 250)
10057      */
10058     typeAheadDelay : 250,
10059     /**
10060      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
10061      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
10062      */
10063     valueNotFoundText : undefined,
10064     /**
10065      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
10066      */
10067     blockFocus : false,
10068     
10069     /**
10070      * @cfg {Boolean} disableClear Disable showing of clear button.
10071      */
10072     disableClear : false,
10073     /**
10074      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
10075      */
10076     alwaysQuery : false,
10077     
10078     /**
10079      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
10080      */
10081     multiple : false,
10082     
10083     //private
10084     addicon : false,
10085     editicon: false,
10086     
10087     page: 0,
10088     hasQuery: false,
10089     append: false,
10090     loadNext: false,
10091     autoFocus : true,
10092     tickable : false,
10093     btnPosition : 'right',
10094     
10095     // element that contains real text value.. (when hidden is used..)
10096     
10097     getAutoCreate : function()
10098     {
10099         var cfg = false;
10100         
10101         /*
10102          *  Normal ComboBox
10103          */
10104         if(!this.tickable){
10105             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
10106             return cfg;
10107         }
10108         
10109         /*
10110          *  ComboBox with tickable selections
10111          */
10112              
10113         var align = this.labelAlign || this.parentLabelAlign();
10114         
10115         cfg = {
10116             cls : 'form-group roo-combobox-tickable' //input-group
10117         };
10118         
10119         
10120         var buttons = {
10121             tag : 'div',
10122             cls : 'tickable-buttons',
10123             cn : [
10124                 {
10125                     tag : 'button',
10126                     type : 'button',
10127                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
10128                     html : 'Edit'
10129                 },
10130                 {
10131                     tag : 'button',
10132                     type : 'button',
10133                     name : 'ok',
10134                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
10135                     html : 'Done'
10136                 },
10137                 {
10138                     tag : 'button',
10139                     type : 'button',
10140                     name : 'cancel',
10141                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
10142                     html : 'Cancel'
10143                 }
10144             ]
10145         };
10146         
10147         var _this = this;
10148         Roo.each(buttons.cn, function(c){
10149             if (_this.size) {
10150                 c.cls += ' btn-' + _this.size;
10151             }
10152
10153             if (_this.disabled) {
10154                 c.disabled = true;
10155             }
10156         });
10157         
10158         var box = {
10159             tag: 'div',
10160             cn: [
10161                 {
10162                     tag: 'input',
10163                     type : 'hidden',
10164                     cls: 'form-hidden-field'
10165                 },
10166                 {
10167                     tag: 'ul',
10168                     cls: 'select2-choices',
10169                     cn:[
10170                         {
10171                             tag: 'li',
10172                             cls: 'select2-search-field',
10173                             cn: [
10174
10175                                 buttons
10176                             ]
10177                         }
10178                     ]
10179                 }
10180             ]
10181         }
10182         
10183         var combobox = {
10184             cls: 'select2-container input-group select2-container-multi',
10185             cn: [
10186                 box,
10187                 {
10188                     tag: 'ul',
10189                     cls: 'typeahead typeahead-long dropdown-menu',
10190                     style: 'display:none; max-height:' + this.maxHeight + 'px;'
10191                 }
10192             ]
10193         };
10194         
10195         if (align ==='left' && this.fieldLabel.length) {
10196             
10197                 Roo.log("left and has label");
10198                 cfg.cn = [
10199                     
10200                     {
10201                         tag: 'label',
10202                         'for' :  id,
10203                         cls : 'control-label col-sm-' + this.labelWidth,
10204                         html : this.fieldLabel
10205                         
10206                     },
10207                     {
10208                         cls : "col-sm-" + (12 - this.labelWidth), 
10209                         cn: [
10210                             combobox
10211                         ]
10212                     }
10213                     
10214                 ];
10215         } else if ( this.fieldLabel.length) {
10216                 Roo.log(" label");
10217                  cfg.cn = [
10218                    
10219                     {
10220                         tag: 'label',
10221                         //cls : 'input-group-addon',
10222                         html : this.fieldLabel
10223                         
10224                     },
10225                     
10226                     combobox
10227                     
10228                 ];
10229
10230         } else {
10231             
10232                 Roo.log(" no label && no align");
10233                 cfg = combobox
10234                      
10235                 
10236         }
10237          
10238         var settings=this;
10239         ['xs','sm','md','lg'].map(function(size){
10240             if (settings[size]) {
10241                 cfg.cls += ' col-' + size + '-' + settings[size];
10242             }
10243         });
10244         
10245         return cfg;
10246         
10247     },
10248     
10249     // private
10250     initEvents: function()
10251     {
10252         
10253         if (!this.store) {
10254             throw "can not find store for combo";
10255         }
10256         this.store = Roo.factory(this.store, Roo.data);
10257         
10258         if(this.tickable){
10259             this.initTickableEvnets();
10260             return;
10261         }
10262         
10263         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
10264         
10265         
10266         if(this.hiddenName){
10267             
10268             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10269             
10270             this.hiddenField.dom.value =
10271                 this.hiddenValue !== undefined ? this.hiddenValue :
10272                 this.value !== undefined ? this.value : '';
10273
10274             // prevent input submission
10275             this.el.dom.removeAttribute('name');
10276             this.hiddenField.dom.setAttribute('name', this.hiddenName);
10277              
10278              
10279         }
10280         //if(Roo.isGecko){
10281         //    this.el.dom.setAttribute('autocomplete', 'off');
10282         //}
10283
10284         var cls = 'x-combo-list';
10285         this.list = this.el.select('ul.dropdown-menu',true).first();
10286
10287         //this.list = new Roo.Layer({
10288         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
10289         //});
10290         
10291         var _this = this;
10292         
10293         (function(){
10294             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
10295             _this.list.setWidth(lw);
10296         }).defer(100);
10297         
10298         this.list.on('mouseover', this.onViewOver, this);
10299         this.list.on('mousemove', this.onViewMove, this);
10300         
10301         this.list.on('scroll', this.onViewScroll, this);
10302         
10303         /*
10304         this.list.swallowEvent('mousewheel');
10305         this.assetHeight = 0;
10306
10307         if(this.title){
10308             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
10309             this.assetHeight += this.header.getHeight();
10310         }
10311
10312         this.innerList = this.list.createChild({cls:cls+'-inner'});
10313         this.innerList.on('mouseover', this.onViewOver, this);
10314         this.innerList.on('mousemove', this.onViewMove, this);
10315         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
10316         
10317         if(this.allowBlank && !this.pageSize && !this.disableClear){
10318             this.footer = this.list.createChild({cls:cls+'-ft'});
10319             this.pageTb = new Roo.Toolbar(this.footer);
10320            
10321         }
10322         if(this.pageSize){
10323             this.footer = this.list.createChild({cls:cls+'-ft'});
10324             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
10325                     {pageSize: this.pageSize});
10326             
10327         }
10328         
10329         if (this.pageTb && this.allowBlank && !this.disableClear) {
10330             var _this = this;
10331             this.pageTb.add(new Roo.Toolbar.Fill(), {
10332                 cls: 'x-btn-icon x-btn-clear',
10333                 text: '&#160;',
10334                 handler: function()
10335                 {
10336                     _this.collapse();
10337                     _this.clearValue();
10338                     _this.onSelect(false, -1);
10339                 }
10340             });
10341         }
10342         if (this.footer) {
10343             this.assetHeight += this.footer.getHeight();
10344         }
10345         */
10346             
10347         if(!this.tpl){
10348             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
10349         }
10350
10351         this.view = new Roo.View(this.el.select('ul.dropdown-menu',true).first(), this.tpl, {
10352             singleSelect:true, store: this.store, selectedClass: this.selectedClass
10353         });
10354         //this.view.wrapEl.setDisplayed(false);
10355         this.view.on('click', this.onViewClick, this);
10356         
10357         
10358         
10359         this.store.on('beforeload', this.onBeforeLoad, this);
10360         this.store.on('load', this.onLoad, this);
10361         this.store.on('loadexception', this.onLoadException, this);
10362         /*
10363         if(this.resizable){
10364             this.resizer = new Roo.Resizable(this.list,  {
10365                pinned:true, handles:'se'
10366             });
10367             this.resizer.on('resize', function(r, w, h){
10368                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
10369                 this.listWidth = w;
10370                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
10371                 this.restrictHeight();
10372             }, this);
10373             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
10374         }
10375         */
10376         if(!this.editable){
10377             this.editable = true;
10378             this.setEditable(false);
10379         }
10380         
10381         /*
10382         
10383         if (typeof(this.events.add.listeners) != 'undefined') {
10384             
10385             this.addicon = this.wrap.createChild(
10386                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
10387        
10388             this.addicon.on('click', function(e) {
10389                 this.fireEvent('add', this);
10390             }, this);
10391         }
10392         if (typeof(this.events.edit.listeners) != 'undefined') {
10393             
10394             this.editicon = this.wrap.createChild(
10395                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
10396             if (this.addicon) {
10397                 this.editicon.setStyle('margin-left', '40px');
10398             }
10399             this.editicon.on('click', function(e) {
10400                 
10401                 // we fire even  if inothing is selected..
10402                 this.fireEvent('edit', this, this.lastData );
10403                 
10404             }, this);
10405         }
10406         */
10407         
10408         this.keyNav = new Roo.KeyNav(this.inputEl(), {
10409             "up" : function(e){
10410                 this.inKeyMode = true;
10411                 this.selectPrev();
10412             },
10413
10414             "down" : function(e){
10415                 if(!this.isExpanded()){
10416                     this.onTriggerClick();
10417                 }else{
10418                     this.inKeyMode = true;
10419                     this.selectNext();
10420                 }
10421             },
10422
10423             "enter" : function(e){
10424 //                this.onViewClick();
10425                 //return true;
10426                 this.collapse();
10427                 
10428                 if(this.fireEvent("specialkey", this, e)){
10429                     this.onViewClick(false);
10430                 }
10431                 
10432                 return true;
10433             },
10434
10435             "esc" : function(e){
10436                 this.collapse();
10437             },
10438
10439             "tab" : function(e){
10440                 this.collapse();
10441                 
10442                 if(this.fireEvent("specialkey", this, e)){
10443                     this.onViewClick(false);
10444                 }
10445                 
10446                 return true;
10447             },
10448
10449             scope : this,
10450
10451             doRelay : function(foo, bar, hname){
10452                 if(hname == 'down' || this.scope.isExpanded()){
10453                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
10454                 }
10455                 return true;
10456             },
10457
10458             forceKeyDown: true
10459         });
10460         
10461         
10462         this.queryDelay = Math.max(this.queryDelay || 10,
10463                 this.mode == 'local' ? 10 : 250);
10464         
10465         
10466         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
10467         
10468         if(this.typeAhead){
10469             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
10470         }
10471         if(this.editable !== false){
10472             this.inputEl().on("keyup", this.onKeyUp, this);
10473         }
10474         if(this.forceSelection){
10475             this.inputEl().on('blur', this.doForce, this);
10476         }
10477         
10478         if(this.multiple){
10479             this.choices = this.el.select('ul.select2-choices', true).first();
10480             this.searchField = this.el.select('ul li.select2-search-field', true).first();
10481         }
10482     },
10483     
10484     initTickableEvnets: function()
10485     {   
10486         if(this.hiddenName){
10487             
10488             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10489             
10490             this.hiddenField.dom.value =
10491                 this.hiddenValue !== undefined ? this.hiddenValue :
10492                 this.value !== undefined ? this.value : '';
10493
10494             // prevent input submission
10495             this.el.dom.removeAttribute('name');
10496             this.hiddenField.dom.setAttribute('name', this.hiddenName);
10497              
10498              
10499         }
10500         
10501         this.list = this.el.select('ul.dropdown-menu',true).first();
10502         
10503         this.choices = this.el.select('ul.select2-choices', true).first();
10504         this.searchField = this.el.select('ul li.select2-search-field', true).first();
10505         
10506         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
10507         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10508         
10509         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
10510         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
10511         
10512         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
10513         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
10514         
10515         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
10516         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
10517         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
10518         
10519         this.okBtn.hide();
10520         this.cancelBtn.hide();
10521         
10522         var _this = this;
10523         
10524         (function(){
10525             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
10526             _this.list.setWidth(lw);
10527         }).defer(100);
10528         
10529         this.list.on('mouseover', this.onViewOver, this);
10530         this.list.on('mousemove', this.onViewMove, this);
10531         
10532         this.list.on('scroll', this.onViewScroll, this);
10533         
10534         if(!this.tpl){
10535             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>';
10536         }
10537
10538         this.view = new Roo.View(this.el.select('ul.dropdown-menu',true).first(), this.tpl, {
10539             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
10540         });
10541         
10542         //this.view.wrapEl.setDisplayed(false);
10543         this.view.on('click', this.onViewClick, this);
10544         
10545         
10546         
10547         this.store.on('beforeload', this.onBeforeLoad, this);
10548         this.store.on('load', this.onLoad, this);
10549         this.store.on('loadexception', this.onLoadException, this);
10550         
10551 //        this.keyNav = new Roo.KeyNav(this.inputEl(), {
10552 //            "up" : function(e){
10553 //                this.inKeyMode = true;
10554 //                this.selectPrev();
10555 //            },
10556 //
10557 //            "down" : function(e){
10558 //                if(!this.isExpanded()){
10559 //                    this.onTriggerClick();
10560 //                }else{
10561 //                    this.inKeyMode = true;
10562 //                    this.selectNext();
10563 //                }
10564 //            },
10565 //
10566 //            "enter" : function(e){
10567 ////                this.onViewClick();
10568 //                //return true;
10569 //                this.collapse();
10570 //                
10571 //                if(this.fireEvent("specialkey", this, e)){
10572 //                    this.onViewClick(false);
10573 //                }
10574 //                
10575 //                return true;
10576 //            },
10577 //
10578 //            "esc" : function(e){
10579 //                this.collapse();
10580 //            },
10581 //
10582 //            "tab" : function(e){
10583 //                this.collapse();
10584 //                
10585 //                if(this.fireEvent("specialkey", this, e)){
10586 //                    this.onViewClick(false);
10587 //                }
10588 //                
10589 //                return true;
10590 //            },
10591 //
10592 //            scope : this,
10593 //
10594 //            doRelay : function(foo, bar, hname){
10595 //                if(hname == 'down' || this.scope.isExpanded()){
10596 //                   return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
10597 //                }
10598 //                return true;
10599 //            },
10600 //
10601 //            forceKeyDown: true
10602 //        });
10603         
10604         
10605         this.queryDelay = Math.max(this.queryDelay || 10,
10606                 this.mode == 'local' ? 10 : 250);
10607         
10608         
10609         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
10610         
10611         if(this.typeAhead){
10612             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
10613         }
10614     },
10615
10616     onDestroy : function(){
10617         if(this.view){
10618             this.view.setStore(null);
10619             this.view.el.removeAllListeners();
10620             this.view.el.remove();
10621             this.view.purgeListeners();
10622         }
10623         if(this.list){
10624             this.list.dom.innerHTML  = '';
10625         }
10626         
10627         if(this.store){
10628             this.store.un('beforeload', this.onBeforeLoad, this);
10629             this.store.un('load', this.onLoad, this);
10630             this.store.un('loadexception', this.onLoadException, this);
10631         }
10632         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
10633     },
10634
10635     // private
10636     fireKey : function(e){
10637         if(e.isNavKeyPress() && !this.list.isVisible()){
10638             this.fireEvent("specialkey", this, e);
10639         }
10640     },
10641
10642     // private
10643     onResize: function(w, h){
10644 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
10645 //        
10646 //        if(typeof w != 'number'){
10647 //            // we do not handle it!?!?
10648 //            return;
10649 //        }
10650 //        var tw = this.trigger.getWidth();
10651 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
10652 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
10653 //        var x = w - tw;
10654 //        this.inputEl().setWidth( this.adjustWidth('input', x));
10655 //            
10656 //        //this.trigger.setStyle('left', x+'px');
10657 //        
10658 //        if(this.list && this.listWidth === undefined){
10659 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
10660 //            this.list.setWidth(lw);
10661 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
10662 //        }
10663         
10664     
10665         
10666     },
10667
10668     /**
10669      * Allow or prevent the user from directly editing the field text.  If false is passed,
10670      * the user will only be able to select from the items defined in the dropdown list.  This method
10671      * is the runtime equivalent of setting the 'editable' config option at config time.
10672      * @param {Boolean} value True to allow the user to directly edit the field text
10673      */
10674     setEditable : function(value){
10675         if(value == this.editable){
10676             return;
10677         }
10678         this.editable = value;
10679         if(!value){
10680             this.inputEl().dom.setAttribute('readOnly', true);
10681             this.inputEl().on('mousedown', this.onTriggerClick,  this);
10682             this.inputEl().addClass('x-combo-noedit');
10683         }else{
10684             this.inputEl().dom.setAttribute('readOnly', false);
10685             this.inputEl().un('mousedown', this.onTriggerClick,  this);
10686             this.inputEl().removeClass('x-combo-noedit');
10687         }
10688     },
10689
10690     // private
10691     
10692     onBeforeLoad : function(combo,opts){
10693         if(!this.hasFocus){
10694             return;
10695         }
10696          if (!opts.add) {
10697             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
10698          }
10699         this.restrictHeight();
10700         this.selectedIndex = -1;
10701     },
10702
10703     // private
10704     onLoad : function(){
10705         
10706         this.hasQuery = false;
10707         
10708         if(!this.hasFocus){
10709             return;
10710         }
10711         
10712         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
10713             this.loading.hide();
10714         }
10715         
10716         if(this.store.getCount() > 0){
10717             this.expand();
10718             this.restrictHeight();
10719             if(this.lastQuery == this.allQuery){
10720                 if(this.editable && !this.tickable){
10721                     this.inputEl().dom.select();
10722                 }
10723                 if(!this.selectByValue(this.value, true) && this.autoFocus){
10724                     this.select(0, true);
10725                 }
10726             }else{
10727                 if(this.autoFocus){
10728                     this.selectNext();
10729                 }
10730                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
10731                     this.taTask.delay(this.typeAheadDelay);
10732                 }
10733             }
10734         }else{
10735             this.onEmptyResults();
10736         }
10737         
10738         //this.el.focus();
10739     },
10740     // private
10741     onLoadException : function()
10742     {
10743         this.hasQuery = false;
10744         
10745         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
10746             this.loading.hide();
10747         }
10748         
10749         this.collapse();
10750         Roo.log(this.store.reader.jsonData);
10751         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
10752             // fixme
10753             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
10754         }
10755         
10756         
10757     },
10758     // private
10759     onTypeAhead : function(){
10760         if(this.store.getCount() > 0){
10761             var r = this.store.getAt(0);
10762             var newValue = r.data[this.displayField];
10763             var len = newValue.length;
10764             var selStart = this.getRawValue().length;
10765             
10766             if(selStart != len){
10767                 this.setRawValue(newValue);
10768                 this.selectText(selStart, newValue.length);
10769             }
10770         }
10771     },
10772
10773     // private
10774     onSelect : function(record, index){
10775         
10776         if(this.fireEvent('beforeselect', this, record, index) !== false){
10777         
10778             this.setFromData(index > -1 ? record.data : false);
10779             
10780             this.collapse();
10781             this.fireEvent('select', this, record, index);
10782         }
10783     },
10784
10785     /**
10786      * Returns the currently selected field value or empty string if no value is set.
10787      * @return {String} value The selected value
10788      */
10789     getValue : function(){
10790         
10791         if(this.multiple){
10792             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
10793         }
10794         
10795         if(this.valueField){
10796             return typeof this.value != 'undefined' ? this.value : '';
10797         }else{
10798             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
10799         }
10800     },
10801
10802     /**
10803      * Clears any text/value currently set in the field
10804      */
10805     clearValue : function(){
10806         if(this.hiddenField){
10807             this.hiddenField.dom.value = '';
10808         }
10809         this.value = '';
10810         this.setRawValue('');
10811         this.lastSelectionText = '';
10812         
10813     },
10814
10815     /**
10816      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
10817      * will be displayed in the field.  If the value does not match the data value of an existing item,
10818      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
10819      * Otherwise the field will be blank (although the value will still be set).
10820      * @param {String} value The value to match
10821      */
10822     setValue : function(v){
10823         if(this.multiple){
10824             this.syncValue();
10825             return;
10826         }
10827         
10828         var text = v;
10829         if(this.valueField){
10830             var r = this.findRecord(this.valueField, v);
10831             if(r){
10832                 text = r.data[this.displayField];
10833             }else if(this.valueNotFoundText !== undefined){
10834                 text = this.valueNotFoundText;
10835             }
10836         }
10837         this.lastSelectionText = text;
10838         if(this.hiddenField){
10839             this.hiddenField.dom.value = v;
10840         }
10841         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
10842         this.value = v;
10843     },
10844     /**
10845      * @property {Object} the last set data for the element
10846      */
10847     
10848     lastData : false,
10849     /**
10850      * Sets the value of the field based on a object which is related to the record format for the store.
10851      * @param {Object} value the value to set as. or false on reset?
10852      */
10853     setFromData : function(o){
10854         
10855         if(this.multiple){
10856             this.addItem(o);
10857             return;
10858         }
10859             
10860         var dv = ''; // display value
10861         var vv = ''; // value value..
10862         this.lastData = o;
10863         if (this.displayField) {
10864             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
10865         } else {
10866             // this is an error condition!!!
10867             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
10868         }
10869         
10870         if(this.valueField){
10871             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
10872         }
10873         
10874         if(this.hiddenField){
10875             this.hiddenField.dom.value = vv;
10876             
10877             this.lastSelectionText = dv;
10878             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
10879             this.value = vv;
10880             return;
10881         }
10882         // no hidden field.. - we store the value in 'value', but still display
10883         // display field!!!!
10884         this.lastSelectionText = dv;
10885         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
10886         this.value = vv;
10887         
10888         
10889     },
10890     // private
10891     reset : function(){
10892         // overridden so that last data is reset..
10893         this.setValue(this.originalValue);
10894         this.clearInvalid();
10895         this.lastData = false;
10896         if (this.view) {
10897             this.view.clearSelections();
10898         }
10899     },
10900     // private
10901     findRecord : function(prop, value){
10902         var record;
10903         if(this.store.getCount() > 0){
10904             this.store.each(function(r){
10905                 if(r.data[prop] == value){
10906                     record = r;
10907                     return false;
10908                 }
10909                 return true;
10910             });
10911         }
10912         return record;
10913     },
10914     
10915     getName: function()
10916     {
10917         // returns hidden if it's set..
10918         if (!this.rendered) {return ''};
10919         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
10920         
10921     },
10922     // private
10923     onViewMove : function(e, t){
10924         this.inKeyMode = false;
10925     },
10926
10927     // private
10928     onViewOver : function(e, t){
10929         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
10930             return;
10931         }
10932         var item = this.view.findItemFromChild(t);
10933         
10934         if(item){
10935             var index = this.view.indexOf(item);
10936             this.select(index, false);
10937         }
10938     },
10939
10940     // private
10941     onViewClick : function(view, doFocus, el, e)
10942     {
10943         var index = this.view.getSelectedIndexes()[0];
10944         
10945         var r = this.store.getAt(index);
10946         
10947         if(this.tickable){
10948             
10949             if(e.getTarget().nodeName.toLowerCase() != 'input'){
10950                 return;
10951             }
10952             
10953             var rm = false;
10954             var _this = this;
10955             
10956             Roo.each(this.tickItems, function(v,k){
10957                 
10958                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
10959                     _this.tickItems.splice(k, 1);
10960                     rm = true;
10961                     return;
10962                 }
10963             })
10964             
10965             if(rm){
10966                 return;
10967             }
10968             
10969             this.tickItems.push(r.data);
10970             return;
10971         }
10972         
10973         if(r){
10974             this.onSelect(r, index);
10975         }
10976         if(doFocus !== false && !this.blockFocus){
10977             this.inputEl().focus();
10978         }
10979     },
10980
10981     // private
10982     restrictHeight : function(){
10983         //this.innerList.dom.style.height = '';
10984         //var inner = this.innerList.dom;
10985         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
10986         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
10987         //this.list.beginUpdate();
10988         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
10989         this.list.alignTo(this.inputEl(), this.listAlign);
10990         //this.list.endUpdate();
10991     },
10992
10993     // private
10994     onEmptyResults : function(){
10995         this.collapse();
10996     },
10997
10998     /**
10999      * Returns true if the dropdown list is expanded, else false.
11000      */
11001     isExpanded : function(){
11002         return this.list.isVisible();
11003     },
11004
11005     /**
11006      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
11007      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11008      * @param {String} value The data value of the item to select
11009      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11010      * selected item if it is not currently in view (defaults to true)
11011      * @return {Boolean} True if the value matched an item in the list, else false
11012      */
11013     selectByValue : function(v, scrollIntoView){
11014         if(v !== undefined && v !== null){
11015             var r = this.findRecord(this.valueField || this.displayField, v);
11016             if(r){
11017                 this.select(this.store.indexOf(r), scrollIntoView);
11018                 return true;
11019             }
11020         }
11021         return false;
11022     },
11023
11024     /**
11025      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
11026      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11027      * @param {Number} index The zero-based index of the list item to select
11028      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11029      * selected item if it is not currently in view (defaults to true)
11030      */
11031     select : function(index, scrollIntoView){
11032         this.selectedIndex = index;
11033         this.view.select(index);
11034         if(scrollIntoView !== false){
11035             var el = this.view.getNode(index);
11036             if(el){
11037                 //this.innerList.scrollChildIntoView(el, false);
11038                 
11039             }
11040         }
11041     },
11042
11043     // private
11044     selectNext : function(){
11045         var ct = this.store.getCount();
11046         if(ct > 0){
11047             if(this.selectedIndex == -1){
11048                 this.select(0);
11049             }else if(this.selectedIndex < ct-1){
11050                 this.select(this.selectedIndex+1);
11051             }
11052         }
11053     },
11054
11055     // private
11056     selectPrev : function(){
11057         var ct = this.store.getCount();
11058         if(ct > 0){
11059             if(this.selectedIndex == -1){
11060                 this.select(0);
11061             }else if(this.selectedIndex != 0){
11062                 this.select(this.selectedIndex-1);
11063             }
11064         }
11065     },
11066
11067     // private
11068     onKeyUp : function(e){
11069         if(this.editable !== false && !e.isSpecialKey()){
11070             this.lastKey = e.getKey();
11071             this.dqTask.delay(this.queryDelay);
11072         }
11073     },
11074
11075     // private
11076     validateBlur : function(){
11077         return !this.list || !this.list.isVisible();   
11078     },
11079
11080     // private
11081     initQuery : function(){
11082         this.doQuery(this.getRawValue());
11083     },
11084
11085     // private
11086     doForce : function(){
11087         if(this.inputEl().dom.value.length > 0){
11088             this.inputEl().dom.value =
11089                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
11090              
11091         }
11092     },
11093
11094     /**
11095      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
11096      * query allowing the query action to be canceled if needed.
11097      * @param {String} query The SQL query to execute
11098      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
11099      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
11100      * saved in the current store (defaults to false)
11101      */
11102     doQuery : function(q, forceAll){
11103         
11104         if(q === undefined || q === null){
11105             q = '';
11106         }
11107         var qe = {
11108             query: q,
11109             forceAll: forceAll,
11110             combo: this,
11111             cancel:false
11112         };
11113         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
11114             return false;
11115         }
11116         q = qe.query;
11117         
11118         forceAll = qe.forceAll;
11119         if(forceAll === true || (q.length >= this.minChars)){
11120             
11121             this.hasQuery = true;
11122             
11123             if(this.lastQuery != q || this.alwaysQuery){
11124                 this.lastQuery = q;
11125                 if(this.mode == 'local'){
11126                     this.selectedIndex = -1;
11127                     if(forceAll){
11128                         this.store.clearFilter();
11129                     }else{
11130                         this.store.filter(this.displayField, q);
11131                     }
11132                     this.onLoad();
11133                 }else{
11134                     this.store.baseParams[this.queryParam] = q;
11135                     
11136                     var options = {params : this.getParams(q)};
11137                     
11138                     if(this.loadNext){
11139                         options.add = true;
11140                         options.params.start = this.page * this.pageSize;
11141                     }
11142                     
11143                     this.store.load(options);
11144                     /*
11145                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
11146                      *  we should expand the list on onLoad
11147                      *  so command out it
11148                      */
11149 //                    this.expand();
11150                 }
11151             }else{
11152                 this.selectedIndex = -1;
11153                 this.onLoad();   
11154             }
11155         }
11156         
11157         this.loadNext = false;
11158     },
11159
11160     // private
11161     getParams : function(q){
11162         var p = {};
11163         //p[this.queryParam] = q;
11164         
11165         if(this.pageSize){
11166             p.start = 0;
11167             p.limit = this.pageSize;
11168         }
11169         return p;
11170     },
11171
11172     /**
11173      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
11174      */
11175     collapse : function(){
11176         if(!this.isExpanded()){
11177             return;
11178         }
11179         
11180         this.list.hide();
11181         
11182         if(this.tickable){
11183             this.okBtn.hide();
11184             this.cancelBtn.hide();
11185             this.trigger.show();
11186         }
11187         
11188         Roo.get(document).un('mousedown', this.collapseIf, this);
11189         Roo.get(document).un('mousewheel', this.collapseIf, this);
11190         if (!this.editable) {
11191             Roo.get(document).un('keydown', this.listKeyPress, this);
11192         }
11193         this.fireEvent('collapse', this);
11194     },
11195
11196     // private
11197     collapseIf : function(e){
11198         var in_combo  = e.within(this.el);
11199         var in_list =  e.within(this.list);
11200         
11201         if (in_combo || in_list) {
11202             //e.stopPropagation();
11203             return;
11204         }
11205         
11206         if(this.tickable){
11207             this.onTickableFooterButtonClick(e, false, false);
11208         }
11209
11210         this.collapse();
11211         
11212     },
11213
11214     /**
11215      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
11216      */
11217     expand : function(){
11218        
11219         if(this.isExpanded() || !this.hasFocus){
11220             return;
11221         }
11222          Roo.log('expand');
11223         this.list.alignTo(this.inputEl(), this.listAlign);
11224         this.list.show();
11225         
11226         if(this.tickable){
11227             
11228             this.tickItems = Roo.apply([], this.item);
11229             
11230             this.okBtn.show();
11231             this.cancelBtn.show();
11232             this.trigger.hide();
11233             
11234         }
11235         
11236         Roo.get(document).on('mousedown', this.collapseIf, this);
11237         Roo.get(document).on('mousewheel', this.collapseIf, this);
11238         if (!this.editable) {
11239             Roo.get(document).on('keydown', this.listKeyPress, this);
11240         }
11241         
11242         this.fireEvent('expand', this);
11243     },
11244
11245     // private
11246     // Implements the default empty TriggerField.onTriggerClick function
11247     onTriggerClick : function()
11248     {
11249         Roo.log('trigger click');
11250         
11251         if(this.disabled){
11252             return;
11253         }
11254         
11255         if(this.tickable){
11256             this.onTickableTriggerClick();
11257             return;
11258         }
11259         
11260         this.page = 0;
11261         this.loadNext = false;
11262         
11263         if(this.isExpanded()){
11264             this.collapse();
11265             if (!this.blockFocus) {
11266                 this.inputEl().focus();
11267             }
11268             
11269         }else {
11270             this.hasFocus = true;
11271             if(this.triggerAction == 'all') {
11272                 this.doQuery(this.allQuery, true);
11273             } else {
11274                 this.doQuery(this.getRawValue());
11275             }
11276             if (!this.blockFocus) {
11277                 this.inputEl().focus();
11278             }
11279         }
11280     },
11281     
11282     onTickableTriggerClick : function()
11283     {
11284         this.page = 0;
11285         this.loadNext = false;
11286         this.hasFocus = true;
11287         
11288         if(this.triggerAction == 'all') {
11289             this.doQuery(this.allQuery, true);
11290         } else {
11291             this.doQuery(this.getRawValue());
11292         }
11293     },
11294     
11295     listKeyPress : function(e)
11296     {
11297         //Roo.log('listkeypress');
11298         // scroll to first matching element based on key pres..
11299         if (e.isSpecialKey()) {
11300             return false;
11301         }
11302         var k = String.fromCharCode(e.getKey()).toUpperCase();
11303         //Roo.log(k);
11304         var match  = false;
11305         var csel = this.view.getSelectedNodes();
11306         var cselitem = false;
11307         if (csel.length) {
11308             var ix = this.view.indexOf(csel[0]);
11309             cselitem  = this.store.getAt(ix);
11310             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
11311                 cselitem = false;
11312             }
11313             
11314         }
11315         
11316         this.store.each(function(v) { 
11317             if (cselitem) {
11318                 // start at existing selection.
11319                 if (cselitem.id == v.id) {
11320                     cselitem = false;
11321                 }
11322                 return true;
11323             }
11324                 
11325             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
11326                 match = this.store.indexOf(v);
11327                 return false;
11328             }
11329             return true;
11330         }, this);
11331         
11332         if (match === false) {
11333             return true; // no more action?
11334         }
11335         // scroll to?
11336         this.view.select(match);
11337         var sn = Roo.get(this.view.getSelectedNodes()[0])
11338         //sn.scrollIntoView(sn.dom.parentNode, false);
11339     },
11340     
11341     onViewScroll : function(e, t){
11342         
11343         if(this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
11344             return;
11345         }
11346         
11347         this.hasQuery = true;
11348         
11349         this.loading = this.list.select('.loading', true).first();
11350         
11351         if(this.loading === null){
11352             this.list.createChild({
11353                 tag: 'div',
11354                 cls: 'loading select2-more-results select2-active',
11355                 html: 'Loading more results...'
11356             })
11357             
11358             this.loading = this.list.select('.loading', true).first();
11359             
11360             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
11361             
11362             this.loading.hide();
11363         }
11364         
11365         this.loading.show();
11366         
11367         var _combo = this;
11368         
11369         this.page++;
11370         this.loadNext = true;
11371         
11372         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
11373         
11374         return;
11375     },
11376     
11377     addItem : function(o)
11378     {   
11379         var dv = ''; // display value
11380         
11381         if (this.displayField) {
11382             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11383         } else {
11384             // this is an error condition!!!
11385             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
11386         }
11387         
11388         if(!dv.length){
11389             return;
11390         }
11391         
11392         var choice = this.choices.createChild({
11393             tag: 'li',
11394             cls: 'select2-search-choice',
11395             cn: [
11396                 {
11397                     tag: 'div',
11398                     html: dv
11399                 },
11400                 {
11401                     tag: 'a',
11402                     href: '#',
11403                     cls: 'select2-search-choice-close',
11404                     tabindex: '-1'
11405                 }
11406             ]
11407             
11408         }, this.searchField);
11409         
11410         var close = choice.select('a.select2-search-choice-close', true).first()
11411         
11412         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
11413         
11414         this.item.push(o);
11415         
11416         this.lastData = o;
11417         
11418         this.syncValue();
11419         
11420         this.inputEl().dom.value = '';
11421         
11422     },
11423     
11424     onRemoveItem : function(e, _self, o)
11425     {
11426         e.preventDefault();
11427         var index = this.item.indexOf(o.data) * 1;
11428         
11429         if( index < 0){
11430             Roo.log('not this item?!');
11431             return;
11432         }
11433         
11434         this.item.splice(index, 1);
11435         o.item.remove();
11436         
11437         this.syncValue();
11438         
11439         this.fireEvent('remove', this, e);
11440         
11441     },
11442     
11443     syncValue : function()
11444     {
11445         if(!this.item.length){
11446             this.clearValue();
11447             return;
11448         }
11449             
11450         var value = [];
11451         var _this = this;
11452         Roo.each(this.item, function(i){
11453             if(_this.valueField){
11454                 value.push(i[_this.valueField]);
11455                 return;
11456             }
11457
11458             value.push(i);
11459         });
11460
11461         this.value = value.join(',');
11462
11463         if(this.hiddenField){
11464             this.hiddenField.dom.value = this.value;
11465         }
11466     },
11467     
11468     clearItem : function()
11469     {
11470         if(!this.multiple){
11471             return;
11472         }
11473         
11474         this.item = [];
11475         
11476         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
11477            c.remove();
11478         });
11479         
11480         this.syncValue();
11481     },
11482     
11483     inputEl: function ()
11484     {
11485         if(this.tickable){
11486             return this.searchField;
11487         }
11488         return this.el.select('input.form-control',true).first();
11489     },
11490     
11491     
11492     onTickableFooterButtonClick : function(e, btn, el)
11493     {
11494         e.preventDefault();
11495         
11496         if(btn && btn.name == 'cancel'){
11497             this.tickItems = Roo.apply([], this.item);
11498             this.collapse();
11499             return;
11500         }
11501         
11502         this.clearItem();
11503         
11504         var _this = this;
11505         
11506         Roo.each(this.tickItems, function(o){
11507             _this.addItem(o);
11508         });
11509         
11510         this.collapse();
11511         
11512     }
11513     
11514     
11515
11516     /** 
11517     * @cfg {Boolean} grow 
11518     * @hide 
11519     */
11520     /** 
11521     * @cfg {Number} growMin 
11522     * @hide 
11523     */
11524     /** 
11525     * @cfg {Number} growMax 
11526     * @hide 
11527     */
11528     /**
11529      * @hide
11530      * @method autoSize
11531      */
11532 });
11533 /*
11534  * Based on:
11535  * Ext JS Library 1.1.1
11536  * Copyright(c) 2006-2007, Ext JS, LLC.
11537  *
11538  * Originally Released Under LGPL - original licence link has changed is not relivant.
11539  *
11540  * Fork - LGPL
11541  * <script type="text/javascript">
11542  */
11543
11544 /**
11545  * @class Roo.View
11546  * @extends Roo.util.Observable
11547  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
11548  * This class also supports single and multi selection modes. <br>
11549  * Create a data model bound view:
11550  <pre><code>
11551  var store = new Roo.data.Store(...);
11552
11553  var view = new Roo.View({
11554     el : "my-element",
11555     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
11556  
11557     singleSelect: true,
11558     selectedClass: "ydataview-selected",
11559     store: store
11560  });
11561
11562  // listen for node click?
11563  view.on("click", function(vw, index, node, e){
11564  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
11565  });
11566
11567  // load XML data
11568  dataModel.load("foobar.xml");
11569  </code></pre>
11570  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
11571  * <br><br>
11572  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
11573  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
11574  * 
11575  * Note: old style constructor is still suported (container, template, config)
11576  * 
11577  * @constructor
11578  * Create a new View
11579  * @param {Object} config The config object
11580  * 
11581  */
11582 Roo.View = function(config, depreciated_tpl, depreciated_config){
11583     
11584     this.parent = false;
11585     
11586     if (typeof(depreciated_tpl) == 'undefined') {
11587         // new way.. - universal constructor.
11588         Roo.apply(this, config);
11589         this.el  = Roo.get(this.el);
11590     } else {
11591         // old format..
11592         this.el  = Roo.get(config);
11593         this.tpl = depreciated_tpl;
11594         Roo.apply(this, depreciated_config);
11595     }
11596     this.wrapEl  = this.el.wrap().wrap();
11597     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
11598     
11599     
11600     if(typeof(this.tpl) == "string"){
11601         this.tpl = new Roo.Template(this.tpl);
11602     } else {
11603         // support xtype ctors..
11604         this.tpl = new Roo.factory(this.tpl, Roo);
11605     }
11606     
11607     
11608     this.tpl.compile();
11609     
11610     /** @private */
11611     this.addEvents({
11612         /**
11613          * @event beforeclick
11614          * Fires before a click is processed. Returns false to cancel the default action.
11615          * @param {Roo.View} this
11616          * @param {Number} index The index of the target node
11617          * @param {HTMLElement} node The target node
11618          * @param {Roo.EventObject} e The raw event object
11619          */
11620             "beforeclick" : true,
11621         /**
11622          * @event click
11623          * Fires when a template node is clicked.
11624          * @param {Roo.View} this
11625          * @param {Number} index The index of the target node
11626          * @param {HTMLElement} node The target node
11627          * @param {Roo.EventObject} e The raw event object
11628          */
11629             "click" : true,
11630         /**
11631          * @event dblclick
11632          * Fires when a template node is double clicked.
11633          * @param {Roo.View} this
11634          * @param {Number} index The index of the target node
11635          * @param {HTMLElement} node The target node
11636          * @param {Roo.EventObject} e The raw event object
11637          */
11638             "dblclick" : true,
11639         /**
11640          * @event contextmenu
11641          * Fires when a template node is right clicked.
11642          * @param {Roo.View} this
11643          * @param {Number} index The index of the target node
11644          * @param {HTMLElement} node The target node
11645          * @param {Roo.EventObject} e The raw event object
11646          */
11647             "contextmenu" : true,
11648         /**
11649          * @event selectionchange
11650          * Fires when the selected nodes change.
11651          * @param {Roo.View} this
11652          * @param {Array} selections Array of the selected nodes
11653          */
11654             "selectionchange" : true,
11655     
11656         /**
11657          * @event beforeselect
11658          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
11659          * @param {Roo.View} this
11660          * @param {HTMLElement} node The node to be selected
11661          * @param {Array} selections Array of currently selected nodes
11662          */
11663             "beforeselect" : true,
11664         /**
11665          * @event preparedata
11666          * Fires on every row to render, to allow you to change the data.
11667          * @param {Roo.View} this
11668          * @param {Object} data to be rendered (change this)
11669          */
11670           "preparedata" : true
11671           
11672           
11673         });
11674
11675
11676
11677     this.el.on({
11678         "click": this.onClick,
11679         "dblclick": this.onDblClick,
11680         "contextmenu": this.onContextMenu,
11681         scope:this
11682     });
11683
11684     this.selections = [];
11685     this.nodes = [];
11686     this.cmp = new Roo.CompositeElementLite([]);
11687     if(this.store){
11688         this.store = Roo.factory(this.store, Roo.data);
11689         this.setStore(this.store, true);
11690     }
11691     
11692     if ( this.footer && this.footer.xtype) {
11693            
11694          var fctr = this.wrapEl.appendChild(document.createElement("div"));
11695         
11696         this.footer.dataSource = this.store
11697         this.footer.container = fctr;
11698         this.footer = Roo.factory(this.footer, Roo);
11699         fctr.insertFirst(this.el);
11700         
11701         // this is a bit insane - as the paging toolbar seems to detach the el..
11702 //        dom.parentNode.parentNode.parentNode
11703          // they get detached?
11704     }
11705     
11706     
11707     Roo.View.superclass.constructor.call(this);
11708     
11709     
11710 };
11711
11712 Roo.extend(Roo.View, Roo.util.Observable, {
11713     
11714      /**
11715      * @cfg {Roo.data.Store} store Data store to load data from.
11716      */
11717     store : false,
11718     
11719     /**
11720      * @cfg {String|Roo.Element} el The container element.
11721      */
11722     el : '',
11723     
11724     /**
11725      * @cfg {String|Roo.Template} tpl The template used by this View 
11726      */
11727     tpl : false,
11728     /**
11729      * @cfg {String} dataName the named area of the template to use as the data area
11730      *                          Works with domtemplates roo-name="name"
11731      */
11732     dataName: false,
11733     /**
11734      * @cfg {String} selectedClass The css class to add to selected nodes
11735      */
11736     selectedClass : "x-view-selected",
11737      /**
11738      * @cfg {String} emptyText The empty text to show when nothing is loaded.
11739      */
11740     emptyText : "",
11741     
11742     /**
11743      * @cfg {String} text to display on mask (default Loading)
11744      */
11745     mask : false,
11746     /**
11747      * @cfg {Boolean} multiSelect Allow multiple selection
11748      */
11749     multiSelect : false,
11750     /**
11751      * @cfg {Boolean} singleSelect Allow single selection
11752      */
11753     singleSelect:  false,
11754     
11755     /**
11756      * @cfg {Boolean} toggleSelect - selecting 
11757      */
11758     toggleSelect : false,
11759     
11760     /**
11761      * @cfg {Boolean} tickable - selecting 
11762      */
11763     tickable : false,
11764     
11765     /**
11766      * Returns the element this view is bound to.
11767      * @return {Roo.Element}
11768      */
11769     getEl : function(){
11770         return this.wrapEl;
11771     },
11772     
11773     
11774
11775     /**
11776      * Refreshes the view. - called by datachanged on the store. - do not call directly.
11777      */
11778     refresh : function(){
11779         Roo.log('refresh');
11780         var t = this.tpl;
11781         
11782         // if we are using something like 'domtemplate', then
11783         // the what gets used is:
11784         // t.applySubtemplate(NAME, data, wrapping data..)
11785         // the outer template then get' applied with
11786         //     the store 'extra data'
11787         // and the body get's added to the
11788         //      roo-name="data" node?
11789         //      <span class='roo-tpl-{name}'></span> ?????
11790         
11791         
11792         
11793         this.clearSelections();
11794         this.el.update("");
11795         var html = [];
11796         var records = this.store.getRange();
11797         if(records.length < 1) {
11798             
11799             // is this valid??  = should it render a template??
11800             
11801             this.el.update(this.emptyText);
11802             return;
11803         }
11804         var el = this.el;
11805         if (this.dataName) {
11806             this.el.update(t.apply(this.store.meta)); //????
11807             el = this.el.child('.roo-tpl-' + this.dataName);
11808         }
11809         
11810         for(var i = 0, len = records.length; i < len; i++){
11811             var data = this.prepareData(records[i].data, i, records[i]);
11812             this.fireEvent("preparedata", this, data, i, records[i]);
11813             
11814             var d = Roo.apply({}, data);
11815             
11816             if(this.tickable){
11817                 Roo.apply(d, {'roo-id' : Roo.id()});
11818                 
11819                 var _this = this;
11820             
11821                 Roo.each(this.parent.item, function(item){
11822                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
11823                         return;
11824                     }
11825                     Roo.apply(d, {'roo-data-checked' : 'checked'});
11826                 });
11827             }
11828             
11829             html[html.length] = Roo.util.Format.trim(
11830                 this.dataName ?
11831                     t.applySubtemplate(this.dataName, d, this.store.meta) :
11832                     t.apply(d)
11833             );
11834         }
11835         
11836         
11837         
11838         el.update(html.join(""));
11839         this.nodes = el.dom.childNodes;
11840         this.updateIndexes(0);
11841     },
11842     
11843
11844     /**
11845      * Function to override to reformat the data that is sent to
11846      * the template for each node.
11847      * DEPRICATED - use the preparedata event handler.
11848      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
11849      * a JSON object for an UpdateManager bound view).
11850      */
11851     prepareData : function(data, index, record)
11852     {
11853         this.fireEvent("preparedata", this, data, index, record);
11854         return data;
11855     },
11856
11857     onUpdate : function(ds, record){
11858          Roo.log('on update');   
11859         this.clearSelections();
11860         var index = this.store.indexOf(record);
11861         var n = this.nodes[index];
11862         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
11863         n.parentNode.removeChild(n);
11864         this.updateIndexes(index, index);
11865     },
11866
11867     
11868     
11869 // --------- FIXME     
11870     onAdd : function(ds, records, index)
11871     {
11872         Roo.log(['on Add', ds, records, index] );        
11873         this.clearSelections();
11874         if(this.nodes.length == 0){
11875             this.refresh();
11876             return;
11877         }
11878         var n = this.nodes[index];
11879         for(var i = 0, len = records.length; i < len; i++){
11880             var d = this.prepareData(records[i].data, i, records[i]);
11881             if(n){
11882                 this.tpl.insertBefore(n, d);
11883             }else{
11884                 
11885                 this.tpl.append(this.el, d);
11886             }
11887         }
11888         this.updateIndexes(index);
11889     },
11890
11891     onRemove : function(ds, record, index){
11892         Roo.log('onRemove');
11893         this.clearSelections();
11894         var el = this.dataName  ?
11895             this.el.child('.roo-tpl-' + this.dataName) :
11896             this.el; 
11897         
11898         el.dom.removeChild(this.nodes[index]);
11899         this.updateIndexes(index);
11900     },
11901
11902     /**
11903      * Refresh an individual node.
11904      * @param {Number} index
11905      */
11906     refreshNode : function(index){
11907         this.onUpdate(this.store, this.store.getAt(index));
11908     },
11909
11910     updateIndexes : function(startIndex, endIndex){
11911         var ns = this.nodes;
11912         startIndex = startIndex || 0;
11913         endIndex = endIndex || ns.length - 1;
11914         for(var i = startIndex; i <= endIndex; i++){
11915             ns[i].nodeIndex = i;
11916         }
11917     },
11918
11919     /**
11920      * Changes the data store this view uses and refresh the view.
11921      * @param {Store} store
11922      */
11923     setStore : function(store, initial){
11924         if(!initial && this.store){
11925             this.store.un("datachanged", this.refresh);
11926             this.store.un("add", this.onAdd);
11927             this.store.un("remove", this.onRemove);
11928             this.store.un("update", this.onUpdate);
11929             this.store.un("clear", this.refresh);
11930             this.store.un("beforeload", this.onBeforeLoad);
11931             this.store.un("load", this.onLoad);
11932             this.store.un("loadexception", this.onLoad);
11933         }
11934         if(store){
11935           
11936             store.on("datachanged", this.refresh, this);
11937             store.on("add", this.onAdd, this);
11938             store.on("remove", this.onRemove, this);
11939             store.on("update", this.onUpdate, this);
11940             store.on("clear", this.refresh, this);
11941             store.on("beforeload", this.onBeforeLoad, this);
11942             store.on("load", this.onLoad, this);
11943             store.on("loadexception", this.onLoad, this);
11944         }
11945         
11946         if(store){
11947             this.refresh();
11948         }
11949     },
11950     /**
11951      * onbeforeLoad - masks the loading area.
11952      *
11953      */
11954     onBeforeLoad : function(store,opts)
11955     {
11956          Roo.log('onBeforeLoad');   
11957         if (!opts.add) {
11958             this.el.update("");
11959         }
11960         this.el.mask(this.mask ? this.mask : "Loading" ); 
11961     },
11962     onLoad : function ()
11963     {
11964         this.el.unmask();
11965     },
11966     
11967
11968     /**
11969      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
11970      * @param {HTMLElement} node
11971      * @return {HTMLElement} The template node
11972      */
11973     findItemFromChild : function(node){
11974         var el = this.dataName  ?
11975             this.el.child('.roo-tpl-' + this.dataName,true) :
11976             this.el.dom; 
11977         
11978         if(!node || node.parentNode == el){
11979                     return node;
11980             }
11981             var p = node.parentNode;
11982             while(p && p != el){
11983             if(p.parentNode == el){
11984                 return p;
11985             }
11986             p = p.parentNode;
11987         }
11988             return null;
11989     },
11990
11991     /** @ignore */
11992     onClick : function(e){
11993         var item = this.findItemFromChild(e.getTarget());
11994         if(item){
11995             var index = this.indexOf(item);
11996             if(this.onItemClick(item, index, e) !== false){
11997                 this.fireEvent("click", this, index, item, e);
11998             }
11999         }else{
12000             this.clearSelections();
12001         }
12002     },
12003
12004     /** @ignore */
12005     onContextMenu : function(e){
12006         var item = this.findItemFromChild(e.getTarget());
12007         if(item){
12008             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
12009         }
12010     },
12011
12012     /** @ignore */
12013     onDblClick : function(e){
12014         var item = this.findItemFromChild(e.getTarget());
12015         if(item){
12016             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
12017         }
12018     },
12019
12020     onItemClick : function(item, index, e)
12021     {
12022         if(this.fireEvent("beforeclick", this, index, item, e) === false){
12023             return false;
12024         }
12025         if (this.toggleSelect) {
12026             var m = this.isSelected(item) ? 'unselect' : 'select';
12027             Roo.log(m);
12028             var _t = this;
12029             _t[m](item, true, false);
12030             return true;
12031         }
12032         if(this.multiSelect || this.singleSelect){
12033             if(this.multiSelect && e.shiftKey && this.lastSelection){
12034                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
12035             }else{
12036                 this.select(item, this.multiSelect && e.ctrlKey);
12037                 this.lastSelection = item;
12038             }
12039             
12040             if(!this.tickable){
12041                 e.preventDefault();
12042             }
12043             
12044         }
12045         return true;
12046     },
12047
12048     /**
12049      * Get the number of selected nodes.
12050      * @return {Number}
12051      */
12052     getSelectionCount : function(){
12053         return this.selections.length;
12054     },
12055
12056     /**
12057      * Get the currently selected nodes.
12058      * @return {Array} An array of HTMLElements
12059      */
12060     getSelectedNodes : function(){
12061         return this.selections;
12062     },
12063
12064     /**
12065      * Get the indexes of the selected nodes.
12066      * @return {Array}
12067      */
12068     getSelectedIndexes : function(){
12069         var indexes = [], s = this.selections;
12070         for(var i = 0, len = s.length; i < len; i++){
12071             indexes.push(s[i].nodeIndex);
12072         }
12073         return indexes;
12074     },
12075
12076     /**
12077      * Clear all selections
12078      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
12079      */
12080     clearSelections : function(suppressEvent){
12081         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
12082             this.cmp.elements = this.selections;
12083             this.cmp.removeClass(this.selectedClass);
12084             this.selections = [];
12085             if(!suppressEvent){
12086                 this.fireEvent("selectionchange", this, this.selections);
12087             }
12088         }
12089     },
12090
12091     /**
12092      * Returns true if the passed node is selected
12093      * @param {HTMLElement/Number} node The node or node index
12094      * @return {Boolean}
12095      */
12096     isSelected : function(node){
12097         var s = this.selections;
12098         if(s.length < 1){
12099             return false;
12100         }
12101         node = this.getNode(node);
12102         return s.indexOf(node) !== -1;
12103     },
12104
12105     /**
12106      * Selects nodes.
12107      * @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
12108      * @param {Boolean} keepExisting (optional) true to keep existing selections
12109      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12110      */
12111     select : function(nodeInfo, keepExisting, suppressEvent){
12112         if(nodeInfo instanceof Array){
12113             if(!keepExisting){
12114                 this.clearSelections(true);
12115             }
12116             for(var i = 0, len = nodeInfo.length; i < len; i++){
12117                 this.select(nodeInfo[i], true, true);
12118             }
12119             return;
12120         } 
12121         var node = this.getNode(nodeInfo);
12122         if(!node || this.isSelected(node)){
12123             return; // already selected.
12124         }
12125         if(!keepExisting){
12126             this.clearSelections(true);
12127         }
12128         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
12129             Roo.fly(node).addClass(this.selectedClass);
12130             this.selections.push(node);
12131             if(!suppressEvent){
12132                 this.fireEvent("selectionchange", this, this.selections);
12133             }
12134         }
12135         
12136         
12137     },
12138       /**
12139      * Unselects nodes.
12140      * @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
12141      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
12142      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12143      */
12144     unselect : function(nodeInfo, keepExisting, suppressEvent)
12145     {
12146         if(nodeInfo instanceof Array){
12147             Roo.each(this.selections, function(s) {
12148                 this.unselect(s, nodeInfo);
12149             }, this);
12150             return;
12151         }
12152         var node = this.getNode(nodeInfo);
12153         if(!node || !this.isSelected(node)){
12154             Roo.log("not selected");
12155             return; // not selected.
12156         }
12157         // fireevent???
12158         var ns = [];
12159         Roo.each(this.selections, function(s) {
12160             if (s == node ) {
12161                 Roo.fly(node).removeClass(this.selectedClass);
12162
12163                 return;
12164             }
12165             ns.push(s);
12166         },this);
12167         
12168         this.selections= ns;
12169         this.fireEvent("selectionchange", this, this.selections);
12170     },
12171
12172     /**
12173      * Gets a template node.
12174      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12175      * @return {HTMLElement} The node or null if it wasn't found
12176      */
12177     getNode : function(nodeInfo){
12178         if(typeof nodeInfo == "string"){
12179             return document.getElementById(nodeInfo);
12180         }else if(typeof nodeInfo == "number"){
12181             return this.nodes[nodeInfo];
12182         }
12183         return nodeInfo;
12184     },
12185
12186     /**
12187      * Gets a range template nodes.
12188      * @param {Number} startIndex
12189      * @param {Number} endIndex
12190      * @return {Array} An array of nodes
12191      */
12192     getNodes : function(start, end){
12193         var ns = this.nodes;
12194         start = start || 0;
12195         end = typeof end == "undefined" ? ns.length - 1 : end;
12196         var nodes = [];
12197         if(start <= end){
12198             for(var i = start; i <= end; i++){
12199                 nodes.push(ns[i]);
12200             }
12201         } else{
12202             for(var i = start; i >= end; i--){
12203                 nodes.push(ns[i]);
12204             }
12205         }
12206         return nodes;
12207     },
12208
12209     /**
12210      * Finds the index of the passed node
12211      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12212      * @return {Number} The index of the node or -1
12213      */
12214     indexOf : function(node){
12215         node = this.getNode(node);
12216         if(typeof node.nodeIndex == "number"){
12217             return node.nodeIndex;
12218         }
12219         var ns = this.nodes;
12220         for(var i = 0, len = ns.length; i < len; i++){
12221             if(ns[i] == node){
12222                 return i;
12223             }
12224         }
12225         return -1;
12226     }
12227 });
12228 /*
12229  * - LGPL
12230  *
12231  * based on jquery fullcalendar
12232  * 
12233  */
12234
12235 Roo.bootstrap = Roo.bootstrap || {};
12236 /**
12237  * @class Roo.bootstrap.Calendar
12238  * @extends Roo.bootstrap.Component
12239  * Bootstrap Calendar class
12240  * @cfg {Boolean} loadMask (true|false) default false
12241  * @cfg {Object} header generate the user specific header of the calendar, default false
12242
12243  * @constructor
12244  * Create a new Container
12245  * @param {Object} config The config object
12246  */
12247
12248
12249
12250 Roo.bootstrap.Calendar = function(config){
12251     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
12252      this.addEvents({
12253         /**
12254              * @event select
12255              * Fires when a date is selected
12256              * @param {DatePicker} this
12257              * @param {Date} date The selected date
12258              */
12259         'select': true,
12260         /**
12261              * @event monthchange
12262              * Fires when the displayed month changes 
12263              * @param {DatePicker} this
12264              * @param {Date} date The selected month
12265              */
12266         'monthchange': true,
12267         /**
12268              * @event evententer
12269              * Fires when mouse over an event
12270              * @param {Calendar} this
12271              * @param {event} Event
12272              */
12273         'evententer': true,
12274         /**
12275              * @event eventleave
12276              * Fires when the mouse leaves an
12277              * @param {Calendar} this
12278              * @param {event}
12279              */
12280         'eventleave': true,
12281         /**
12282              * @event eventclick
12283              * Fires when the mouse click an
12284              * @param {Calendar} this
12285              * @param {event}
12286              */
12287         'eventclick': true
12288         
12289     });
12290
12291 };
12292
12293 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
12294     
12295      /**
12296      * @cfg {Number} startDay
12297      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
12298      */
12299     startDay : 0,
12300     
12301     loadMask : false,
12302     
12303     header : false,
12304       
12305     getAutoCreate : function(){
12306         
12307         
12308         var fc_button = function(name, corner, style, content ) {
12309             return Roo.apply({},{
12310                 tag : 'span',
12311                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
12312                          (corner.length ?
12313                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
12314                             ''
12315                         ),
12316                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
12317                 unselectable: 'on'
12318             });
12319         };
12320         
12321         var header = {};
12322         
12323         if(!this.header){
12324             header = {
12325                 tag : 'table',
12326                 cls : 'fc-header',
12327                 style : 'width:100%',
12328                 cn : [
12329                     {
12330                         tag: 'tr',
12331                         cn : [
12332                             {
12333                                 tag : 'td',
12334                                 cls : 'fc-header-left',
12335                                 cn : [
12336                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
12337                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
12338                                     { tag: 'span', cls: 'fc-header-space' },
12339                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
12340
12341
12342                                 ]
12343                             },
12344
12345                             {
12346                                 tag : 'td',
12347                                 cls : 'fc-header-center',
12348                                 cn : [
12349                                     {
12350                                         tag: 'span',
12351                                         cls: 'fc-header-title',
12352                                         cn : {
12353                                             tag: 'H2',
12354                                             html : 'month / year'
12355                                         }
12356                                     }
12357
12358                                 ]
12359                             },
12360                             {
12361                                 tag : 'td',
12362                                 cls : 'fc-header-right',
12363                                 cn : [
12364                               /*      fc_button('month', 'left', '', 'month' ),
12365                                     fc_button('week', '', '', 'week' ),
12366                                     fc_button('day', 'right', '', 'day' )
12367                                 */    
12368
12369                                 ]
12370                             }
12371
12372                         ]
12373                     }
12374                 ]
12375             };
12376         }
12377         
12378         header = this.header;
12379         
12380        
12381         var cal_heads = function() {
12382             var ret = [];
12383             // fixme - handle this.
12384             
12385             for (var i =0; i < Date.dayNames.length; i++) {
12386                 var d = Date.dayNames[i];
12387                 ret.push({
12388                     tag: 'th',
12389                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
12390                     html : d.substring(0,3)
12391                 });
12392                 
12393             }
12394             ret[0].cls += ' fc-first';
12395             ret[6].cls += ' fc-last';
12396             return ret;
12397         };
12398         var cal_cell = function(n) {
12399             return  {
12400                 tag: 'td',
12401                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
12402                 cn : [
12403                     {
12404                         cn : [
12405                             {
12406                                 cls: 'fc-day-number',
12407                                 html: 'D'
12408                             },
12409                             {
12410                                 cls: 'fc-day-content',
12411                              
12412                                 cn : [
12413                                      {
12414                                         style: 'position: relative;' // height: 17px;
12415                                     }
12416                                 ]
12417                             }
12418                             
12419                             
12420                         ]
12421                     }
12422                 ]
12423                 
12424             }
12425         };
12426         var cal_rows = function() {
12427             
12428             var ret = []
12429             for (var r = 0; r < 6; r++) {
12430                 var row= {
12431                     tag : 'tr',
12432                     cls : 'fc-week',
12433                     cn : []
12434                 };
12435                 
12436                 for (var i =0; i < Date.dayNames.length; i++) {
12437                     var d = Date.dayNames[i];
12438                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
12439
12440                 }
12441                 row.cn[0].cls+=' fc-first';
12442                 row.cn[0].cn[0].style = 'min-height:90px';
12443                 row.cn[6].cls+=' fc-last';
12444                 ret.push(row);
12445                 
12446             }
12447             ret[0].cls += ' fc-first';
12448             ret[4].cls += ' fc-prev-last';
12449             ret[5].cls += ' fc-last';
12450             return ret;
12451             
12452         };
12453         
12454         var cal_table = {
12455             tag: 'table',
12456             cls: 'fc-border-separate',
12457             style : 'width:100%',
12458             cellspacing  : 0,
12459             cn : [
12460                 { 
12461                     tag: 'thead',
12462                     cn : [
12463                         { 
12464                             tag: 'tr',
12465                             cls : 'fc-first fc-last',
12466                             cn : cal_heads()
12467                         }
12468                     ]
12469                 },
12470                 { 
12471                     tag: 'tbody',
12472                     cn : cal_rows()
12473                 }
12474                   
12475             ]
12476         };
12477          
12478          var cfg = {
12479             cls : 'fc fc-ltr',
12480             cn : [
12481                 header,
12482                 {
12483                     cls : 'fc-content',
12484                     style : "position: relative;",
12485                     cn : [
12486                         {
12487                             cls : 'fc-view fc-view-month fc-grid',
12488                             style : 'position: relative',
12489                             unselectable : 'on',
12490                             cn : [
12491                                 {
12492                                     cls : 'fc-event-container',
12493                                     style : 'position:absolute;z-index:8;top:0;left:0;'
12494                                 },
12495                                 cal_table
12496                             ]
12497                         }
12498                     ]
12499     
12500                 }
12501            ] 
12502             
12503         };
12504         
12505          
12506         
12507         return cfg;
12508     },
12509     
12510     
12511     initEvents : function()
12512     {
12513         if(!this.store){
12514             throw "can not find store for calendar";
12515         }
12516         
12517         var mark = {
12518             tag: "div",
12519             cls:"x-dlg-mask",
12520             style: "text-align:center",
12521             cn: [
12522                 {
12523                     tag: "div",
12524                     style: "background-color:white;width:50%;margin:250 auto",
12525                     cn: [
12526                         {
12527                             tag: "img",
12528                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
12529                         },
12530                         {
12531                             tag: "span",
12532                             html: "Loading"
12533                         }
12534                         
12535                     ]
12536                 }
12537             ]
12538         }
12539         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
12540         
12541         var size = this.el.select('.fc-content', true).first().getSize();
12542         this.maskEl.setSize(size.width, size.height);
12543         this.maskEl.enableDisplayMode("block");
12544         if(!this.loadMask){
12545             this.maskEl.hide();
12546         }
12547         
12548         this.store = Roo.factory(this.store, Roo.data);
12549         this.store.on('load', this.onLoad, this);
12550         this.store.on('beforeload', this.onBeforeLoad, this);
12551         
12552         this.resize();
12553         
12554         this.cells = this.el.select('.fc-day',true);
12555         //Roo.log(this.cells);
12556         this.textNodes = this.el.query('.fc-day-number');
12557         this.cells.addClassOnOver('fc-state-hover');
12558         
12559         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
12560         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
12561         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
12562         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
12563         
12564         this.on('monthchange', this.onMonthChange, this);
12565         
12566         this.update(new Date().clearTime());
12567     },
12568     
12569     resize : function() {
12570         var sz  = this.el.getSize();
12571         
12572         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
12573         this.el.select('.fc-day-content div',true).setHeight(34);
12574     },
12575     
12576     
12577     // private
12578     showPrevMonth : function(e){
12579         this.update(this.activeDate.add("mo", -1));
12580     },
12581     showToday : function(e){
12582         this.update(new Date().clearTime());
12583     },
12584     // private
12585     showNextMonth : function(e){
12586         this.update(this.activeDate.add("mo", 1));
12587     },
12588
12589     // private
12590     showPrevYear : function(){
12591         this.update(this.activeDate.add("y", -1));
12592     },
12593
12594     // private
12595     showNextYear : function(){
12596         this.update(this.activeDate.add("y", 1));
12597     },
12598
12599     
12600    // private
12601     update : function(date)
12602     {
12603         var vd = this.activeDate;
12604         this.activeDate = date;
12605 //        if(vd && this.el){
12606 //            var t = date.getTime();
12607 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
12608 //                Roo.log('using add remove');
12609 //                
12610 //                this.fireEvent('monthchange', this, date);
12611 //                
12612 //                this.cells.removeClass("fc-state-highlight");
12613 //                this.cells.each(function(c){
12614 //                   if(c.dateValue == t){
12615 //                       c.addClass("fc-state-highlight");
12616 //                       setTimeout(function(){
12617 //                            try{c.dom.firstChild.focus();}catch(e){}
12618 //                       }, 50);
12619 //                       return false;
12620 //                   }
12621 //                   return true;
12622 //                });
12623 //                return;
12624 //            }
12625 //        }
12626         
12627         var days = date.getDaysInMonth();
12628         
12629         var firstOfMonth = date.getFirstDateOfMonth();
12630         var startingPos = firstOfMonth.getDay()-this.startDay;
12631         
12632         if(startingPos < this.startDay){
12633             startingPos += 7;
12634         }
12635         
12636         var pm = date.add(Date.MONTH, -1);
12637         var prevStart = pm.getDaysInMonth()-startingPos;
12638 //        
12639         this.cells = this.el.select('.fc-day',true);
12640         this.textNodes = this.el.query('.fc-day-number');
12641         this.cells.addClassOnOver('fc-state-hover');
12642         
12643         var cells = this.cells.elements;
12644         var textEls = this.textNodes;
12645         
12646         Roo.each(cells, function(cell){
12647             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
12648         });
12649         
12650         days += startingPos;
12651
12652         // convert everything to numbers so it's fast
12653         var day = 86400000;
12654         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
12655         //Roo.log(d);
12656         //Roo.log(pm);
12657         //Roo.log(prevStart);
12658         
12659         var today = new Date().clearTime().getTime();
12660         var sel = date.clearTime().getTime();
12661         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
12662         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
12663         var ddMatch = this.disabledDatesRE;
12664         var ddText = this.disabledDatesText;
12665         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
12666         var ddaysText = this.disabledDaysText;
12667         var format = this.format;
12668         
12669         var setCellClass = function(cal, cell){
12670             cell.row = 0;
12671             cell.events = [];
12672             cell.more = [];
12673             //Roo.log('set Cell Class');
12674             cell.title = "";
12675             var t = d.getTime();
12676             
12677             //Roo.log(d);
12678             
12679             cell.dateValue = t;
12680             if(t == today){
12681                 cell.className += " fc-today";
12682                 cell.className += " fc-state-highlight";
12683                 cell.title = cal.todayText;
12684             }
12685             if(t == sel){
12686                 // disable highlight in other month..
12687                 //cell.className += " fc-state-highlight";
12688                 
12689             }
12690             // disabling
12691             if(t < min) {
12692                 cell.className = " fc-state-disabled";
12693                 cell.title = cal.minText;
12694                 return;
12695             }
12696             if(t > max) {
12697                 cell.className = " fc-state-disabled";
12698                 cell.title = cal.maxText;
12699                 return;
12700             }
12701             if(ddays){
12702                 if(ddays.indexOf(d.getDay()) != -1){
12703                     cell.title = ddaysText;
12704                     cell.className = " fc-state-disabled";
12705                 }
12706             }
12707             if(ddMatch && format){
12708                 var fvalue = d.dateFormat(format);
12709                 if(ddMatch.test(fvalue)){
12710                     cell.title = ddText.replace("%0", fvalue);
12711                     cell.className = " fc-state-disabled";
12712                 }
12713             }
12714             
12715             if (!cell.initialClassName) {
12716                 cell.initialClassName = cell.dom.className;
12717             }
12718             
12719             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
12720         };
12721
12722         var i = 0;
12723         
12724         for(; i < startingPos; i++) {
12725             textEls[i].innerHTML = (++prevStart);
12726             d.setDate(d.getDate()+1);
12727             
12728             cells[i].className = "fc-past fc-other-month";
12729             setCellClass(this, cells[i]);
12730         }
12731         
12732         var intDay = 0;
12733         
12734         for(; i < days; i++){
12735             intDay = i - startingPos + 1;
12736             textEls[i].innerHTML = (intDay);
12737             d.setDate(d.getDate()+1);
12738             
12739             cells[i].className = ''; // "x-date-active";
12740             setCellClass(this, cells[i]);
12741         }
12742         var extraDays = 0;
12743         
12744         for(; i < 42; i++) {
12745             textEls[i].innerHTML = (++extraDays);
12746             d.setDate(d.getDate()+1);
12747             
12748             cells[i].className = "fc-future fc-other-month";
12749             setCellClass(this, cells[i]);
12750         }
12751         
12752         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
12753         
12754         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
12755         
12756         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
12757         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
12758         
12759         if(totalRows != 6){
12760             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
12761             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
12762         }
12763         
12764         this.fireEvent('monthchange', this, date);
12765         
12766         
12767         /*
12768         if(!this.internalRender){
12769             var main = this.el.dom.firstChild;
12770             var w = main.offsetWidth;
12771             this.el.setWidth(w + this.el.getBorderWidth("lr"));
12772             Roo.fly(main).setWidth(w);
12773             this.internalRender = true;
12774             // opera does not respect the auto grow header center column
12775             // then, after it gets a width opera refuses to recalculate
12776             // without a second pass
12777             if(Roo.isOpera && !this.secondPass){
12778                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
12779                 this.secondPass = true;
12780                 this.update.defer(10, this, [date]);
12781             }
12782         }
12783         */
12784         
12785     },
12786     
12787     findCell : function(dt) {
12788         dt = dt.clearTime().getTime();
12789         var ret = false;
12790         this.cells.each(function(c){
12791             //Roo.log("check " +c.dateValue + '?=' + dt);
12792             if(c.dateValue == dt){
12793                 ret = c;
12794                 return false;
12795             }
12796             return true;
12797         });
12798         
12799         return ret;
12800     },
12801     
12802     findCells : function(ev) {
12803         var s = ev.start.clone().clearTime().getTime();
12804        // Roo.log(s);
12805         var e= ev.end.clone().clearTime().getTime();
12806        // Roo.log(e);
12807         var ret = [];
12808         this.cells.each(function(c){
12809              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
12810             
12811             if(c.dateValue > e){
12812                 return ;
12813             }
12814             if(c.dateValue < s){
12815                 return ;
12816             }
12817             ret.push(c);
12818         });
12819         
12820         return ret;    
12821     },
12822     
12823 //    findBestRow: function(cells)
12824 //    {
12825 //        var ret = 0;
12826 //        
12827 //        for (var i =0 ; i < cells.length;i++) {
12828 //            ret  = Math.max(cells[i].rows || 0,ret);
12829 //        }
12830 //        return ret;
12831 //        
12832 //    },
12833     
12834     
12835     addItem : function(ev)
12836     {
12837         // look for vertical location slot in
12838         var cells = this.findCells(ev);
12839         
12840 //        ev.row = this.findBestRow(cells);
12841         
12842         // work out the location.
12843         
12844         var crow = false;
12845         var rows = [];
12846         for(var i =0; i < cells.length; i++) {
12847             
12848             cells[i].row = cells[0].row;
12849             
12850             if(i == 0){
12851                 cells[i].row = cells[i].row + 1;
12852             }
12853             
12854             if (!crow) {
12855                 crow = {
12856                     start : cells[i],
12857                     end :  cells[i]
12858                 };
12859                 continue;
12860             }
12861             if (crow.start.getY() == cells[i].getY()) {
12862                 // on same row.
12863                 crow.end = cells[i];
12864                 continue;
12865             }
12866             // different row.
12867             rows.push(crow);
12868             crow = {
12869                 start: cells[i],
12870                 end : cells[i]
12871             };
12872             
12873         }
12874         
12875         rows.push(crow);
12876         ev.els = [];
12877         ev.rows = rows;
12878         ev.cells = cells;
12879         
12880         cells[0].events.push(ev);
12881         
12882         this.calevents.push(ev);
12883     },
12884     
12885     clearEvents: function() {
12886         
12887         if(!this.calevents){
12888             return;
12889         }
12890         
12891         Roo.each(this.cells.elements, function(c){
12892             c.row = 0;
12893             c.events = [];
12894             c.more = [];
12895         });
12896         
12897         Roo.each(this.calevents, function(e) {
12898             Roo.each(e.els, function(el) {
12899                 el.un('mouseenter' ,this.onEventEnter, this);
12900                 el.un('mouseleave' ,this.onEventLeave, this);
12901                 el.remove();
12902             },this);
12903         },this);
12904         
12905         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
12906             e.remove();
12907         });
12908         
12909     },
12910     
12911     renderEvents: function()
12912     {   
12913         var _this = this;
12914         
12915         this.cells.each(function(c) {
12916             
12917             if(c.row < 5){
12918                 return;
12919             }
12920             
12921             var ev = c.events;
12922             
12923             var r = 4;
12924             if(c.row != c.events.length){
12925                 r = 4 - (4 - (c.row - c.events.length));
12926             }
12927             
12928             c.events = ev.slice(0, r);
12929             c.more = ev.slice(r);
12930             
12931             if(c.more.length && c.more.length == 1){
12932                 c.events.push(c.more.pop());
12933             }
12934             
12935             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
12936             
12937         });
12938             
12939         this.cells.each(function(c) {
12940             
12941             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
12942             
12943             
12944             for (var e = 0; e < c.events.length; e++){
12945                 var ev = c.events[e];
12946                 var rows = ev.rows;
12947                 
12948                 for(var i = 0; i < rows.length; i++) {
12949                 
12950                     // how many rows should it span..
12951
12952                     var  cfg = {
12953                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
12954                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
12955
12956                         unselectable : "on",
12957                         cn : [
12958                             {
12959                                 cls: 'fc-event-inner',
12960                                 cn : [
12961     //                                {
12962     //                                  tag:'span',
12963     //                                  cls: 'fc-event-time',
12964     //                                  html : cells.length > 1 ? '' : ev.time
12965     //                                },
12966                                     {
12967                                       tag:'span',
12968                                       cls: 'fc-event-title',
12969                                       html : String.format('{0}', ev.title)
12970                                     }
12971
12972
12973                                 ]
12974                             },
12975                             {
12976                                 cls: 'ui-resizable-handle ui-resizable-e',
12977                                 html : '&nbsp;&nbsp;&nbsp'
12978                             }
12979
12980                         ]
12981                     };
12982
12983                     if (i == 0) {
12984                         cfg.cls += ' fc-event-start';
12985                     }
12986                     if ((i+1) == rows.length) {
12987                         cfg.cls += ' fc-event-end';
12988                     }
12989
12990                     var ctr = _this.el.select('.fc-event-container',true).first();
12991                     var cg = ctr.createChild(cfg);
12992
12993                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
12994                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
12995
12996                     var r = (c.more.length) ? 1 : 0;
12997                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
12998                     cg.setWidth(ebox.right - sbox.x -2);
12999
13000                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
13001                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
13002                     cg.on('click', _this.onEventClick, _this, ev);
13003
13004                     ev.els.push(cg);
13005                     
13006                 }
13007                 
13008             }
13009             
13010             
13011             if(c.more.length){
13012                 var  cfg = {
13013                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
13014                     style : 'position: absolute',
13015                     unselectable : "on",
13016                     cn : [
13017                         {
13018                             cls: 'fc-event-inner',
13019                             cn : [
13020                                 {
13021                                   tag:'span',
13022                                   cls: 'fc-event-title',
13023                                   html : 'More'
13024                                 }
13025
13026
13027                             ]
13028                         },
13029                         {
13030                             cls: 'ui-resizable-handle ui-resizable-e',
13031                             html : '&nbsp;&nbsp;&nbsp'
13032                         }
13033
13034                     ]
13035                 };
13036
13037                 var ctr = _this.el.select('.fc-event-container',true).first();
13038                 var cg = ctr.createChild(cfg);
13039
13040                 var sbox = c.select('.fc-day-content',true).first().getBox();
13041                 var ebox = c.select('.fc-day-content',true).first().getBox();
13042                 //Roo.log(cg);
13043                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
13044                 cg.setWidth(ebox.right - sbox.x -2);
13045
13046                 cg.on('click', _this.onMoreEventClick, _this, c.more);
13047                 
13048             }
13049             
13050         });
13051         
13052         
13053         
13054     },
13055     
13056     onEventEnter: function (e, el,event,d) {
13057         this.fireEvent('evententer', this, el, event);
13058     },
13059     
13060     onEventLeave: function (e, el,event,d) {
13061         this.fireEvent('eventleave', this, el, event);
13062     },
13063     
13064     onEventClick: function (e, el,event,d) {
13065         this.fireEvent('eventclick', this, el, event);
13066     },
13067     
13068     onMonthChange: function () {
13069         this.store.load();
13070     },
13071     
13072     onMoreEventClick: function(e, el, more)
13073     {
13074         var _this = this;
13075         
13076         this.calpopover.placement = 'right';
13077         this.calpopover.setTitle('More');
13078         
13079         this.calpopover.setContent('');
13080         
13081         var ctr = this.calpopover.el.select('.popover-content', true).first();
13082         
13083         Roo.each(more, function(m){
13084             var cfg = {
13085                 cls : 'fc-event-hori fc-event-draggable',
13086                 html : m.title
13087             }
13088             var cg = ctr.createChild(cfg);
13089             
13090             cg.on('click', _this.onEventClick, _this, m);
13091         });
13092         
13093         this.calpopover.show(el);
13094         
13095         
13096     },
13097     
13098     onLoad: function () 
13099     {   
13100         this.calevents = [];
13101         var cal = this;
13102         
13103         if(this.store.getCount() > 0){
13104             this.store.data.each(function(d){
13105                cal.addItem({
13106                     id : d.data.id,
13107                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
13108                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
13109                     time : d.data.start_time,
13110                     title : d.data.title,
13111                     description : d.data.description,
13112                     venue : d.data.venue
13113                 });
13114             });
13115         }
13116         
13117         this.renderEvents();
13118         
13119         if(this.calevents.length && this.loadMask){
13120             this.maskEl.hide();
13121         }
13122     },
13123     
13124     onBeforeLoad: function()
13125     {
13126         this.clearEvents();
13127         if(this.loadMask){
13128             this.maskEl.show();
13129         }
13130     }
13131 });
13132
13133  
13134  /*
13135  * - LGPL
13136  *
13137  * element
13138  * 
13139  */
13140
13141 /**
13142  * @class Roo.bootstrap.Popover
13143  * @extends Roo.bootstrap.Component
13144  * Bootstrap Popover class
13145  * @cfg {String} html contents of the popover   (or false to use children..)
13146  * @cfg {String} title of popover (or false to hide)
13147  * @cfg {String} placement how it is placed
13148  * @cfg {String} trigger click || hover (or false to trigger manually)
13149  * @cfg {String} over what (parent or false to trigger manually.)
13150  * 
13151  * @constructor
13152  * Create a new Popover
13153  * @param {Object} config The config object
13154  */
13155
13156 Roo.bootstrap.Popover = function(config){
13157     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
13158 };
13159
13160 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
13161     
13162     title: 'Fill in a title',
13163     html: false,
13164     
13165     placement : 'right',
13166     trigger : 'hover', // hover
13167     
13168     over: 'parent',
13169     
13170     can_build_overlaid : false,
13171     
13172     getChildContainer : function()
13173     {
13174         return this.el.select('.popover-content',true).first();
13175     },
13176     
13177     getAutoCreate : function(){
13178          Roo.log('make popover?');
13179         var cfg = {
13180            cls : 'popover roo-dynamic',
13181            style: 'display:block',
13182            cn : [
13183                 {
13184                     cls : 'arrow'
13185                 },
13186                 {
13187                     cls : 'popover-inner',
13188                     cn : [
13189                         {
13190                             tag: 'h3',
13191                             cls: 'popover-title',
13192                             html : this.title
13193                         },
13194                         {
13195                             cls : 'popover-content',
13196                             html : this.html
13197                         }
13198                     ]
13199                     
13200                 }
13201            ]
13202         };
13203         
13204         return cfg;
13205     },
13206     setTitle: function(str)
13207     {
13208         this.el.select('.popover-title',true).first().dom.innerHTML = str;
13209     },
13210     setContent: function(str)
13211     {
13212         this.el.select('.popover-content',true).first().dom.innerHTML = str;
13213     },
13214     // as it get's added to the bottom of the page.
13215     onRender : function(ct, position)
13216     {
13217         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
13218         if(!this.el){
13219             var cfg = Roo.apply({},  this.getAutoCreate());
13220             cfg.id = Roo.id();
13221             
13222             if (this.cls) {
13223                 cfg.cls += ' ' + this.cls;
13224             }
13225             if (this.style) {
13226                 cfg.style = this.style;
13227             }
13228             Roo.log("adding to ")
13229             this.el = Roo.get(document.body).createChild(cfg, position);
13230             Roo.log(this.el);
13231         }
13232         this.initEvents();
13233     },
13234     
13235     initEvents : function()
13236     {
13237         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
13238         this.el.enableDisplayMode('block');
13239         this.el.hide();
13240         if (this.over === false) {
13241             return; 
13242         }
13243         if (this.triggers === false) {
13244             return;
13245         }
13246         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13247         var triggers = this.trigger ? this.trigger.split(' ') : [];
13248         Roo.each(triggers, function(trigger) {
13249         
13250             if (trigger == 'click') {
13251                 on_el.on('click', this.toggle, this);
13252             } else if (trigger != 'manual') {
13253                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
13254                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
13255       
13256                 on_el.on(eventIn  ,this.enter, this);
13257                 on_el.on(eventOut, this.leave, this);
13258             }
13259         }, this);
13260         
13261     },
13262     
13263     
13264     // private
13265     timeout : null,
13266     hoverState : null,
13267     
13268     toggle : function () {
13269         this.hoverState == 'in' ? this.leave() : this.enter();
13270     },
13271     
13272     enter : function () {
13273        
13274     
13275         clearTimeout(this.timeout);
13276     
13277         this.hoverState = 'in'
13278     
13279         if (!this.delay || !this.delay.show) {
13280             this.show();
13281             return 
13282         }
13283         var _t = this;
13284         this.timeout = setTimeout(function () {
13285             if (_t.hoverState == 'in') {
13286                 _t.show();
13287             }
13288         }, this.delay.show)
13289     },
13290     leave : function() {
13291         clearTimeout(this.timeout);
13292     
13293         this.hoverState = 'out'
13294     
13295         if (!this.delay || !this.delay.hide) {
13296             this.hide();
13297             return 
13298         }
13299         var _t = this;
13300         this.timeout = setTimeout(function () {
13301             if (_t.hoverState == 'out') {
13302                 _t.hide();
13303             }
13304         }, this.delay.hide)
13305     },
13306     
13307     show : function (on_el)
13308     {
13309         if (!on_el) {
13310             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13311         }
13312         // set content.
13313         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
13314         if (this.html !== false) {
13315             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
13316         }
13317         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
13318         if (!this.title.length) {
13319             this.el.select('.popover-title',true).hide();
13320         }
13321         
13322         var placement = typeof this.placement == 'function' ?
13323             this.placement.call(this, this.el, on_el) :
13324             this.placement;
13325             
13326         var autoToken = /\s?auto?\s?/i;
13327         var autoPlace = autoToken.test(placement);
13328         if (autoPlace) {
13329             placement = placement.replace(autoToken, '') || 'top';
13330         }
13331         
13332         //this.el.detach()
13333         //this.el.setXY([0,0]);
13334         this.el.show();
13335         this.el.dom.style.display='block';
13336         this.el.addClass(placement);
13337         
13338         //this.el.appendTo(on_el);
13339         
13340         var p = this.getPosition();
13341         var box = this.el.getBox();
13342         
13343         if (autoPlace) {
13344             // fixme..
13345         }
13346         var align = Roo.bootstrap.Popover.alignment[placement]
13347         this.el.alignTo(on_el, align[0],align[1]);
13348         //var arrow = this.el.select('.arrow',true).first();
13349         //arrow.set(align[2], 
13350         
13351         this.el.addClass('in');
13352         this.hoverState = null;
13353         
13354         if (this.el.hasClass('fade')) {
13355             // fade it?
13356         }
13357         
13358     },
13359     hide : function()
13360     {
13361         this.el.setXY([0,0]);
13362         this.el.removeClass('in');
13363         this.el.hide();
13364         
13365     }
13366     
13367 });
13368
13369 Roo.bootstrap.Popover.alignment = {
13370     'left' : ['r-l', [-10,0], 'right'],
13371     'right' : ['l-r', [10,0], 'left'],
13372     'bottom' : ['t-b', [0,10], 'top'],
13373     'top' : [ 'b-t', [0,-10], 'bottom']
13374 };
13375
13376  /*
13377  * - LGPL
13378  *
13379  * Progress
13380  * 
13381  */
13382
13383 /**
13384  * @class Roo.bootstrap.Progress
13385  * @extends Roo.bootstrap.Component
13386  * Bootstrap Progress class
13387  * @cfg {Boolean} striped striped of the progress bar
13388  * @cfg {Boolean} active animated of the progress bar
13389  * 
13390  * 
13391  * @constructor
13392  * Create a new Progress
13393  * @param {Object} config The config object
13394  */
13395
13396 Roo.bootstrap.Progress = function(config){
13397     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
13398 };
13399
13400 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
13401     
13402     striped : false,
13403     active: false,
13404     
13405     getAutoCreate : function(){
13406         var cfg = {
13407             tag: 'div',
13408             cls: 'progress'
13409         };
13410         
13411         
13412         if(this.striped){
13413             cfg.cls += ' progress-striped';
13414         }
13415       
13416         if(this.active){
13417             cfg.cls += ' active';
13418         }
13419         
13420         
13421         return cfg;
13422     }
13423    
13424 });
13425
13426  
13427
13428  /*
13429  * - LGPL
13430  *
13431  * ProgressBar
13432  * 
13433  */
13434
13435 /**
13436  * @class Roo.bootstrap.ProgressBar
13437  * @extends Roo.bootstrap.Component
13438  * Bootstrap ProgressBar class
13439  * @cfg {Number} aria_valuenow aria-value now
13440  * @cfg {Number} aria_valuemin aria-value min
13441  * @cfg {Number} aria_valuemax aria-value max
13442  * @cfg {String} label label for the progress bar
13443  * @cfg {String} panel (success | info | warning | danger )
13444  * @cfg {String} role role of the progress bar
13445  * @cfg {String} sr_only text
13446  * 
13447  * 
13448  * @constructor
13449  * Create a new ProgressBar
13450  * @param {Object} config The config object
13451  */
13452
13453 Roo.bootstrap.ProgressBar = function(config){
13454     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
13455 };
13456
13457 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
13458     
13459     aria_valuenow : 0,
13460     aria_valuemin : 0,
13461     aria_valuemax : 100,
13462     label : false,
13463     panel : false,
13464     role : false,
13465     sr_only: false,
13466     
13467     getAutoCreate : function()
13468     {
13469         
13470         var cfg = {
13471             tag: 'div',
13472             cls: 'progress-bar',
13473             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
13474         };
13475         
13476         if(this.sr_only){
13477             cfg.cn = {
13478                 tag: 'span',
13479                 cls: 'sr-only',
13480                 html: this.sr_only
13481             }
13482         }
13483         
13484         if(this.role){
13485             cfg.role = this.role;
13486         }
13487         
13488         if(this.aria_valuenow){
13489             cfg['aria-valuenow'] = this.aria_valuenow;
13490         }
13491         
13492         if(this.aria_valuemin){
13493             cfg['aria-valuemin'] = this.aria_valuemin;
13494         }
13495         
13496         if(this.aria_valuemax){
13497             cfg['aria-valuemax'] = this.aria_valuemax;
13498         }
13499         
13500         if(this.label && !this.sr_only){
13501             cfg.html = this.label;
13502         }
13503         
13504         if(this.panel){
13505             cfg.cls += ' progress-bar-' + this.panel;
13506         }
13507         
13508         return cfg;
13509     },
13510     
13511     update : function(aria_valuenow)
13512     {
13513         this.aria_valuenow = aria_valuenow;
13514         
13515         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
13516     }
13517    
13518 });
13519
13520  
13521
13522  /*
13523  * - LGPL
13524  *
13525  * TabPanel
13526  * 
13527  */
13528
13529 /**
13530  * @class Roo.bootstrap.TabPanel
13531  * @extends Roo.bootstrap.Component
13532  * Bootstrap TabPanel class
13533  * @cfg {Boolean} active panel active
13534  * @cfg {String} html panel content
13535  * @cfg {String} tabId tab relate id
13536  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
13537  * 
13538  * 
13539  * @constructor
13540  * Create a new TabPanel
13541  * @param {Object} config The config object
13542  */
13543
13544 Roo.bootstrap.TabPanel = function(config){
13545     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
13546      this.addEvents({
13547         /**
13548              * @event changed
13549              * Fires when the active status changes
13550              * @param {Roo.bootstrap.TabPanel} this
13551              * @param {Boolean} state the new state
13552             
13553          */
13554         'changed': true
13555      });
13556 };
13557
13558 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
13559     
13560     active: false,
13561     html: false,
13562     tabId: false,
13563     navId : false,
13564     
13565     getAutoCreate : function(){
13566         var cfg = {
13567             tag: 'div',
13568             cls: 'tab-pane',
13569             html: this.html || ''
13570         };
13571         
13572         if(this.active){
13573             cfg.cls += ' active';
13574         }
13575         
13576         if(this.tabId){
13577             cfg.tabId = this.tabId;
13578         }
13579         
13580         return cfg;
13581     },
13582     onRender : function(ct, position)
13583     {
13584        // Roo.log("Call onRender: " + this.xtype);
13585         
13586         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
13587         
13588         if (this.navId && this.tabId) {
13589             var item = Roo.bootstrap.NavGroup.get(this.navId).getNavItem(this.tabId);
13590             if (!item) {
13591                 Roo.log("could not find navID:"  + this.navId + ", tabId: " + this.tabId);
13592             } else {
13593                 item.on('changed', function(item, state) {
13594                     this.setActive(state);
13595                 }, this);
13596             }
13597         }
13598         
13599     },
13600     setActive: function(state)
13601     {
13602         Roo.log("panel - set active " + this.tabId + "=" + state);
13603         
13604         this.active = state;
13605         if (!state) {
13606             this.el.removeClass('active');
13607             
13608         } else  if (!this.el.hasClass('active')) {
13609             this.el.addClass('active');
13610         }
13611         this.fireEvent('changed', this, state);
13612     }
13613     
13614     
13615 });
13616  
13617
13618  
13619
13620  /*
13621  * - LGPL
13622  *
13623  * DateField
13624  * 
13625  */
13626
13627 /**
13628  * @class Roo.bootstrap.DateField
13629  * @extends Roo.bootstrap.Input
13630  * Bootstrap DateField class
13631  * @cfg {Number} weekStart default 0
13632  * @cfg {Number} weekStart default 0
13633  * @cfg {Number} viewMode default empty, (months|years)
13634  * @cfg {Number} minViewMode default empty, (months|years)
13635  * @cfg {Number} startDate default -Infinity
13636  * @cfg {Number} endDate default Infinity
13637  * @cfg {Boolean} todayHighlight default false
13638  * @cfg {Boolean} todayBtn default false
13639  * @cfg {Boolean} calendarWeeks default false
13640  * @cfg {Object} daysOfWeekDisabled default empty
13641  * 
13642  * @cfg {Boolean} keyboardNavigation default true
13643  * @cfg {String} language default en
13644  * 
13645  * @constructor
13646  * Create a new DateField
13647  * @param {Object} config The config object
13648  */
13649
13650 Roo.bootstrap.DateField = function(config){
13651     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
13652      this.addEvents({
13653             /**
13654              * @event show
13655              * Fires when this field show.
13656              * @param {Roo.bootstrap.DateField} this
13657              * @param {Mixed} date The date value
13658              */
13659             show : true,
13660             /**
13661              * @event show
13662              * Fires when this field hide.
13663              * @param {Roo.bootstrap.DateField} this
13664              * @param {Mixed} date The date value
13665              */
13666             hide : true,
13667             /**
13668              * @event select
13669              * Fires when select a date.
13670              * @param {Roo.bootstrap.DateField} this
13671              * @param {Mixed} date The date value
13672              */
13673             select : true
13674         });
13675 };
13676
13677 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
13678     
13679     /**
13680      * @cfg {String} format
13681      * The default date format string which can be overriden for localization support.  The format must be
13682      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
13683      */
13684     format : "m/d/y",
13685     /**
13686      * @cfg {String} altFormats
13687      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
13688      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
13689      */
13690     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
13691     
13692     weekStart : 0,
13693     
13694     viewMode : '',
13695     
13696     minViewMode : '',
13697     
13698     todayHighlight : false,
13699     
13700     todayBtn: false,
13701     
13702     language: 'en',
13703     
13704     keyboardNavigation: true,
13705     
13706     calendarWeeks: false,
13707     
13708     startDate: -Infinity,
13709     
13710     endDate: Infinity,
13711     
13712     daysOfWeekDisabled: [],
13713     
13714     _events: [],
13715     
13716     UTCDate: function()
13717     {
13718         return new Date(Date.UTC.apply(Date, arguments));
13719     },
13720     
13721     UTCToday: function()
13722     {
13723         var today = new Date();
13724         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
13725     },
13726     
13727     getDate: function() {
13728             var d = this.getUTCDate();
13729             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
13730     },
13731     
13732     getUTCDate: function() {
13733             return this.date;
13734     },
13735     
13736     setDate: function(d) {
13737             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
13738     },
13739     
13740     setUTCDate: function(d) {
13741             this.date = d;
13742             this.setValue(this.formatDate(this.date));
13743     },
13744         
13745     onRender: function(ct, position)
13746     {
13747         
13748         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
13749         
13750         this.language = this.language || 'en';
13751         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
13752         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
13753         
13754         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
13755         this.format = this.format || 'm/d/y';
13756         this.isInline = false;
13757         this.isInput = true;
13758         this.component = this.el.select('.add-on', true).first() || false;
13759         this.component = (this.component && this.component.length === 0) ? false : this.component;
13760         this.hasInput = this.component && this.inputEL().length;
13761         
13762         if (typeof(this.minViewMode === 'string')) {
13763             switch (this.minViewMode) {
13764                 case 'months':
13765                     this.minViewMode = 1;
13766                     break;
13767                 case 'years':
13768                     this.minViewMode = 2;
13769                     break;
13770                 default:
13771                     this.minViewMode = 0;
13772                     break;
13773             }
13774         }
13775         
13776         if (typeof(this.viewMode === 'string')) {
13777             switch (this.viewMode) {
13778                 case 'months':
13779                     this.viewMode = 1;
13780                     break;
13781                 case 'years':
13782                     this.viewMode = 2;
13783                     break;
13784                 default:
13785                     this.viewMode = 0;
13786                     break;
13787             }
13788         }
13789                 
13790         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
13791         
13792         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13793         
13794         this.picker().on('mousedown', this.onMousedown, this);
13795         this.picker().on('click', this.onClick, this);
13796         
13797         this.picker().addClass('datepicker-dropdown');
13798         
13799         this.startViewMode = this.viewMode;
13800         
13801         
13802         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
13803             if(!this.calendarWeeks){
13804                 v.remove();
13805                 return;
13806             };
13807             
13808             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
13809             v.attr('colspan', function(i, val){
13810                 return parseInt(val) + 1;
13811             });
13812         })
13813                         
13814         
13815         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
13816         
13817         this.setStartDate(this.startDate);
13818         this.setEndDate(this.endDate);
13819         
13820         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
13821         
13822         this.fillDow();
13823         this.fillMonths();
13824         this.update();
13825         this.showMode();
13826         
13827         if(this.isInline) {
13828             this.show();
13829         }
13830     },
13831     
13832     picker : function()
13833     {
13834         return this.el.select('.datepicker', true).first();
13835     },
13836     
13837     fillDow: function()
13838     {
13839         var dowCnt = this.weekStart;
13840         
13841         var dow = {
13842             tag: 'tr',
13843             cn: [
13844                 
13845             ]
13846         };
13847         
13848         if(this.calendarWeeks){
13849             dow.cn.push({
13850                 tag: 'th',
13851                 cls: 'cw',
13852                 html: '&nbsp;'
13853             })
13854         }
13855         
13856         while (dowCnt < this.weekStart + 7) {
13857             dow.cn.push({
13858                 tag: 'th',
13859                 cls: 'dow',
13860                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
13861             });
13862         }
13863         
13864         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
13865     },
13866     
13867     fillMonths: function()
13868     {    
13869         var i = 0
13870         var months = this.picker().select('>.datepicker-months td', true).first();
13871         
13872         months.dom.innerHTML = '';
13873         
13874         while (i < 12) {
13875             var month = {
13876                 tag: 'span',
13877                 cls: 'month',
13878                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
13879             }
13880             
13881             months.createChild(month);
13882         }
13883         
13884     },
13885     
13886     update: function()
13887     {
13888         
13889         this.date = (typeof(this.date) === 'undefined') ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
13890         
13891         if (this.date < this.startDate) {
13892             this.viewDate = new Date(this.startDate);
13893         } else if (this.date > this.endDate) {
13894             this.viewDate = new Date(this.endDate);
13895         } else {
13896             this.viewDate = new Date(this.date);
13897         }
13898         
13899         this.fill();
13900     },
13901     
13902     fill: function() 
13903     {
13904         var d = new Date(this.viewDate),
13905                 year = d.getUTCFullYear(),
13906                 month = d.getUTCMonth(),
13907                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
13908                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
13909                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
13910                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
13911                 currentDate = this.date && this.date.valueOf(),
13912                 today = this.UTCToday();
13913         
13914         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
13915         
13916 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
13917         
13918 //        this.picker.select('>tfoot th.today').
13919 //                                              .text(dates[this.language].today)
13920 //                                              .toggle(this.todayBtn !== false);
13921     
13922         this.updateNavArrows();
13923         this.fillMonths();
13924                                                 
13925         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
13926         
13927         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
13928          
13929         prevMonth.setUTCDate(day);
13930         
13931         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
13932         
13933         var nextMonth = new Date(prevMonth);
13934         
13935         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
13936         
13937         nextMonth = nextMonth.valueOf();
13938         
13939         var fillMonths = false;
13940         
13941         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
13942         
13943         while(prevMonth.valueOf() < nextMonth) {
13944             var clsName = '';
13945             
13946             if (prevMonth.getUTCDay() === this.weekStart) {
13947                 if(fillMonths){
13948                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
13949                 }
13950                     
13951                 fillMonths = {
13952                     tag: 'tr',
13953                     cn: []
13954                 };
13955                 
13956                 if(this.calendarWeeks){
13957                     // ISO 8601: First week contains first thursday.
13958                     // ISO also states week starts on Monday, but we can be more abstract here.
13959                     var
13960                     // Start of current week: based on weekstart/current date
13961                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
13962                     // Thursday of this week
13963                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
13964                     // First Thursday of year, year from thursday
13965                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
13966                     // Calendar week: ms between thursdays, div ms per day, div 7 days
13967                     calWeek =  (th - yth) / 864e5 / 7 + 1;
13968                     
13969                     fillMonths.cn.push({
13970                         tag: 'td',
13971                         cls: 'cw',
13972                         html: calWeek
13973                     });
13974                 }
13975             }
13976             
13977             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
13978                 clsName += ' old';
13979             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
13980                 clsName += ' new';
13981             }
13982             if (this.todayHighlight &&
13983                 prevMonth.getUTCFullYear() == today.getFullYear() &&
13984                 prevMonth.getUTCMonth() == today.getMonth() &&
13985                 prevMonth.getUTCDate() == today.getDate()) {
13986                 clsName += ' today';
13987             }
13988             
13989             if (currentDate && prevMonth.valueOf() === currentDate) {
13990                 clsName += ' active';
13991             }
13992             
13993             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
13994                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
13995                     clsName += ' disabled';
13996             }
13997             
13998             fillMonths.cn.push({
13999                 tag: 'td',
14000                 cls: 'day ' + clsName,
14001                 html: prevMonth.getDate()
14002             })
14003             
14004             prevMonth.setDate(prevMonth.getDate()+1);
14005         }
14006           
14007         var currentYear = this.date && this.date.getUTCFullYear();
14008         var currentMonth = this.date && this.date.getUTCMonth();
14009         
14010         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
14011         
14012         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
14013             v.removeClass('active');
14014             
14015             if(currentYear === year && k === currentMonth){
14016                 v.addClass('active');
14017             }
14018             
14019             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
14020                 v.addClass('disabled');
14021             }
14022             
14023         });
14024         
14025         
14026         year = parseInt(year/10, 10) * 10;
14027         
14028         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
14029         
14030         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
14031         
14032         year -= 1;
14033         for (var i = -1; i < 11; i++) {
14034             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
14035                 tag: 'span',
14036                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
14037                 html: year
14038             })
14039             
14040             year += 1;
14041         }
14042     },
14043     
14044     showMode: function(dir) 
14045     {
14046         if (dir) {
14047             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
14048         }
14049         Roo.each(this.picker().select('>div',true).elements, function(v){
14050             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14051             v.hide();
14052         });
14053         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
14054     },
14055     
14056     place: function()
14057     {
14058         if(this.isInline) return;
14059         
14060         this.picker().removeClass(['bottom', 'top']);
14061         
14062         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
14063             /*
14064              * place to the top of element!
14065              *
14066              */
14067             
14068             this.picker().addClass('top');
14069             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
14070             
14071             return;
14072         }
14073         
14074         this.picker().addClass('bottom');
14075         
14076         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
14077     },
14078     
14079     parseDate : function(value)
14080     {
14081         if(!value || value instanceof Date){
14082             return value;
14083         }
14084         var v = Date.parseDate(value, this.format);
14085         if (!v && this.useIso) {
14086             v = Date.parseDate(value, 'Y-m-d');
14087         }
14088         if(!v && this.altFormats){
14089             if(!this.altFormatsArray){
14090                 this.altFormatsArray = this.altFormats.split("|");
14091             }
14092             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
14093                 v = Date.parseDate(value, this.altFormatsArray[i]);
14094             }
14095         }
14096         return v;
14097     },
14098     
14099     formatDate : function(date, fmt)
14100     {
14101         return (!date || !(date instanceof Date)) ?
14102         date : date.dateFormat(fmt || this.format);
14103     },
14104     
14105     onFocus : function()
14106     {
14107         Roo.bootstrap.DateField.superclass.onFocus.call(this);
14108         this.show();
14109     },
14110     
14111     onBlur : function()
14112     {
14113         Roo.bootstrap.DateField.superclass.onBlur.call(this);
14114         
14115         var d = this.inputEl().getValue();
14116         
14117         if(d && d.length){
14118             this.setValue(d);
14119         }
14120                 
14121         this.hide();
14122     },
14123     
14124     show : function()
14125     {
14126         this.picker().show();
14127         this.update();
14128         this.place();
14129         
14130         this.fireEvent('show', this, this.date);
14131     },
14132     
14133     hide : function()
14134     {
14135         if(this.isInline) return;
14136         this.picker().hide();
14137         this.viewMode = this.startViewMode;
14138         this.showMode();
14139         
14140         this.fireEvent('hide', this, this.date);
14141         
14142     },
14143     
14144     onMousedown: function(e)
14145     {
14146         e.stopPropagation();
14147         e.preventDefault();
14148     },
14149     
14150     keyup: function(e)
14151     {
14152         Roo.bootstrap.DateField.superclass.keyup.call(this);
14153         this.update();
14154     },
14155
14156     setValue: function(v)
14157     {
14158         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
14159         
14160         var d = new Date(v);
14161         
14162         if(isNaN(d.getTime())){
14163             return;
14164         }
14165         
14166         this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
14167
14168         this.update();
14169
14170         this.fireEvent('select', this, this.date);
14171         
14172     },
14173     
14174     getValue: function()
14175     {
14176         return this.formatDate(this.date);
14177     },
14178     
14179     fireKey: function(e)
14180     {
14181         if (!this.picker().isVisible()){
14182             if (e.keyCode == 27) // allow escape to hide and re-show picker
14183                 this.show();
14184             return;
14185         }
14186         var dateChanged = false,
14187         dir, day, month,
14188         newDate, newViewDate;
14189         
14190         switch(e.keyCode){
14191             case 27: // escape
14192                 this.hide();
14193                 e.preventDefault();
14194                 break;
14195             case 37: // left
14196             case 39: // right
14197                 if (!this.keyboardNavigation) break;
14198                 dir = e.keyCode == 37 ? -1 : 1;
14199                 
14200                 if (e.ctrlKey){
14201                     newDate = this.moveYear(this.date, dir);
14202                     newViewDate = this.moveYear(this.viewDate, dir);
14203                 } else if (e.shiftKey){
14204                     newDate = this.moveMonth(this.date, dir);
14205                     newViewDate = this.moveMonth(this.viewDate, dir);
14206                 } else {
14207                     newDate = new Date(this.date);
14208                     newDate.setUTCDate(this.date.getUTCDate() + dir);
14209                     newViewDate = new Date(this.viewDate);
14210                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
14211                 }
14212                 if (this.dateWithinRange(newDate)){
14213                     this.date = newDate;
14214                     this.viewDate = newViewDate;
14215                     this.setValue(this.formatDate(this.date));
14216 //                    this.update();
14217                     e.preventDefault();
14218                     dateChanged = true;
14219                 }
14220                 break;
14221             case 38: // up
14222             case 40: // down
14223                 if (!this.keyboardNavigation) break;
14224                 dir = e.keyCode == 38 ? -1 : 1;
14225                 if (e.ctrlKey){
14226                     newDate = this.moveYear(this.date, dir);
14227                     newViewDate = this.moveYear(this.viewDate, dir);
14228                 } else if (e.shiftKey){
14229                     newDate = this.moveMonth(this.date, dir);
14230                     newViewDate = this.moveMonth(this.viewDate, dir);
14231                 } else {
14232                     newDate = new Date(this.date);
14233                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
14234                     newViewDate = new Date(this.viewDate);
14235                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
14236                 }
14237                 if (this.dateWithinRange(newDate)){
14238                     this.date = newDate;
14239                     this.viewDate = newViewDate;
14240                     this.setValue(this.formatDate(this.date));
14241 //                    this.update();
14242                     e.preventDefault();
14243                     dateChanged = true;
14244                 }
14245                 break;
14246             case 13: // enter
14247                 this.setValue(this.formatDate(this.date));
14248                 this.hide();
14249                 e.preventDefault();
14250                 break;
14251             case 9: // tab
14252                 this.setValue(this.formatDate(this.date));
14253                 this.hide();
14254                 break;
14255                 
14256         }
14257     },
14258     
14259     
14260     onClick: function(e) 
14261     {
14262         e.stopPropagation();
14263         e.preventDefault();
14264         
14265         var target = e.getTarget();
14266         
14267         if(target.nodeName.toLowerCase() === 'i'){
14268             target = Roo.get(target).dom.parentNode;
14269         }
14270         
14271         var nodeName = target.nodeName;
14272         var className = target.className;
14273         var html = target.innerHTML;
14274         
14275         switch(nodeName.toLowerCase()) {
14276             case 'th':
14277                 switch(className) {
14278                     case 'switch':
14279                         this.showMode(1);
14280                         break;
14281                     case 'prev':
14282                     case 'next':
14283                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
14284                         switch(this.viewMode){
14285                                 case 0:
14286                                         this.viewDate = this.moveMonth(this.viewDate, dir);
14287                                         break;
14288                                 case 1:
14289                                 case 2:
14290                                         this.viewDate = this.moveYear(this.viewDate, dir);
14291                                         break;
14292                         }
14293                         this.fill();
14294                         break;
14295                     case 'today':
14296                         var date = new Date();
14297                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
14298 //                        this.fill()
14299                         this.setValue(this.formatDate(this.date));
14300                         
14301                         this.hide();
14302                         break;
14303                 }
14304                 break;
14305             case 'span':
14306                 if (className.indexOf('disabled') === -1) {
14307                     this.viewDate.setUTCDate(1);
14308                     if (className.indexOf('month') !== -1) {
14309                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
14310                     } else {
14311                         var year = parseInt(html, 10) || 0;
14312                         this.viewDate.setUTCFullYear(year);
14313                         
14314                     }
14315                     this.showMode(-1);
14316                     this.fill();
14317                 }
14318                 break;
14319                 
14320             case 'td':
14321                 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
14322                     var day = parseInt(html, 10) || 1;
14323                     var year = this.viewDate.getUTCFullYear(),
14324                         month = this.viewDate.getUTCMonth();
14325
14326                     if (className.indexOf('old') !== -1) {
14327                         if(month === 0 ){
14328                             month = 11;
14329                             year -= 1;
14330                         }else{
14331                             month -= 1;
14332                         }
14333                     } else if (className.indexOf('new') !== -1) {
14334                         if (month == 11) {
14335                             month = 0;
14336                             year += 1;
14337                         } else {
14338                             month += 1;
14339                         }
14340                     }
14341                     this.date = this.UTCDate(year, month, day,0,0,0,0);
14342                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
14343 //                    this.fill();
14344                     this.setValue(this.formatDate(this.date));
14345                     this.hide();
14346                 }
14347                 break;
14348         }
14349     },
14350     
14351     setStartDate: function(startDate)
14352     {
14353         this.startDate = startDate || -Infinity;
14354         if (this.startDate !== -Infinity) {
14355             this.startDate = this.parseDate(this.startDate);
14356         }
14357         this.update();
14358         this.updateNavArrows();
14359     },
14360
14361     setEndDate: function(endDate)
14362     {
14363         this.endDate = endDate || Infinity;
14364         if (this.endDate !== Infinity) {
14365             this.endDate = this.parseDate(this.endDate);
14366         }
14367         this.update();
14368         this.updateNavArrows();
14369     },
14370     
14371     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
14372     {
14373         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
14374         if (typeof(this.daysOfWeekDisabled) !== 'object') {
14375             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
14376         }
14377         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
14378             return parseInt(d, 10);
14379         });
14380         this.update();
14381         this.updateNavArrows();
14382     },
14383     
14384     updateNavArrows: function() 
14385     {
14386         var d = new Date(this.viewDate),
14387         year = d.getUTCFullYear(),
14388         month = d.getUTCMonth();
14389         
14390         Roo.each(this.picker().select('.prev', true).elements, function(v){
14391             v.show();
14392             switch (this.viewMode) {
14393                 case 0:
14394
14395                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
14396                         v.hide();
14397                     }
14398                     break;
14399                 case 1:
14400                 case 2:
14401                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
14402                         v.hide();
14403                     }
14404                     break;
14405             }
14406         });
14407         
14408         Roo.each(this.picker().select('.next', true).elements, function(v){
14409             v.show();
14410             switch (this.viewMode) {
14411                 case 0:
14412
14413                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
14414                         v.hide();
14415                     }
14416                     break;
14417                 case 1:
14418                 case 2:
14419                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
14420                         v.hide();
14421                     }
14422                     break;
14423             }
14424         })
14425     },
14426     
14427     moveMonth: function(date, dir)
14428     {
14429         if (!dir) return date;
14430         var new_date = new Date(date.valueOf()),
14431         day = new_date.getUTCDate(),
14432         month = new_date.getUTCMonth(),
14433         mag = Math.abs(dir),
14434         new_month, test;
14435         dir = dir > 0 ? 1 : -1;
14436         if (mag == 1){
14437             test = dir == -1
14438             // If going back one month, make sure month is not current month
14439             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
14440             ? function(){
14441                 return new_date.getUTCMonth() == month;
14442             }
14443             // If going forward one month, make sure month is as expected
14444             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
14445             : function(){
14446                 return new_date.getUTCMonth() != new_month;
14447             };
14448             new_month = month + dir;
14449             new_date.setUTCMonth(new_month);
14450             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
14451             if (new_month < 0 || new_month > 11)
14452                 new_month = (new_month + 12) % 12;
14453         } else {
14454             // For magnitudes >1, move one month at a time...
14455             for (var i=0; i<mag; i++)
14456                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
14457                 new_date = this.moveMonth(new_date, dir);
14458             // ...then reset the day, keeping it in the new month
14459             new_month = new_date.getUTCMonth();
14460             new_date.setUTCDate(day);
14461             test = function(){
14462                 return new_month != new_date.getUTCMonth();
14463             };
14464         }
14465         // Common date-resetting loop -- if date is beyond end of month, make it
14466         // end of month
14467         while (test()){
14468             new_date.setUTCDate(--day);
14469             new_date.setUTCMonth(new_month);
14470         }
14471         return new_date;
14472     },
14473
14474     moveYear: function(date, dir)
14475     {
14476         return this.moveMonth(date, dir*12);
14477     },
14478
14479     dateWithinRange: function(date)
14480     {
14481         return date >= this.startDate && date <= this.endDate;
14482     },
14483
14484     
14485     remove: function() 
14486     {
14487         this.picker().remove();
14488     }
14489    
14490 });
14491
14492 Roo.apply(Roo.bootstrap.DateField,  {
14493     
14494     head : {
14495         tag: 'thead',
14496         cn: [
14497         {
14498             tag: 'tr',
14499             cn: [
14500             {
14501                 tag: 'th',
14502                 cls: 'prev',
14503                 html: '<i class="fa fa-arrow-left"/>'
14504             },
14505             {
14506                 tag: 'th',
14507                 cls: 'switch',
14508                 colspan: '5'
14509             },
14510             {
14511                 tag: 'th',
14512                 cls: 'next',
14513                 html: '<i class="fa fa-arrow-right"/>'
14514             }
14515
14516             ]
14517         }
14518         ]
14519     },
14520     
14521     content : {
14522         tag: 'tbody',
14523         cn: [
14524         {
14525             tag: 'tr',
14526             cn: [
14527             {
14528                 tag: 'td',
14529                 colspan: '7'
14530             }
14531             ]
14532         }
14533         ]
14534     },
14535     
14536     footer : {
14537         tag: 'tfoot',
14538         cn: [
14539         {
14540             tag: 'tr',
14541             cn: [
14542             {
14543                 tag: 'th',
14544                 colspan: '7',
14545                 cls: 'today'
14546             }
14547                     
14548             ]
14549         }
14550         ]
14551     },
14552     
14553     dates:{
14554         en: {
14555             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
14556             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
14557             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
14558             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
14559             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
14560             today: "Today"
14561         }
14562     },
14563     
14564     modes: [
14565     {
14566         clsName: 'days',
14567         navFnc: 'Month',
14568         navStep: 1
14569     },
14570     {
14571         clsName: 'months',
14572         navFnc: 'FullYear',
14573         navStep: 1
14574     },
14575     {
14576         clsName: 'years',
14577         navFnc: 'FullYear',
14578         navStep: 10
14579     }]
14580 });
14581
14582 Roo.apply(Roo.bootstrap.DateField,  {
14583   
14584     template : {
14585         tag: 'div',
14586         cls: 'datepicker dropdown-menu',
14587         cn: [
14588         {
14589             tag: 'div',
14590             cls: 'datepicker-days',
14591             cn: [
14592             {
14593                 tag: 'table',
14594                 cls: 'table-condensed',
14595                 cn:[
14596                 Roo.bootstrap.DateField.head,
14597                 {
14598                     tag: 'tbody'
14599                 },
14600                 Roo.bootstrap.DateField.footer
14601                 ]
14602             }
14603             ]
14604         },
14605         {
14606             tag: 'div',
14607             cls: 'datepicker-months',
14608             cn: [
14609             {
14610                 tag: 'table',
14611                 cls: 'table-condensed',
14612                 cn:[
14613                 Roo.bootstrap.DateField.head,
14614                 Roo.bootstrap.DateField.content,
14615                 Roo.bootstrap.DateField.footer
14616                 ]
14617             }
14618             ]
14619         },
14620         {
14621             tag: 'div',
14622             cls: 'datepicker-years',
14623             cn: [
14624             {
14625                 tag: 'table',
14626                 cls: 'table-condensed',
14627                 cn:[
14628                 Roo.bootstrap.DateField.head,
14629                 Roo.bootstrap.DateField.content,
14630                 Roo.bootstrap.DateField.footer
14631                 ]
14632             }
14633             ]
14634         }
14635         ]
14636     }
14637 });
14638
14639  
14640
14641  /*
14642  * - LGPL
14643  *
14644  * TimeField
14645  * 
14646  */
14647
14648 /**
14649  * @class Roo.bootstrap.TimeField
14650  * @extends Roo.bootstrap.Input
14651  * Bootstrap DateField class
14652  * 
14653  * 
14654  * @constructor
14655  * Create a new TimeField
14656  * @param {Object} config The config object
14657  */
14658
14659 Roo.bootstrap.TimeField = function(config){
14660     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
14661     this.addEvents({
14662             /**
14663              * @event show
14664              * Fires when this field show.
14665              * @param {Roo.bootstrap.DateField} this
14666              * @param {Mixed} date The date value
14667              */
14668             show : true,
14669             /**
14670              * @event show
14671              * Fires when this field hide.
14672              * @param {Roo.bootstrap.DateField} this
14673              * @param {Mixed} date The date value
14674              */
14675             hide : true,
14676             /**
14677              * @event select
14678              * Fires when select a date.
14679              * @param {Roo.bootstrap.DateField} this
14680              * @param {Mixed} date The date value
14681              */
14682             select : true
14683         });
14684 };
14685
14686 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
14687     
14688     /**
14689      * @cfg {String} format
14690      * The default time format string which can be overriden for localization support.  The format must be
14691      * valid according to {@link Date#parseDate} (defaults to 'H:i').
14692      */
14693     format : "H:i",
14694        
14695     onRender: function(ct, position)
14696     {
14697         
14698         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
14699                 
14700         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
14701         
14702         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14703         
14704         this.pop = this.picker().select('>.datepicker-time',true).first();
14705         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block' 
14706         
14707         this.picker().on('mousedown', this.onMousedown, this);
14708         this.picker().on('click', this.onClick, this);
14709         
14710         this.picker().addClass('datepicker-dropdown');
14711     
14712         this.fillTime();
14713         this.update();
14714             
14715         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
14716         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
14717         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
14718         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
14719         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
14720         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
14721
14722     },
14723     
14724     fireKey: function(e){
14725         if (!this.picker().isVisible()){
14726             if (e.keyCode == 27) // allow escape to hide and re-show picker
14727                 this.show();
14728             return;
14729         }
14730
14731         e.preventDefault();
14732         
14733         switch(e.keyCode){
14734             case 27: // escape
14735                 this.hide();
14736                 break;
14737             case 37: // left
14738             case 39: // right
14739                 this.onTogglePeriod();
14740                 break;
14741             case 38: // up
14742                 this.onIncrementMinutes();
14743                 break;
14744             case 40: // down
14745                 this.onDecrementMinutes();
14746                 break;
14747             case 13: // enter
14748             case 9: // tab
14749                 this.setTime();
14750                 break;
14751         }
14752     },
14753     
14754     onClick: function(e) {
14755         e.stopPropagation();
14756         e.preventDefault();
14757     },
14758     
14759     picker : function()
14760     {
14761         return this.el.select('.datepicker', true).first();
14762     },
14763     
14764     fillTime: function()
14765     {    
14766         var time = this.pop.select('tbody', true).first();
14767         
14768         time.dom.innerHTML = '';
14769         
14770         time.createChild({
14771             tag: 'tr',
14772             cn: [
14773                 {
14774                     tag: 'td',
14775                     cn: [
14776                         {
14777                             tag: 'a',
14778                             href: '#',
14779                             cls: 'btn',
14780                             cn: [
14781                                 {
14782                                     tag: 'span',
14783                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
14784                                 }
14785                             ]
14786                         } 
14787                     ]
14788                 },
14789                 {
14790                     tag: 'td',
14791                     cls: 'separator'
14792                 },
14793                 {
14794                     tag: 'td',
14795                     cn: [
14796                         {
14797                             tag: 'a',
14798                             href: '#',
14799                             cls: 'btn',
14800                             cn: [
14801                                 {
14802                                     tag: 'span',
14803                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
14804                                 }
14805                             ]
14806                         }
14807                     ]
14808                 },
14809                 {
14810                     tag: 'td',
14811                     cls: 'separator'
14812                 }
14813             ]
14814         });
14815         
14816         time.createChild({
14817             tag: 'tr',
14818             cn: [
14819                 {
14820                     tag: 'td',
14821                     cn: [
14822                         {
14823                             tag: 'span',
14824                             cls: 'timepicker-hour',
14825                             html: '00'
14826                         }  
14827                     ]
14828                 },
14829                 {
14830                     tag: 'td',
14831                     cls: 'separator',
14832                     html: ':'
14833                 },
14834                 {
14835                     tag: 'td',
14836                     cn: [
14837                         {
14838                             tag: 'span',
14839                             cls: 'timepicker-minute',
14840                             html: '00'
14841                         }  
14842                     ]
14843                 },
14844                 {
14845                     tag: 'td',
14846                     cls: 'separator'
14847                 },
14848                 {
14849                     tag: 'td',
14850                     cn: [
14851                         {
14852                             tag: 'button',
14853                             type: 'button',
14854                             cls: 'btn btn-primary period',
14855                             html: 'AM'
14856                             
14857                         }
14858                     ]
14859                 }
14860             ]
14861         });
14862         
14863         time.createChild({
14864             tag: 'tr',
14865             cn: [
14866                 {
14867                     tag: 'td',
14868                     cn: [
14869                         {
14870                             tag: 'a',
14871                             href: '#',
14872                             cls: 'btn',
14873                             cn: [
14874                                 {
14875                                     tag: 'span',
14876                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
14877                                 }
14878                             ]
14879                         }
14880                     ]
14881                 },
14882                 {
14883                     tag: 'td',
14884                     cls: 'separator'
14885                 },
14886                 {
14887                     tag: 'td',
14888                     cn: [
14889                         {
14890                             tag: 'a',
14891                             href: '#',
14892                             cls: 'btn',
14893                             cn: [
14894                                 {
14895                                     tag: 'span',
14896                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
14897                                 }
14898                             ]
14899                         }
14900                     ]
14901                 },
14902                 {
14903                     tag: 'td',
14904                     cls: 'separator'
14905                 }
14906             ]
14907         });
14908         
14909     },
14910     
14911     update: function()
14912     {
14913         
14914         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
14915         
14916         this.fill();
14917     },
14918     
14919     fill: function() 
14920     {
14921         var hours = this.time.getHours();
14922         var minutes = this.time.getMinutes();
14923         var period = 'AM';
14924         
14925         if(hours > 11){
14926             period = 'PM';
14927         }
14928         
14929         if(hours == 0){
14930             hours = 12;
14931         }
14932         
14933         
14934         if(hours > 12){
14935             hours = hours - 12;
14936         }
14937         
14938         if(hours < 10){
14939             hours = '0' + hours;
14940         }
14941         
14942         if(minutes < 10){
14943             minutes = '0' + minutes;
14944         }
14945         
14946         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
14947         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
14948         this.pop.select('button', true).first().dom.innerHTML = period;
14949         
14950     },
14951     
14952     place: function()
14953     {   
14954         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
14955         
14956         var cls = ['bottom'];
14957         
14958         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
14959             cls.pop();
14960             cls.push('top');
14961         }
14962         
14963         cls.push('right');
14964         
14965         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
14966             cls.pop();
14967             cls.push('left');
14968         }
14969         
14970         this.picker().addClass(cls.join('-'));
14971         
14972         var _this = this;
14973         
14974         Roo.each(cls, function(c){
14975             if(c == 'bottom'){
14976                 _this.picker().setTop(_this.inputEl().getHeight());
14977                 return;
14978             }
14979             if(c == 'top'){
14980                 _this.picker().setTop(0 - _this.picker().getHeight());
14981                 return;
14982             }
14983             
14984             if(c == 'left'){
14985                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
14986                 return;
14987             }
14988             if(c == 'right'){
14989                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
14990                 return;
14991             }
14992         });
14993         
14994     },
14995   
14996     onFocus : function()
14997     {
14998         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
14999         this.show();
15000     },
15001     
15002     onBlur : function()
15003     {
15004         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
15005         this.hide();
15006     },
15007     
15008     show : function()
15009     {
15010         this.picker().show();
15011         this.pop.show();
15012         this.update();
15013         this.place();
15014         
15015         this.fireEvent('show', this, this.date);
15016     },
15017     
15018     hide : function()
15019     {
15020         this.picker().hide();
15021         this.pop.hide();
15022         
15023         this.fireEvent('hide', this, this.date);
15024     },
15025     
15026     setTime : function()
15027     {
15028         this.hide();
15029         this.setValue(this.time.format(this.format));
15030         
15031         this.fireEvent('select', this, this.date);
15032         
15033         
15034     },
15035     
15036     onMousedown: function(e){
15037         e.stopPropagation();
15038         e.preventDefault();
15039     },
15040     
15041     onIncrementHours: function()
15042     {
15043         Roo.log('onIncrementHours');
15044         this.time = this.time.add(Date.HOUR, 1);
15045         this.update();
15046         
15047     },
15048     
15049     onDecrementHours: function()
15050     {
15051         Roo.log('onDecrementHours');
15052         this.time = this.time.add(Date.HOUR, -1);
15053         this.update();
15054     },
15055     
15056     onIncrementMinutes: function()
15057     {
15058         Roo.log('onIncrementMinutes');
15059         this.time = this.time.add(Date.MINUTE, 1);
15060         this.update();
15061     },
15062     
15063     onDecrementMinutes: function()
15064     {
15065         Roo.log('onDecrementMinutes');
15066         this.time = this.time.add(Date.MINUTE, -1);
15067         this.update();
15068     },
15069     
15070     onTogglePeriod: function()
15071     {
15072         Roo.log('onTogglePeriod');
15073         this.time = this.time.add(Date.HOUR, 12);
15074         this.update();
15075     }
15076     
15077    
15078 });
15079
15080 Roo.apply(Roo.bootstrap.TimeField,  {
15081     
15082     content : {
15083         tag: 'tbody',
15084         cn: [
15085             {
15086                 tag: 'tr',
15087                 cn: [
15088                 {
15089                     tag: 'td',
15090                     colspan: '7'
15091                 }
15092                 ]
15093             }
15094         ]
15095     },
15096     
15097     footer : {
15098         tag: 'tfoot',
15099         cn: [
15100             {
15101                 tag: 'tr',
15102                 cn: [
15103                 {
15104                     tag: 'th',
15105                     colspan: '7',
15106                     cls: '',
15107                     cn: [
15108                         {
15109                             tag: 'button',
15110                             cls: 'btn btn-info ok',
15111                             html: 'OK'
15112                         }
15113                     ]
15114                 }
15115
15116                 ]
15117             }
15118         ]
15119     }
15120 });
15121
15122 Roo.apply(Roo.bootstrap.TimeField,  {
15123   
15124     template : {
15125         tag: 'div',
15126         cls: 'datepicker dropdown-menu',
15127         cn: [
15128             {
15129                 tag: 'div',
15130                 cls: 'datepicker-time',
15131                 cn: [
15132                 {
15133                     tag: 'table',
15134                     cls: 'table-condensed',
15135                     cn:[
15136                     Roo.bootstrap.TimeField.content,
15137                     Roo.bootstrap.TimeField.footer
15138                     ]
15139                 }
15140                 ]
15141             }
15142         ]
15143     }
15144 });
15145
15146  
15147
15148  /*
15149  * - LGPL
15150  *
15151  * CheckBox
15152  * 
15153  */
15154
15155 /**
15156  * @class Roo.bootstrap.CheckBox
15157  * @extends Roo.bootstrap.Input
15158  * Bootstrap CheckBox class
15159  * 
15160  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
15161  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
15162  * @cfg {String} boxLabel The text that appears beside the checkbox
15163  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
15164  * @cfg {Boolean} checked initnal the element
15165  * 
15166  * 
15167  * @constructor
15168  * Create a new CheckBox
15169  * @param {Object} config The config object
15170  */
15171
15172 Roo.bootstrap.CheckBox = function(config){
15173     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
15174    
15175         this.addEvents({
15176             /**
15177             * @event check
15178             * Fires when the element is checked or unchecked.
15179             * @param {Roo.bootstrap.CheckBox} this This input
15180             * @param {Boolean} checked The new checked value
15181             */
15182            check : true
15183         });
15184 };
15185
15186 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
15187     
15188     inputType: 'checkbox',
15189     inputValue: 1,
15190     valueOff: 0,
15191     boxLabel: false,
15192     checked: false,
15193     weight : false,
15194     
15195     getAutoCreate : function()
15196     {
15197         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
15198         
15199         var id = Roo.id();
15200         
15201         var cfg = {};
15202         
15203         cfg.cls = 'form-group checkbox' //input-group
15204         
15205         
15206         
15207         
15208         var input =  {
15209             tag: 'input',
15210             id : id,
15211             type : this.inputType,
15212             value : (!this.checked) ? this.valueOff : this.inputValue,
15213             cls : 'roo-checkbox', //'form-box',
15214             placeholder : this.placeholder || ''
15215             
15216         };
15217         
15218         if (this.weight) { // Validity check?
15219             cfg.cls += " checkbox-" + this.weight;
15220         }
15221         
15222         if (this.disabled) {
15223             input.disabled=true;
15224         }
15225         
15226         if(this.checked){
15227             input.checked = this.checked;
15228         }
15229         
15230         if (this.name) {
15231             input.name = this.name;
15232         }
15233         
15234         if (this.size) {
15235             input.cls += ' input-' + this.size;
15236         }
15237         
15238         var settings=this;
15239         ['xs','sm','md','lg'].map(function(size){
15240             if (settings[size]) {
15241                 cfg.cls += ' col-' + size + '-' + settings[size];
15242             }
15243         });
15244         
15245        
15246         
15247         var inputblock = input;
15248         
15249         
15250         
15251         
15252         if (this.before || this.after) {
15253             
15254             inputblock = {
15255                 cls : 'input-group',
15256                 cn :  [] 
15257             };
15258             if (this.before) {
15259                 inputblock.cn.push({
15260                     tag :'span',
15261                     cls : 'input-group-addon',
15262                     html : this.before
15263                 });
15264             }
15265             inputblock.cn.push(input);
15266             if (this.after) {
15267                 inputblock.cn.push({
15268                     tag :'span',
15269                     cls : 'input-group-addon',
15270                     html : this.after
15271                 });
15272             }
15273             
15274         };
15275         
15276         if (align ==='left' && this.fieldLabel.length) {
15277                 Roo.log("left and has label");
15278                 cfg.cn = [
15279                     
15280                     {
15281                         tag: 'label',
15282                         'for' :  id,
15283                         cls : 'control-label col-md-' + this.labelWidth,
15284                         html : this.fieldLabel
15285                         
15286                     },
15287                     {
15288                         cls : "col-md-" + (12 - this.labelWidth), 
15289                         cn: [
15290                             inputblock
15291                         ]
15292                     }
15293                     
15294                 ];
15295         } else if ( this.fieldLabel.length) {
15296                 Roo.log(" label");
15297                 cfg.cn = [
15298                    
15299                     {
15300                         tag: this.boxLabel ? 'span' : 'label',
15301                         'for': id,
15302                         cls: 'control-label box-input-label',
15303                         //cls : 'input-group-addon',
15304                         html : this.fieldLabel
15305                         
15306                     },
15307                     
15308                     inputblock
15309                     
15310                 ];
15311
15312         } else {
15313             
15314                 Roo.log(" no label && no align");
15315                 cfg.cn = [  inputblock ] ;
15316                 
15317                 
15318         };
15319          if(this.boxLabel){
15320             cfg.cn.push( {
15321                 tag: 'label',
15322                 'for': id,
15323                 cls: 'box-label',
15324                 html: this.boxLabel
15325                 
15326             });
15327         }
15328         
15329         
15330        
15331         return cfg;
15332         
15333     },
15334     
15335     /**
15336      * return the real input element.
15337      */
15338     inputEl: function ()
15339     {
15340         return this.el.select('input.roo-checkbox',true).first();
15341     },
15342     
15343     label: function()
15344     {
15345         return this.el.select('label.control-label',true).first();
15346     },
15347     
15348     initEvents : function()
15349     {
15350 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
15351         
15352         this.inputEl().on('click', this.onClick,  this);
15353         
15354     },
15355     
15356     onClick : function()
15357     {   
15358         this.setChecked(!this.checked);
15359     },
15360     
15361     setChecked : function(state,suppressEvent)
15362     {
15363         this.checked = state;
15364         
15365         this.inputEl().dom.checked = state;
15366         
15367         if(suppressEvent !== true){
15368             this.fireEvent('check', this, state);
15369         }
15370         
15371         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
15372         
15373     },
15374     
15375     setValue : function(v,suppressEvent)
15376     {
15377         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
15378     }
15379     
15380 });
15381
15382  
15383 /*
15384  * - LGPL
15385  *
15386  * Radio
15387  * 
15388  */
15389
15390 /**
15391  * @class Roo.bootstrap.Radio
15392  * @extends Roo.bootstrap.CheckBox
15393  * Bootstrap Radio class
15394
15395  * @constructor
15396  * Create a new Radio
15397  * @param {Object} config The config object
15398  */
15399
15400 Roo.bootstrap.Radio = function(config){
15401     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
15402    
15403 };
15404
15405 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
15406     
15407     inputType: 'radio',
15408     inputValue: '',
15409     valueOff: '',
15410     
15411     getAutoCreate : function()
15412     {
15413         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
15414         
15415         var id = Roo.id();
15416         
15417         var cfg = {};
15418         
15419         cfg.cls = 'form-group radio' //input-group
15420         
15421         var input =  {
15422             tag: 'input',
15423             id : id,
15424             type : this.inputType,
15425             value : (!this.checked) ? this.valueOff : this.inputValue,
15426             cls : 'roo-radio',
15427             placeholder : this.placeholder || ''
15428             
15429         };
15430           if (this.weight) { // Validity check?
15431             cfg.cls += " radio-" + this.weight;
15432         }
15433         if (this.disabled) {
15434             input.disabled=true;
15435         }
15436         
15437         if(this.checked){
15438             input.checked = this.checked;
15439         }
15440         
15441         if (this.name) {
15442             input.name = this.name;
15443         }
15444         
15445         if (this.size) {
15446             input.cls += ' input-' + this.size;
15447         }
15448         
15449         var settings=this;
15450         ['xs','sm','md','lg'].map(function(size){
15451             if (settings[size]) {
15452                 cfg.cls += ' col-' + size + '-' + settings[size];
15453             }
15454         });
15455         
15456         var inputblock = input;
15457         
15458         if (this.before || this.after) {
15459             
15460             inputblock = {
15461                 cls : 'input-group',
15462                 cn :  [] 
15463             };
15464             if (this.before) {
15465                 inputblock.cn.push({
15466                     tag :'span',
15467                     cls : 'input-group-addon',
15468                     html : this.before
15469                 });
15470             }
15471             inputblock.cn.push(input);
15472             if (this.after) {
15473                 inputblock.cn.push({
15474                     tag :'span',
15475                     cls : 'input-group-addon',
15476                     html : this.after
15477                 });
15478             }
15479             
15480         };
15481         
15482         if (align ==='left' && this.fieldLabel.length) {
15483                 Roo.log("left and has label");
15484                 cfg.cn = [
15485                     
15486                     {
15487                         tag: 'label',
15488                         'for' :  id,
15489                         cls : 'control-label col-md-' + this.labelWidth,
15490                         html : this.fieldLabel
15491                         
15492                     },
15493                     {
15494                         cls : "col-md-" + (12 - this.labelWidth), 
15495                         cn: [
15496                             inputblock
15497                         ]
15498                     }
15499                     
15500                 ];
15501         } else if ( this.fieldLabel.length) {
15502                 Roo.log(" label");
15503                  cfg.cn = [
15504                    
15505                     {
15506                         tag: 'label',
15507                         'for': id,
15508                         cls: 'control-label box-input-label',
15509                         //cls : 'input-group-addon',
15510                         html : this.fieldLabel
15511                         
15512                     },
15513                     
15514                     inputblock
15515                     
15516                 ];
15517
15518         } else {
15519             
15520                    Roo.log(" no label && no align");
15521                 cfg.cn = [
15522                     
15523                         inputblock
15524                     
15525                 ];
15526                 
15527                 
15528         };
15529         
15530         if(this.boxLabel){
15531             cfg.cn.push({
15532                 tag: 'label',
15533                 'for': id,
15534                 cls: 'box-label',
15535                 html: this.boxLabel
15536             })
15537         }
15538         
15539         return cfg;
15540         
15541     },
15542     inputEl: function ()
15543     {
15544         return this.el.select('input.roo-radio',true).first();
15545     },
15546     onClick : function()
15547     {   
15548         this.setChecked(true);
15549     },
15550     
15551     setChecked : function(state,suppressEvent)
15552     {
15553         if(state){
15554             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
15555                 v.dom.checked = false;
15556             });
15557         }
15558         
15559         this.checked = state;
15560         this.inputEl().dom.checked = state;
15561         
15562         if(suppressEvent !== true){
15563             this.fireEvent('check', this, state);
15564         }
15565         
15566         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
15567         
15568     },
15569     
15570     getGroupValue : function()
15571     {
15572         var value = ''
15573         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
15574             if(v.dom.checked == true){
15575                 value = v.dom.value;
15576             }
15577         });
15578         
15579         return value;
15580     },
15581     
15582     /**
15583      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
15584      * @return {Mixed} value The field value
15585      */
15586     getValue : function(){
15587         return this.getGroupValue();
15588     }
15589     
15590 });
15591
15592  
15593 //<script type="text/javascript">
15594
15595 /*
15596  * Based  Ext JS Library 1.1.1
15597  * Copyright(c) 2006-2007, Ext JS, LLC.
15598  * LGPL
15599  *
15600  */
15601  
15602 /**
15603  * @class Roo.HtmlEditorCore
15604  * @extends Roo.Component
15605  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
15606  *
15607  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
15608  */
15609
15610 Roo.HtmlEditorCore = function(config){
15611     
15612     
15613     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
15614     this.addEvents({
15615         /**
15616          * @event initialize
15617          * Fires when the editor is fully initialized (including the iframe)
15618          * @param {Roo.HtmlEditorCore} this
15619          */
15620         initialize: true,
15621         /**
15622          * @event activate
15623          * Fires when the editor is first receives the focus. Any insertion must wait
15624          * until after this event.
15625          * @param {Roo.HtmlEditorCore} this
15626          */
15627         activate: true,
15628          /**
15629          * @event beforesync
15630          * Fires before the textarea is updated with content from the editor iframe. Return false
15631          * to cancel the sync.
15632          * @param {Roo.HtmlEditorCore} this
15633          * @param {String} html
15634          */
15635         beforesync: true,
15636          /**
15637          * @event beforepush
15638          * Fires before the iframe editor is updated with content from the textarea. Return false
15639          * to cancel the push.
15640          * @param {Roo.HtmlEditorCore} this
15641          * @param {String} html
15642          */
15643         beforepush: true,
15644          /**
15645          * @event sync
15646          * Fires when the textarea is updated with content from the editor iframe.
15647          * @param {Roo.HtmlEditorCore} this
15648          * @param {String} html
15649          */
15650         sync: true,
15651          /**
15652          * @event push
15653          * Fires when the iframe editor is updated with content from the textarea.
15654          * @param {Roo.HtmlEditorCore} this
15655          * @param {String} html
15656          */
15657         push: true,
15658         
15659         /**
15660          * @event editorevent
15661          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
15662          * @param {Roo.HtmlEditorCore} this
15663          */
15664         editorevent: true
15665     });
15666      
15667 };
15668
15669
15670 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
15671
15672
15673      /**
15674      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
15675      */
15676     
15677     owner : false,
15678     
15679      /**
15680      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
15681      *                        Roo.resizable.
15682      */
15683     resizable : false,
15684      /**
15685      * @cfg {Number} height (in pixels)
15686      */   
15687     height: 300,
15688    /**
15689      * @cfg {Number} width (in pixels)
15690      */   
15691     width: 500,
15692     
15693     /**
15694      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
15695      * 
15696      */
15697     stylesheets: false,
15698     
15699     // id of frame..
15700     frameId: false,
15701     
15702     // private properties
15703     validationEvent : false,
15704     deferHeight: true,
15705     initialized : false,
15706     activated : false,
15707     sourceEditMode : false,
15708     onFocus : Roo.emptyFn,
15709     iframePad:3,
15710     hideMode:'offsets',
15711     
15712     clearUp: true,
15713     
15714      
15715     
15716
15717     /**
15718      * Protected method that will not generally be called directly. It
15719      * is called when the editor initializes the iframe with HTML contents. Override this method if you
15720      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
15721      */
15722     getDocMarkup : function(){
15723         // body styles..
15724         var st = '';
15725         Roo.log(this.stylesheets);
15726         
15727         // inherit styels from page...?? 
15728         if (this.stylesheets === false) {
15729             
15730             Roo.get(document.head).select('style').each(function(node) {
15731                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
15732             });
15733             
15734             Roo.get(document.head).select('link').each(function(node) { 
15735                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
15736             });
15737             
15738         } else if (!this.stylesheets.length) {
15739                 // simple..
15740                 st = '<style type="text/css">' +
15741                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
15742                    '</style>';
15743         } else {
15744             Roo.each(this.stylesheets, function(s) {
15745                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
15746             });
15747             
15748         }
15749         
15750         st +=  '<style type="text/css">' +
15751             'IMG { cursor: pointer } ' +
15752         '</style>';
15753
15754         
15755         return '<html><head>' + st  +
15756             //<style type="text/css">' +
15757             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
15758             //'</style>' +
15759             ' </head><body class="roo-htmleditor-body"></body></html>';
15760     },
15761
15762     // private
15763     onRender : function(ct, position)
15764     {
15765         var _t = this;
15766         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
15767         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
15768         
15769         
15770         this.el.dom.style.border = '0 none';
15771         this.el.dom.setAttribute('tabIndex', -1);
15772         this.el.addClass('x-hidden hide');
15773         
15774         
15775         
15776         if(Roo.isIE){ // fix IE 1px bogus margin
15777             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
15778         }
15779        
15780         
15781         this.frameId = Roo.id();
15782         
15783          
15784         
15785         var iframe = this.owner.wrap.createChild({
15786             tag: 'iframe',
15787             cls: 'form-control', // bootstrap..
15788             id: this.frameId,
15789             name: this.frameId,
15790             frameBorder : 'no',
15791             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
15792         }, this.el
15793         );
15794         
15795         
15796         this.iframe = iframe.dom;
15797
15798          this.assignDocWin();
15799         
15800         this.doc.designMode = 'on';
15801        
15802         this.doc.open();
15803         this.doc.write(this.getDocMarkup());
15804         this.doc.close();
15805
15806         
15807         var task = { // must defer to wait for browser to be ready
15808             run : function(){
15809                 //console.log("run task?" + this.doc.readyState);
15810                 this.assignDocWin();
15811                 if(this.doc.body || this.doc.readyState == 'complete'){
15812                     try {
15813                         this.doc.designMode="on";
15814                     } catch (e) {
15815                         return;
15816                     }
15817                     Roo.TaskMgr.stop(task);
15818                     this.initEditor.defer(10, this);
15819                 }
15820             },
15821             interval : 10,
15822             duration: 10000,
15823             scope: this
15824         };
15825         Roo.TaskMgr.start(task);
15826
15827         
15828          
15829     },
15830
15831     // private
15832     onResize : function(w, h)
15833     {
15834          Roo.log('resize: ' +w + ',' + h );
15835         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
15836         if(!this.iframe){
15837             return;
15838         }
15839         if(typeof w == 'number'){
15840             
15841             this.iframe.style.width = w + 'px';
15842         }
15843         if(typeof h == 'number'){
15844             
15845             this.iframe.style.height = h + 'px';
15846             if(this.doc){
15847                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
15848             }
15849         }
15850         
15851     },
15852
15853     /**
15854      * Toggles the editor between standard and source edit mode.
15855      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
15856      */
15857     toggleSourceEdit : function(sourceEditMode){
15858         
15859         this.sourceEditMode = sourceEditMode === true;
15860         
15861         if(this.sourceEditMode){
15862  
15863             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
15864             
15865         }else{
15866             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
15867             //this.iframe.className = '';
15868             this.deferFocus();
15869         }
15870         //this.setSize(this.owner.wrap.getSize());
15871         //this.fireEvent('editmodechange', this, this.sourceEditMode);
15872     },
15873
15874     
15875   
15876
15877     /**
15878      * Protected method that will not generally be called directly. If you need/want
15879      * custom HTML cleanup, this is the method you should override.
15880      * @param {String} html The HTML to be cleaned
15881      * return {String} The cleaned HTML
15882      */
15883     cleanHtml : function(html){
15884         html = String(html);
15885         if(html.length > 5){
15886             if(Roo.isSafari){ // strip safari nonsense
15887                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
15888             }
15889         }
15890         if(html == '&nbsp;'){
15891             html = '';
15892         }
15893         return html;
15894     },
15895
15896     /**
15897      * HTML Editor -> Textarea
15898      * Protected method that will not generally be called directly. Syncs the contents
15899      * of the editor iframe with the textarea.
15900      */
15901     syncValue : function(){
15902         if(this.initialized){
15903             var bd = (this.doc.body || this.doc.documentElement);
15904             //this.cleanUpPaste(); -- this is done else where and causes havoc..
15905             var html = bd.innerHTML;
15906             if(Roo.isSafari){
15907                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
15908                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
15909                 if(m && m[1]){
15910                     html = '<div style="'+m[0]+'">' + html + '</div>';
15911                 }
15912             }
15913             html = this.cleanHtml(html);
15914             // fix up the special chars.. normaly like back quotes in word...
15915             // however we do not want to do this with chinese..
15916             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
15917                 var cc = b.charCodeAt();
15918                 if (
15919                     (cc >= 0x4E00 && cc < 0xA000 ) ||
15920                     (cc >= 0x3400 && cc < 0x4E00 ) ||
15921                     (cc >= 0xf900 && cc < 0xfb00 )
15922                 ) {
15923                         return b;
15924                 }
15925                 return "&#"+cc+";" 
15926             });
15927             if(this.owner.fireEvent('beforesync', this, html) !== false){
15928                 this.el.dom.value = html;
15929                 this.owner.fireEvent('sync', this, html);
15930             }
15931         }
15932     },
15933
15934     /**
15935      * Protected method that will not generally be called directly. Pushes the value of the textarea
15936      * into the iframe editor.
15937      */
15938     pushValue : function(){
15939         if(this.initialized){
15940             var v = this.el.dom.value.trim();
15941             
15942 //            if(v.length < 1){
15943 //                v = '&#160;';
15944 //            }
15945             
15946             if(this.owner.fireEvent('beforepush', this, v) !== false){
15947                 var d = (this.doc.body || this.doc.documentElement);
15948                 d.innerHTML = v;
15949                 this.cleanUpPaste();
15950                 this.el.dom.value = d.innerHTML;
15951                 this.owner.fireEvent('push', this, v);
15952             }
15953         }
15954     },
15955
15956     // private
15957     deferFocus : function(){
15958         this.focus.defer(10, this);
15959     },
15960
15961     // doc'ed in Field
15962     focus : function(){
15963         if(this.win && !this.sourceEditMode){
15964             this.win.focus();
15965         }else{
15966             this.el.focus();
15967         }
15968     },
15969     
15970     assignDocWin: function()
15971     {
15972         var iframe = this.iframe;
15973         
15974          if(Roo.isIE){
15975             this.doc = iframe.contentWindow.document;
15976             this.win = iframe.contentWindow;
15977         } else {
15978             if (!Roo.get(this.frameId)) {
15979                 return;
15980             }
15981             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
15982             this.win = Roo.get(this.frameId).dom.contentWindow;
15983         }
15984     },
15985     
15986     // private
15987     initEditor : function(){
15988         //console.log("INIT EDITOR");
15989         this.assignDocWin();
15990         
15991         
15992         
15993         this.doc.designMode="on";
15994         this.doc.open();
15995         this.doc.write(this.getDocMarkup());
15996         this.doc.close();
15997         
15998         var dbody = (this.doc.body || this.doc.documentElement);
15999         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
16000         // this copies styles from the containing element into thsi one..
16001         // not sure why we need all of this..
16002         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
16003         
16004         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
16005         //ss['background-attachment'] = 'fixed'; // w3c
16006         dbody.bgProperties = 'fixed'; // ie
16007         //Roo.DomHelper.applyStyles(dbody, ss);
16008         Roo.EventManager.on(this.doc, {
16009             //'mousedown': this.onEditorEvent,
16010             'mouseup': this.onEditorEvent,
16011             'dblclick': this.onEditorEvent,
16012             'click': this.onEditorEvent,
16013             'keyup': this.onEditorEvent,
16014             buffer:100,
16015             scope: this
16016         });
16017         if(Roo.isGecko){
16018             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
16019         }
16020         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
16021             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
16022         }
16023         this.initialized = true;
16024
16025         this.owner.fireEvent('initialize', this);
16026         this.pushValue();
16027     },
16028
16029     // private
16030     onDestroy : function(){
16031         
16032         
16033         
16034         if(this.rendered){
16035             
16036             //for (var i =0; i < this.toolbars.length;i++) {
16037             //    // fixme - ask toolbars for heights?
16038             //    this.toolbars[i].onDestroy();
16039            // }
16040             
16041             //this.wrap.dom.innerHTML = '';
16042             //this.wrap.remove();
16043         }
16044     },
16045
16046     // private
16047     onFirstFocus : function(){
16048         
16049         this.assignDocWin();
16050         
16051         
16052         this.activated = true;
16053          
16054     
16055         if(Roo.isGecko){ // prevent silly gecko errors
16056             this.win.focus();
16057             var s = this.win.getSelection();
16058             if(!s.focusNode || s.focusNode.nodeType != 3){
16059                 var r = s.getRangeAt(0);
16060                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
16061                 r.collapse(true);
16062                 this.deferFocus();
16063             }
16064             try{
16065                 this.execCmd('useCSS', true);
16066                 this.execCmd('styleWithCSS', false);
16067             }catch(e){}
16068         }
16069         this.owner.fireEvent('activate', this);
16070     },
16071
16072     // private
16073     adjustFont: function(btn){
16074         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
16075         //if(Roo.isSafari){ // safari
16076         //    adjust *= 2;
16077        // }
16078         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
16079         if(Roo.isSafari){ // safari
16080             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
16081             v =  (v < 10) ? 10 : v;
16082             v =  (v > 48) ? 48 : v;
16083             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
16084             
16085         }
16086         
16087         
16088         v = Math.max(1, v+adjust);
16089         
16090         this.execCmd('FontSize', v  );
16091     },
16092
16093     onEditorEvent : function(e){
16094         this.owner.fireEvent('editorevent', this, e);
16095       //  this.updateToolbar();
16096         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
16097     },
16098
16099     insertTag : function(tg)
16100     {
16101         // could be a bit smarter... -> wrap the current selected tRoo..
16102         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
16103             
16104             range = this.createRange(this.getSelection());
16105             var wrappingNode = this.doc.createElement(tg.toLowerCase());
16106             wrappingNode.appendChild(range.extractContents());
16107             range.insertNode(wrappingNode);
16108
16109             return;
16110             
16111             
16112             
16113         }
16114         this.execCmd("formatblock",   tg);
16115         
16116     },
16117     
16118     insertText : function(txt)
16119     {
16120         
16121         
16122         var range = this.createRange();
16123         range.deleteContents();
16124                //alert(Sender.getAttribute('label'));
16125                
16126         range.insertNode(this.doc.createTextNode(txt));
16127     } ,
16128     
16129      
16130
16131     /**
16132      * Executes a Midas editor command on the editor document and performs necessary focus and
16133      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
16134      * @param {String} cmd The Midas command
16135      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16136      */
16137     relayCmd : function(cmd, value){
16138         this.win.focus();
16139         this.execCmd(cmd, value);
16140         this.owner.fireEvent('editorevent', this);
16141         //this.updateToolbar();
16142         this.owner.deferFocus();
16143     },
16144
16145     /**
16146      * Executes a Midas editor command directly on the editor document.
16147      * For visual commands, you should use {@link #relayCmd} instead.
16148      * <b>This should only be called after the editor is initialized.</b>
16149      * @param {String} cmd The Midas command
16150      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16151      */
16152     execCmd : function(cmd, value){
16153         this.doc.execCommand(cmd, false, value === undefined ? null : value);
16154         this.syncValue();
16155     },
16156  
16157  
16158    
16159     /**
16160      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
16161      * to insert tRoo.
16162      * @param {String} text | dom node.. 
16163      */
16164     insertAtCursor : function(text)
16165     {
16166         
16167         
16168         
16169         if(!this.activated){
16170             return;
16171         }
16172         /*
16173         if(Roo.isIE){
16174             this.win.focus();
16175             var r = this.doc.selection.createRange();
16176             if(r){
16177                 r.collapse(true);
16178                 r.pasteHTML(text);
16179                 this.syncValue();
16180                 this.deferFocus();
16181             
16182             }
16183             return;
16184         }
16185         */
16186         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
16187             this.win.focus();
16188             
16189             
16190             // from jquery ui (MIT licenced)
16191             var range, node;
16192             var win = this.win;
16193             
16194             if (win.getSelection && win.getSelection().getRangeAt) {
16195                 range = win.getSelection().getRangeAt(0);
16196                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
16197                 range.insertNode(node);
16198             } else if (win.document.selection && win.document.selection.createRange) {
16199                 // no firefox support
16200                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
16201                 win.document.selection.createRange().pasteHTML(txt);
16202             } else {
16203                 // no firefox support
16204                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
16205                 this.execCmd('InsertHTML', txt);
16206             } 
16207             
16208             this.syncValue();
16209             
16210             this.deferFocus();
16211         }
16212     },
16213  // private
16214     mozKeyPress : function(e){
16215         if(e.ctrlKey){
16216             var c = e.getCharCode(), cmd;
16217           
16218             if(c > 0){
16219                 c = String.fromCharCode(c).toLowerCase();
16220                 switch(c){
16221                     case 'b':
16222                         cmd = 'bold';
16223                         break;
16224                     case 'i':
16225                         cmd = 'italic';
16226                         break;
16227                     
16228                     case 'u':
16229                         cmd = 'underline';
16230                         break;
16231                     
16232                     case 'v':
16233                         this.cleanUpPaste.defer(100, this);
16234                         return;
16235                         
16236                 }
16237                 if(cmd){
16238                     this.win.focus();
16239                     this.execCmd(cmd);
16240                     this.deferFocus();
16241                     e.preventDefault();
16242                 }
16243                 
16244             }
16245         }
16246     },
16247
16248     // private
16249     fixKeys : function(){ // load time branching for fastest keydown performance
16250         if(Roo.isIE){
16251             return function(e){
16252                 var k = e.getKey(), r;
16253                 if(k == e.TAB){
16254                     e.stopEvent();
16255                     r = this.doc.selection.createRange();
16256                     if(r){
16257                         r.collapse(true);
16258                         r.pasteHTML('&#160;&#160;&#160;&#160;');
16259                         this.deferFocus();
16260                     }
16261                     return;
16262                 }
16263                 
16264                 if(k == e.ENTER){
16265                     r = this.doc.selection.createRange();
16266                     if(r){
16267                         var target = r.parentElement();
16268                         if(!target || target.tagName.toLowerCase() != 'li'){
16269                             e.stopEvent();
16270                             r.pasteHTML('<br />');
16271                             r.collapse(false);
16272                             r.select();
16273                         }
16274                     }
16275                 }
16276                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
16277                     this.cleanUpPaste.defer(100, this);
16278                     return;
16279                 }
16280                 
16281                 
16282             };
16283         }else if(Roo.isOpera){
16284             return function(e){
16285                 var k = e.getKey();
16286                 if(k == e.TAB){
16287                     e.stopEvent();
16288                     this.win.focus();
16289                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
16290                     this.deferFocus();
16291                 }
16292                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
16293                     this.cleanUpPaste.defer(100, this);
16294                     return;
16295                 }
16296                 
16297             };
16298         }else if(Roo.isSafari){
16299             return function(e){
16300                 var k = e.getKey();
16301                 
16302                 if(k == e.TAB){
16303                     e.stopEvent();
16304                     this.execCmd('InsertText','\t');
16305                     this.deferFocus();
16306                     return;
16307                 }
16308                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
16309                     this.cleanUpPaste.defer(100, this);
16310                     return;
16311                 }
16312                 
16313              };
16314         }
16315     }(),
16316     
16317     getAllAncestors: function()
16318     {
16319         var p = this.getSelectedNode();
16320         var a = [];
16321         if (!p) {
16322             a.push(p); // push blank onto stack..
16323             p = this.getParentElement();
16324         }
16325         
16326         
16327         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
16328             a.push(p);
16329             p = p.parentNode;
16330         }
16331         a.push(this.doc.body);
16332         return a;
16333     },
16334     lastSel : false,
16335     lastSelNode : false,
16336     
16337     
16338     getSelection : function() 
16339     {
16340         this.assignDocWin();
16341         return Roo.isIE ? this.doc.selection : this.win.getSelection();
16342     },
16343     
16344     getSelectedNode: function() 
16345     {
16346         // this may only work on Gecko!!!
16347         
16348         // should we cache this!!!!
16349         
16350         
16351         
16352          
16353         var range = this.createRange(this.getSelection()).cloneRange();
16354         
16355         if (Roo.isIE) {
16356             var parent = range.parentElement();
16357             while (true) {
16358                 var testRange = range.duplicate();
16359                 testRange.moveToElementText(parent);
16360                 if (testRange.inRange(range)) {
16361                     break;
16362                 }
16363                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
16364                     break;
16365                 }
16366                 parent = parent.parentElement;
16367             }
16368             return parent;
16369         }
16370         
16371         // is ancestor a text element.
16372         var ac =  range.commonAncestorContainer;
16373         if (ac.nodeType == 3) {
16374             ac = ac.parentNode;
16375         }
16376         
16377         var ar = ac.childNodes;
16378          
16379         var nodes = [];
16380         var other_nodes = [];
16381         var has_other_nodes = false;
16382         for (var i=0;i<ar.length;i++) {
16383             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
16384                 continue;
16385             }
16386             // fullly contained node.
16387             
16388             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
16389                 nodes.push(ar[i]);
16390                 continue;
16391             }
16392             
16393             // probably selected..
16394             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
16395                 other_nodes.push(ar[i]);
16396                 continue;
16397             }
16398             // outer..
16399             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
16400                 continue;
16401             }
16402             
16403             
16404             has_other_nodes = true;
16405         }
16406         if (!nodes.length && other_nodes.length) {
16407             nodes= other_nodes;
16408         }
16409         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
16410             return false;
16411         }
16412         
16413         return nodes[0];
16414     },
16415     createRange: function(sel)
16416     {
16417         // this has strange effects when using with 
16418         // top toolbar - not sure if it's a great idea.
16419         //this.editor.contentWindow.focus();
16420         if (typeof sel != "undefined") {
16421             try {
16422                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
16423             } catch(e) {
16424                 return this.doc.createRange();
16425             }
16426         } else {
16427             return this.doc.createRange();
16428         }
16429     },
16430     getParentElement: function()
16431     {
16432         
16433         this.assignDocWin();
16434         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
16435         
16436         var range = this.createRange(sel);
16437          
16438         try {
16439             var p = range.commonAncestorContainer;
16440             while (p.nodeType == 3) { // text node
16441                 p = p.parentNode;
16442             }
16443             return p;
16444         } catch (e) {
16445             return null;
16446         }
16447     
16448     },
16449     /***
16450      *
16451      * Range intersection.. the hard stuff...
16452      *  '-1' = before
16453      *  '0' = hits..
16454      *  '1' = after.
16455      *         [ -- selected range --- ]
16456      *   [fail]                        [fail]
16457      *
16458      *    basically..
16459      *      if end is before start or  hits it. fail.
16460      *      if start is after end or hits it fail.
16461      *
16462      *   if either hits (but other is outside. - then it's not 
16463      *   
16464      *    
16465      **/
16466     
16467     
16468     // @see http://www.thismuchiknow.co.uk/?p=64.
16469     rangeIntersectsNode : function(range, node)
16470     {
16471         var nodeRange = node.ownerDocument.createRange();
16472         try {
16473             nodeRange.selectNode(node);
16474         } catch (e) {
16475             nodeRange.selectNodeContents(node);
16476         }
16477     
16478         var rangeStartRange = range.cloneRange();
16479         rangeStartRange.collapse(true);
16480     
16481         var rangeEndRange = range.cloneRange();
16482         rangeEndRange.collapse(false);
16483     
16484         var nodeStartRange = nodeRange.cloneRange();
16485         nodeStartRange.collapse(true);
16486     
16487         var nodeEndRange = nodeRange.cloneRange();
16488         nodeEndRange.collapse(false);
16489     
16490         return rangeStartRange.compareBoundaryPoints(
16491                  Range.START_TO_START, nodeEndRange) == -1 &&
16492                rangeEndRange.compareBoundaryPoints(
16493                  Range.START_TO_START, nodeStartRange) == 1;
16494         
16495          
16496     },
16497     rangeCompareNode : function(range, node)
16498     {
16499         var nodeRange = node.ownerDocument.createRange();
16500         try {
16501             nodeRange.selectNode(node);
16502         } catch (e) {
16503             nodeRange.selectNodeContents(node);
16504         }
16505         
16506         
16507         range.collapse(true);
16508     
16509         nodeRange.collapse(true);
16510      
16511         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
16512         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
16513          
16514         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
16515         
16516         var nodeIsBefore   =  ss == 1;
16517         var nodeIsAfter    = ee == -1;
16518         
16519         if (nodeIsBefore && nodeIsAfter)
16520             return 0; // outer
16521         if (!nodeIsBefore && nodeIsAfter)
16522             return 1; //right trailed.
16523         
16524         if (nodeIsBefore && !nodeIsAfter)
16525             return 2;  // left trailed.
16526         // fully contined.
16527         return 3;
16528     },
16529
16530     // private? - in a new class?
16531     cleanUpPaste :  function()
16532     {
16533         // cleans up the whole document..
16534         Roo.log('cleanuppaste');
16535         
16536         this.cleanUpChildren(this.doc.body);
16537         var clean = this.cleanWordChars(this.doc.body.innerHTML);
16538         if (clean != this.doc.body.innerHTML) {
16539             this.doc.body.innerHTML = clean;
16540         }
16541         
16542     },
16543     
16544     cleanWordChars : function(input) {// change the chars to hex code
16545         var he = Roo.HtmlEditorCore;
16546         
16547         var output = input;
16548         Roo.each(he.swapCodes, function(sw) { 
16549             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
16550             
16551             output = output.replace(swapper, sw[1]);
16552         });
16553         
16554         return output;
16555     },
16556     
16557     
16558     cleanUpChildren : function (n)
16559     {
16560         if (!n.childNodes.length) {
16561             return;
16562         }
16563         for (var i = n.childNodes.length-1; i > -1 ; i--) {
16564            this.cleanUpChild(n.childNodes[i]);
16565         }
16566     },
16567     
16568     
16569         
16570     
16571     cleanUpChild : function (node)
16572     {
16573         var ed = this;
16574         //console.log(node);
16575         if (node.nodeName == "#text") {
16576             // clean up silly Windows -- stuff?
16577             return; 
16578         }
16579         if (node.nodeName == "#comment") {
16580             node.parentNode.removeChild(node);
16581             // clean up silly Windows -- stuff?
16582             return; 
16583         }
16584         
16585         if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1 && this.clearUp) {
16586             // remove node.
16587             node.parentNode.removeChild(node);
16588             return;
16589             
16590         }
16591         
16592         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
16593         
16594         // remove <a name=....> as rendering on yahoo mailer is borked with this.
16595         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
16596         
16597         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
16598         //    remove_keep_children = true;
16599         //}
16600         
16601         if (remove_keep_children) {
16602             this.cleanUpChildren(node);
16603             // inserts everything just before this node...
16604             while (node.childNodes.length) {
16605                 var cn = node.childNodes[0];
16606                 node.removeChild(cn);
16607                 node.parentNode.insertBefore(cn, node);
16608             }
16609             node.parentNode.removeChild(node);
16610             return;
16611         }
16612         
16613         if (!node.attributes || !node.attributes.length) {
16614             this.cleanUpChildren(node);
16615             return;
16616         }
16617         
16618         function cleanAttr(n,v)
16619         {
16620             
16621             if (v.match(/^\./) || v.match(/^\//)) {
16622                 return;
16623             }
16624             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
16625                 return;
16626             }
16627             if (v.match(/^#/)) {
16628                 return;
16629             }
16630 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
16631             node.removeAttribute(n);
16632             
16633         }
16634         
16635         function cleanStyle(n,v)
16636         {
16637             if (v.match(/expression/)) { //XSS?? should we even bother..
16638                 node.removeAttribute(n);
16639                 return;
16640             }
16641             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
16642             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
16643             
16644             
16645             var parts = v.split(/;/);
16646             var clean = [];
16647             
16648             Roo.each(parts, function(p) {
16649                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
16650                 if (!p.length) {
16651                     return true;
16652                 }
16653                 var l = p.split(':').shift().replace(/\s+/g,'');
16654                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
16655                 
16656                 if ( cblack.indexOf(l) > -1) {
16657 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
16658                     //node.removeAttribute(n);
16659                     return true;
16660                 }
16661                 //Roo.log()
16662                 // only allow 'c whitelisted system attributes'
16663                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
16664 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
16665                     //node.removeAttribute(n);
16666                     return true;
16667                 }
16668                 
16669                 
16670                  
16671                 
16672                 clean.push(p);
16673                 return true;
16674             });
16675             if (clean.length) { 
16676                 node.setAttribute(n, clean.join(';'));
16677             } else {
16678                 node.removeAttribute(n);
16679             }
16680             
16681         }
16682         
16683         
16684         for (var i = node.attributes.length-1; i > -1 ; i--) {
16685             var a = node.attributes[i];
16686             //console.log(a);
16687             
16688             if (a.name.toLowerCase().substr(0,2)=='on')  {
16689                 node.removeAttribute(a.name);
16690                 continue;
16691             }
16692             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
16693                 node.removeAttribute(a.name);
16694                 continue;
16695             }
16696             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
16697                 cleanAttr(a.name,a.value); // fixme..
16698                 continue;
16699             }
16700             if (a.name == 'style') {
16701                 cleanStyle(a.name,a.value);
16702                 continue;
16703             }
16704             /// clean up MS crap..
16705             // tecnically this should be a list of valid class'es..
16706             
16707             
16708             if (a.name == 'class') {
16709                 if (a.value.match(/^Mso/)) {
16710                     node.className = '';
16711                 }
16712                 
16713                 if (a.value.match(/body/)) {
16714                     node.className = '';
16715                 }
16716                 continue;
16717             }
16718             
16719             // style cleanup!?
16720             // class cleanup?
16721             
16722         }
16723         
16724         
16725         this.cleanUpChildren(node);
16726         
16727         
16728     },
16729     /**
16730      * Clean up MS wordisms...
16731      */
16732     cleanWord : function(node)
16733     {
16734         var _t = this;
16735         var cleanWordChildren = function()
16736         {
16737             if (!node.childNodes.length) {
16738                 return;
16739             }
16740             for (var i = node.childNodes.length-1; i > -1 ; i--) {
16741                _t.cleanWord(node.childNodes[i]);
16742             }
16743         }
16744         
16745         
16746         if (!node) {
16747             this.cleanWord(this.doc.body);
16748             return;
16749         }
16750         if (node.nodeName == "#text") {
16751             // clean up silly Windows -- stuff?
16752             return; 
16753         }
16754         if (node.nodeName == "#comment") {
16755             node.parentNode.removeChild(node);
16756             // clean up silly Windows -- stuff?
16757             return; 
16758         }
16759         
16760         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
16761             node.parentNode.removeChild(node);
16762             return;
16763         }
16764         
16765         // remove - but keep children..
16766         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
16767             while (node.childNodes.length) {
16768                 var cn = node.childNodes[0];
16769                 node.removeChild(cn);
16770                 node.parentNode.insertBefore(cn, node);
16771             }
16772             node.parentNode.removeChild(node);
16773             cleanWordChildren();
16774             return;
16775         }
16776         // clean styles
16777         if (node.className.length) {
16778             
16779             var cn = node.className.split(/\W+/);
16780             var cna = [];
16781             Roo.each(cn, function(cls) {
16782                 if (cls.match(/Mso[a-zA-Z]+/)) {
16783                     return;
16784                 }
16785                 cna.push(cls);
16786             });
16787             node.className = cna.length ? cna.join(' ') : '';
16788             if (!cna.length) {
16789                 node.removeAttribute("class");
16790             }
16791         }
16792         
16793         if (node.hasAttribute("lang")) {
16794             node.removeAttribute("lang");
16795         }
16796         
16797         if (node.hasAttribute("style")) {
16798             
16799             var styles = node.getAttribute("style").split(";");
16800             var nstyle = [];
16801             Roo.each(styles, function(s) {
16802                 if (!s.match(/:/)) {
16803                     return;
16804                 }
16805                 var kv = s.split(":");
16806                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
16807                     return;
16808                 }
16809                 // what ever is left... we allow.
16810                 nstyle.push(s);
16811             });
16812             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
16813             if (!nstyle.length) {
16814                 node.removeAttribute('style');
16815             }
16816         }
16817         
16818         cleanWordChildren();
16819         
16820         
16821     },
16822     domToHTML : function(currentElement, depth, nopadtext) {
16823         
16824             depth = depth || 0;
16825             nopadtext = nopadtext || false;
16826         
16827             if (!currentElement) {
16828                 return this.domToHTML(this.doc.body);
16829             }
16830             
16831             //Roo.log(currentElement);
16832             var j;
16833             var allText = false;
16834             var nodeName = currentElement.nodeName;
16835             var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
16836             
16837             if  (nodeName == '#text') {
16838                 return currentElement.nodeValue;
16839             }
16840             
16841             
16842             var ret = '';
16843             if (nodeName != 'BODY') {
16844                  
16845                 var i = 0;
16846                 // Prints the node tagName, such as <A>, <IMG>, etc
16847                 if (tagName) {
16848                     var attr = [];
16849                     for(i = 0; i < currentElement.attributes.length;i++) {
16850                         // quoting?
16851                         var aname = currentElement.attributes.item(i).name;
16852                         if (!currentElement.attributes.item(i).value.length) {
16853                             continue;
16854                         }
16855                         attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
16856                     }
16857                     
16858                     ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
16859                 } 
16860                 else {
16861                     
16862                     // eack
16863                 }
16864             } else {
16865                 tagName = false;
16866             }
16867             if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
16868                 return ret;
16869             }
16870             if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
16871                 nopadtext = true;
16872             }
16873             
16874             
16875             // Traverse the tree
16876             i = 0;
16877             var currentElementChild = currentElement.childNodes.item(i);
16878             var allText = true;
16879             var innerHTML  = '';
16880             lastnode = '';
16881             while (currentElementChild) {
16882                 // Formatting code (indent the tree so it looks nice on the screen)
16883                 var nopad = nopadtext;
16884                 if (lastnode == 'SPAN') {
16885                     nopad  = true;
16886                 }
16887                 // text
16888                 if  (currentElementChild.nodeName == '#text') {
16889                     var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
16890                     if (!nopad && toadd.length > 80) {
16891                         innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
16892                     }
16893                     innerHTML  += toadd;
16894                     
16895                     i++;
16896                     currentElementChild = currentElement.childNodes.item(i);
16897                     lastNode = '';
16898                     continue;
16899                 }
16900                 allText = false;
16901                 
16902                 innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
16903                     
16904                 // Recursively traverse the tree structure of the child node
16905                 innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
16906                 lastnode = currentElementChild.nodeName;
16907                 i++;
16908                 currentElementChild=currentElement.childNodes.item(i);
16909             }
16910             
16911             ret += innerHTML;
16912             
16913             if (!allText) {
16914                     // The remaining code is mostly for formatting the tree
16915                 ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
16916             }
16917             
16918             
16919             if (tagName) {
16920                 ret+= "</"+tagName+">";
16921             }
16922             return ret;
16923             
16924         }
16925     
16926     // hide stuff that is not compatible
16927     /**
16928      * @event blur
16929      * @hide
16930      */
16931     /**
16932      * @event change
16933      * @hide
16934      */
16935     /**
16936      * @event focus
16937      * @hide
16938      */
16939     /**
16940      * @event specialkey
16941      * @hide
16942      */
16943     /**
16944      * @cfg {String} fieldClass @hide
16945      */
16946     /**
16947      * @cfg {String} focusClass @hide
16948      */
16949     /**
16950      * @cfg {String} autoCreate @hide
16951      */
16952     /**
16953      * @cfg {String} inputType @hide
16954      */
16955     /**
16956      * @cfg {String} invalidClass @hide
16957      */
16958     /**
16959      * @cfg {String} invalidText @hide
16960      */
16961     /**
16962      * @cfg {String} msgFx @hide
16963      */
16964     /**
16965      * @cfg {String} validateOnBlur @hide
16966      */
16967 });
16968
16969 Roo.HtmlEditorCore.white = [
16970         'area', 'br', 'img', 'input', 'hr', 'wbr',
16971         
16972        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
16973        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
16974        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
16975        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
16976        'table',   'ul',         'xmp', 
16977        
16978        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
16979       'thead',   'tr', 
16980      
16981       'dir', 'menu', 'ol', 'ul', 'dl',
16982        
16983       'embed',  'object'
16984 ];
16985
16986
16987 Roo.HtmlEditorCore.black = [
16988     //    'embed',  'object', // enable - backend responsiblity to clean thiese
16989         'applet', // 
16990         'base',   'basefont', 'bgsound', 'blink',  'body', 
16991         'frame',  'frameset', 'head',    'html',   'ilayer', 
16992         'iframe', 'layer',  'link',     'meta',    'object',   
16993         'script', 'style' ,'title',  'xml' // clean later..
16994 ];
16995 Roo.HtmlEditorCore.clean = [
16996     'script', 'style', 'title', 'xml'
16997 ];
16998 Roo.HtmlEditorCore.remove = [
16999     'font'
17000 ];
17001 // attributes..
17002
17003 Roo.HtmlEditorCore.ablack = [
17004     'on'
17005 ];
17006     
17007 Roo.HtmlEditorCore.aclean = [ 
17008     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
17009 ];
17010
17011 // protocols..
17012 Roo.HtmlEditorCore.pwhite= [
17013         'http',  'https',  'mailto'
17014 ];
17015
17016 // white listed style attributes.
17017 Roo.HtmlEditorCore.cwhite= [
17018       //  'text-align', /// default is to allow most things..
17019       
17020          
17021 //        'font-size'//??
17022 ];
17023
17024 // black listed style attributes.
17025 Roo.HtmlEditorCore.cblack= [
17026       //  'font-size' -- this can be set by the project 
17027 ];
17028
17029
17030 Roo.HtmlEditorCore.swapCodes   =[ 
17031     [    8211, "--" ], 
17032     [    8212, "--" ], 
17033     [    8216,  "'" ],  
17034     [    8217, "'" ],  
17035     [    8220, '"' ],  
17036     [    8221, '"' ],  
17037     [    8226, "*" ],  
17038     [    8230, "..." ]
17039 ]; 
17040
17041     /*
17042  * - LGPL
17043  *
17044  * HtmlEditor
17045  * 
17046  */
17047
17048 /**
17049  * @class Roo.bootstrap.HtmlEditor
17050  * @extends Roo.bootstrap.TextArea
17051  * Bootstrap HtmlEditor class
17052
17053  * @constructor
17054  * Create a new HtmlEditor
17055  * @param {Object} config The config object
17056  */
17057
17058 Roo.bootstrap.HtmlEditor = function(config){
17059     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
17060     if (!this.toolbars) {
17061         this.toolbars = [];
17062     }
17063     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
17064     this.addEvents({
17065             /**
17066              * @event initialize
17067              * Fires when the editor is fully initialized (including the iframe)
17068              * @param {HtmlEditor} this
17069              */
17070             initialize: true,
17071             /**
17072              * @event activate
17073              * Fires when the editor is first receives the focus. Any insertion must wait
17074              * until after this event.
17075              * @param {HtmlEditor} this
17076              */
17077             activate: true,
17078              /**
17079              * @event beforesync
17080              * Fires before the textarea is updated with content from the editor iframe. Return false
17081              * to cancel the sync.
17082              * @param {HtmlEditor} this
17083              * @param {String} html
17084              */
17085             beforesync: true,
17086              /**
17087              * @event beforepush
17088              * Fires before the iframe editor is updated with content from the textarea. Return false
17089              * to cancel the push.
17090              * @param {HtmlEditor} this
17091              * @param {String} html
17092              */
17093             beforepush: true,
17094              /**
17095              * @event sync
17096              * Fires when the textarea is updated with content from the editor iframe.
17097              * @param {HtmlEditor} this
17098              * @param {String} html
17099              */
17100             sync: true,
17101              /**
17102              * @event push
17103              * Fires when the iframe editor is updated with content from the textarea.
17104              * @param {HtmlEditor} this
17105              * @param {String} html
17106              */
17107             push: true,
17108              /**
17109              * @event editmodechange
17110              * Fires when the editor switches edit modes
17111              * @param {HtmlEditor} this
17112              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
17113              */
17114             editmodechange: true,
17115             /**
17116              * @event editorevent
17117              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
17118              * @param {HtmlEditor} this
17119              */
17120             editorevent: true,
17121             /**
17122              * @event firstfocus
17123              * Fires when on first focus - needed by toolbars..
17124              * @param {HtmlEditor} this
17125              */
17126             firstfocus: true,
17127             /**
17128              * @event autosave
17129              * Auto save the htmlEditor value as a file into Events
17130              * @param {HtmlEditor} this
17131              */
17132             autosave: true,
17133             /**
17134              * @event savedpreview
17135              * preview the saved version of htmlEditor
17136              * @param {HtmlEditor} this
17137              */
17138             savedpreview: true
17139         });
17140 };
17141
17142
17143 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
17144     
17145     
17146       /**
17147      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
17148      */
17149     toolbars : false,
17150    
17151      /**
17152      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
17153      *                        Roo.resizable.
17154      */
17155     resizable : false,
17156      /**
17157      * @cfg {Number} height (in pixels)
17158      */   
17159     height: 300,
17160    /**
17161      * @cfg {Number} width (in pixels)
17162      */   
17163     width: false,
17164     
17165     /**
17166      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
17167      * 
17168      */
17169     stylesheets: false,
17170     
17171     // id of frame..
17172     frameId: false,
17173     
17174     // private properties
17175     validationEvent : false,
17176     deferHeight: true,
17177     initialized : false,
17178     activated : false,
17179     
17180     onFocus : Roo.emptyFn,
17181     iframePad:3,
17182     hideMode:'offsets',
17183     
17184     
17185     tbContainer : false,
17186     
17187     toolbarContainer :function() {
17188         return this.wrap.select('.x-html-editor-tb',true).first();
17189     },
17190
17191     /**
17192      * Protected method that will not generally be called directly. It
17193      * is called when the editor creates its toolbar. Override this method if you need to
17194      * add custom toolbar buttons.
17195      * @param {HtmlEditor} editor
17196      */
17197     createToolbar : function(){
17198         
17199         Roo.log("create toolbars");
17200         
17201         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
17202         this.toolbars[0].render(this.toolbarContainer());
17203         
17204         return;
17205         
17206 //        if (!editor.toolbars || !editor.toolbars.length) {
17207 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
17208 //        }
17209 //        
17210 //        for (var i =0 ; i < editor.toolbars.length;i++) {
17211 //            editor.toolbars[i] = Roo.factory(
17212 //                    typeof(editor.toolbars[i]) == 'string' ?
17213 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
17214 //                Roo.bootstrap.HtmlEditor);
17215 //            editor.toolbars[i].init(editor);
17216 //        }
17217     },
17218
17219      
17220     // private
17221     onRender : function(ct, position)
17222     {
17223        // Roo.log("Call onRender: " + this.xtype);
17224         var _t = this;
17225         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
17226       
17227         this.wrap = this.inputEl().wrap({
17228             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
17229         });
17230         
17231         this.editorcore.onRender(ct, position);
17232          
17233         if (this.resizable) {
17234             this.resizeEl = new Roo.Resizable(this.wrap, {
17235                 pinned : true,
17236                 wrap: true,
17237                 dynamic : true,
17238                 minHeight : this.height,
17239                 height: this.height,
17240                 handles : this.resizable,
17241                 width: this.width,
17242                 listeners : {
17243                     resize : function(r, w, h) {
17244                         _t.onResize(w,h); // -something
17245                     }
17246                 }
17247             });
17248             
17249         }
17250         this.createToolbar(this);
17251        
17252         
17253         if(!this.width && this.resizable){
17254             this.setSize(this.wrap.getSize());
17255         }
17256         if (this.resizeEl) {
17257             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
17258             // should trigger onReize..
17259         }
17260         
17261     },
17262
17263     // private
17264     onResize : function(w, h)
17265     {
17266         Roo.log('resize: ' +w + ',' + h );
17267         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
17268         var ew = false;
17269         var eh = false;
17270         
17271         if(this.inputEl() ){
17272             if(typeof w == 'number'){
17273                 var aw = w - this.wrap.getFrameWidth('lr');
17274                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
17275                 ew = aw;
17276             }
17277             if(typeof h == 'number'){
17278                  var tbh = -11;  // fixme it needs to tool bar size!
17279                 for (var i =0; i < this.toolbars.length;i++) {
17280                     // fixme - ask toolbars for heights?
17281                     tbh += this.toolbars[i].el.getHeight();
17282                     //if (this.toolbars[i].footer) {
17283                     //    tbh += this.toolbars[i].footer.el.getHeight();
17284                     //}
17285                 }
17286               
17287                 
17288                 
17289                 
17290                 
17291                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
17292                 ah -= 5; // knock a few pixes off for look..
17293                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
17294                 var eh = ah;
17295             }
17296         }
17297         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
17298         this.editorcore.onResize(ew,eh);
17299         
17300     },
17301
17302     /**
17303      * Toggles the editor between standard and source edit mode.
17304      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
17305      */
17306     toggleSourceEdit : function(sourceEditMode)
17307     {
17308         this.editorcore.toggleSourceEdit(sourceEditMode);
17309         
17310         if(this.editorcore.sourceEditMode){
17311             Roo.log('editor - showing textarea');
17312             
17313 //            Roo.log('in');
17314 //            Roo.log(this.syncValue());
17315             this.syncValue();
17316             this.inputEl().removeClass(['hide', 'x-hidden']);
17317             this.inputEl().dom.removeAttribute('tabIndex');
17318             this.inputEl().focus();
17319         }else{
17320             Roo.log('editor - hiding textarea');
17321 //            Roo.log('out')
17322 //            Roo.log(this.pushValue()); 
17323             this.pushValue();
17324             
17325             this.inputEl().addClass(['hide', 'x-hidden']);
17326             this.inputEl().dom.setAttribute('tabIndex', -1);
17327             //this.deferFocus();
17328         }
17329          
17330         if(this.resizable){
17331             this.setSize(this.wrap.getSize());
17332         }
17333         
17334         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
17335     },
17336  
17337     // private (for BoxComponent)
17338     adjustSize : Roo.BoxComponent.prototype.adjustSize,
17339
17340     // private (for BoxComponent)
17341     getResizeEl : function(){
17342         return this.wrap;
17343     },
17344
17345     // private (for BoxComponent)
17346     getPositionEl : function(){
17347         return this.wrap;
17348     },
17349
17350     // private
17351     initEvents : function(){
17352         this.originalValue = this.getValue();
17353     },
17354
17355 //    /**
17356 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
17357 //     * @method
17358 //     */
17359 //    markInvalid : Roo.emptyFn,
17360 //    /**
17361 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
17362 //     * @method
17363 //     */
17364 //    clearInvalid : Roo.emptyFn,
17365
17366     setValue : function(v){
17367         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
17368         this.editorcore.pushValue();
17369     },
17370
17371      
17372     // private
17373     deferFocus : function(){
17374         this.focus.defer(10, this);
17375     },
17376
17377     // doc'ed in Field
17378     focus : function(){
17379         this.editorcore.focus();
17380         
17381     },
17382       
17383
17384     // private
17385     onDestroy : function(){
17386         
17387         
17388         
17389         if(this.rendered){
17390             
17391             for (var i =0; i < this.toolbars.length;i++) {
17392                 // fixme - ask toolbars for heights?
17393                 this.toolbars[i].onDestroy();
17394             }
17395             
17396             this.wrap.dom.innerHTML = '';
17397             this.wrap.remove();
17398         }
17399     },
17400
17401     // private
17402     onFirstFocus : function(){
17403         //Roo.log("onFirstFocus");
17404         this.editorcore.onFirstFocus();
17405          for (var i =0; i < this.toolbars.length;i++) {
17406             this.toolbars[i].onFirstFocus();
17407         }
17408         
17409     },
17410     
17411     // private
17412     syncValue : function()
17413     {   
17414         this.editorcore.syncValue();
17415     },
17416     
17417     pushValue : function()
17418     {   
17419         this.editorcore.pushValue();
17420     }
17421      
17422     
17423     // hide stuff that is not compatible
17424     /**
17425      * @event blur
17426      * @hide
17427      */
17428     /**
17429      * @event change
17430      * @hide
17431      */
17432     /**
17433      * @event focus
17434      * @hide
17435      */
17436     /**
17437      * @event specialkey
17438      * @hide
17439      */
17440     /**
17441      * @cfg {String} fieldClass @hide
17442      */
17443     /**
17444      * @cfg {String} focusClass @hide
17445      */
17446     /**
17447      * @cfg {String} autoCreate @hide
17448      */
17449     /**
17450      * @cfg {String} inputType @hide
17451      */
17452     /**
17453      * @cfg {String} invalidClass @hide
17454      */
17455     /**
17456      * @cfg {String} invalidText @hide
17457      */
17458     /**
17459      * @cfg {String} msgFx @hide
17460      */
17461     /**
17462      * @cfg {String} validateOnBlur @hide
17463      */
17464 });
17465  
17466     
17467    
17468    
17469    
17470       
17471 Roo.namespace('Roo.bootstrap.htmleditor');
17472 /**
17473  * @class Roo.bootstrap.HtmlEditorToolbar1
17474  * Basic Toolbar
17475  * 
17476  * Usage:
17477  *
17478  new Roo.bootstrap.HtmlEditor({
17479     ....
17480     toolbars : [
17481         new Roo.bootstrap.HtmlEditorToolbar1({
17482             disable : { fonts: 1 , format: 1, ..., ... , ...],
17483             btns : [ .... ]
17484         })
17485     }
17486      
17487  * 
17488  * @cfg {Object} disable List of elements to disable..
17489  * @cfg {Array} btns List of additional buttons.
17490  * 
17491  * 
17492  * NEEDS Extra CSS? 
17493  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
17494  */
17495  
17496 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
17497 {
17498     
17499     Roo.apply(this, config);
17500     
17501     // default disabled, based on 'good practice'..
17502     this.disable = this.disable || {};
17503     Roo.applyIf(this.disable, {
17504         fontSize : true,
17505         colors : true,
17506         specialElements : true
17507     });
17508     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
17509     
17510     this.editor = config.editor;
17511     this.editorcore = config.editor.editorcore;
17512     
17513     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
17514     
17515     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
17516     // dont call parent... till later.
17517 }
17518 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
17519      
17520     bar : true,
17521     
17522     editor : false,
17523     editorcore : false,
17524     
17525     
17526     formats : [
17527         "p" ,  
17528         "h1","h2","h3","h4","h5","h6", 
17529         "pre", "code", 
17530         "abbr", "acronym", "address", "cite", "samp", "var",
17531         'div','span'
17532     ],
17533     
17534     onRender : function(ct, position)
17535     {
17536        // Roo.log("Call onRender: " + this.xtype);
17537         
17538        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
17539        Roo.log(this.el);
17540        this.el.dom.style.marginBottom = '0';
17541        var _this = this;
17542        var editorcore = this.editorcore;
17543        var editor= this.editor;
17544        
17545        var children = [];
17546        var btn = function(id,cmd , toggle, handler){
17547        
17548             var  event = toggle ? 'toggle' : 'click';
17549        
17550             var a = {
17551                 size : 'sm',
17552                 xtype: 'Button',
17553                 xns: Roo.bootstrap,
17554                 glyphicon : id,
17555                 cmd : id || cmd,
17556                 enableToggle:toggle !== false,
17557                 //html : 'submit'
17558                 pressed : toggle ? false : null,
17559                 listeners : {}
17560             }
17561             a.listeners[toggle ? 'toggle' : 'click'] = function() {
17562                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
17563             }
17564             children.push(a);
17565             return a;
17566        }
17567         
17568         var style = {
17569                 xtype: 'Button',
17570                 size : 'sm',
17571                 xns: Roo.bootstrap,
17572                 glyphicon : 'font',
17573                 //html : 'submit'
17574                 menu : {
17575                     xtype: 'Menu',
17576                     xns: Roo.bootstrap,
17577                     items:  []
17578                 }
17579         };
17580         Roo.each(this.formats, function(f) {
17581             style.menu.items.push({
17582                 xtype :'MenuItem',
17583                 xns: Roo.bootstrap,
17584                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
17585                 tagname : f,
17586                 listeners : {
17587                     click : function()
17588                     {
17589                         editorcore.insertTag(this.tagname);
17590                         editor.focus();
17591                     }
17592                 }
17593                 
17594             });
17595         });
17596          children.push(style);   
17597             
17598             
17599         btn('bold',false,true);
17600         btn('italic',false,true);
17601         btn('align-left', 'justifyleft',true);
17602         btn('align-center', 'justifycenter',true);
17603         btn('align-right' , 'justifyright',true);
17604         btn('link', false, false, function(btn) {
17605             //Roo.log("create link?");
17606             var url = prompt(this.createLinkText, this.defaultLinkValue);
17607             if(url && url != 'http:/'+'/'){
17608                 this.editorcore.relayCmd('createlink', url);
17609             }
17610         }),
17611         btn('list','insertunorderedlist',true);
17612         btn('pencil', false,true, function(btn){
17613                 Roo.log(this);
17614                 
17615                 this.toggleSourceEdit(btn.pressed);
17616         });
17617         /*
17618         var cog = {
17619                 xtype: 'Button',
17620                 size : 'sm',
17621                 xns: Roo.bootstrap,
17622                 glyphicon : 'cog',
17623                 //html : 'submit'
17624                 menu : {
17625                     xtype: 'Menu',
17626                     xns: Roo.bootstrap,
17627                     items:  []
17628                 }
17629         };
17630         
17631         cog.menu.items.push({
17632             xtype :'MenuItem',
17633             xns: Roo.bootstrap,
17634             html : Clean styles,
17635             tagname : f,
17636             listeners : {
17637                 click : function()
17638                 {
17639                     editorcore.insertTag(this.tagname);
17640                     editor.focus();
17641                 }
17642             }
17643             
17644         });
17645        */
17646         
17647          
17648        this.xtype = 'NavSimplebar';
17649         
17650         for(var i=0;i< children.length;i++) {
17651             
17652             this.buttons.add(this.addxtypeChild(children[i]));
17653             
17654         }
17655         
17656         editor.on('editorevent', this.updateToolbar, this);
17657     },
17658     onBtnClick : function(id)
17659     {
17660        this.editorcore.relayCmd(id);
17661        this.editorcore.focus();
17662     },
17663     
17664     /**
17665      * Protected method that will not generally be called directly. It triggers
17666      * a toolbar update by reading the markup state of the current selection in the editor.
17667      */
17668     updateToolbar: function(){
17669
17670         if(!this.editorcore.activated){
17671             this.editor.onFirstFocus(); // is this neeed?
17672             return;
17673         }
17674
17675         var btns = this.buttons; 
17676         var doc = this.editorcore.doc;
17677         btns.get('bold').setActive(doc.queryCommandState('bold'));
17678         btns.get('italic').setActive(doc.queryCommandState('italic'));
17679         //btns.get('underline').setActive(doc.queryCommandState('underline'));
17680         
17681         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
17682         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
17683         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
17684         
17685         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
17686         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
17687          /*
17688         
17689         var ans = this.editorcore.getAllAncestors();
17690         if (this.formatCombo) {
17691             
17692             
17693             var store = this.formatCombo.store;
17694             this.formatCombo.setValue("");
17695             for (var i =0; i < ans.length;i++) {
17696                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
17697                     // select it..
17698                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
17699                     break;
17700                 }
17701             }
17702         }
17703         
17704         
17705         
17706         // hides menus... - so this cant be on a menu...
17707         Roo.bootstrap.MenuMgr.hideAll();
17708         */
17709         Roo.bootstrap.MenuMgr.hideAll();
17710         //this.editorsyncValue();
17711     },
17712     onFirstFocus: function() {
17713         this.buttons.each(function(item){
17714            item.enable();
17715         });
17716     },
17717     toggleSourceEdit : function(sourceEditMode){
17718         
17719           
17720         if(sourceEditMode){
17721             Roo.log("disabling buttons");
17722            this.buttons.each( function(item){
17723                 if(item.cmd != 'pencil'){
17724                     item.disable();
17725                 }
17726             });
17727           
17728         }else{
17729             Roo.log("enabling buttons");
17730             if(this.editorcore.initialized){
17731                 this.buttons.each( function(item){
17732                     item.enable();
17733                 });
17734             }
17735             
17736         }
17737         Roo.log("calling toggole on editor");
17738         // tell the editor that it's been pressed..
17739         this.editor.toggleSourceEdit(sourceEditMode);
17740        
17741     }
17742 });
17743
17744
17745
17746
17747
17748 /**
17749  * @class Roo.bootstrap.Table.AbstractSelectionModel
17750  * @extends Roo.util.Observable
17751  * Abstract base class for grid SelectionModels.  It provides the interface that should be
17752  * implemented by descendant classes.  This class should not be directly instantiated.
17753  * @constructor
17754  */
17755 Roo.bootstrap.Table.AbstractSelectionModel = function(){
17756     this.locked = false;
17757     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
17758 };
17759
17760
17761 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
17762     /** @ignore Called by the grid automatically. Do not call directly. */
17763     init : function(grid){
17764         this.grid = grid;
17765         this.initEvents();
17766     },
17767
17768     /**
17769      * Locks the selections.
17770      */
17771     lock : function(){
17772         this.locked = true;
17773     },
17774
17775     /**
17776      * Unlocks the selections.
17777      */
17778     unlock : function(){
17779         this.locked = false;
17780     },
17781
17782     /**
17783      * Returns true if the selections are locked.
17784      * @return {Boolean}
17785      */
17786     isLocked : function(){
17787         return this.locked;
17788     }
17789 });
17790 /**
17791  * @extends Roo.bootstrap.Table.AbstractSelectionModel
17792  * @class Roo.bootstrap.Table.RowSelectionModel
17793  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
17794  * It supports multiple selections and keyboard selection/navigation. 
17795  * @constructor
17796  * @param {Object} config
17797  */
17798
17799 Roo.bootstrap.Table.RowSelectionModel = function(config){
17800     Roo.apply(this, config);
17801     this.selections = new Roo.util.MixedCollection(false, function(o){
17802         return o.id;
17803     });
17804
17805     this.last = false;
17806     this.lastActive = false;
17807
17808     this.addEvents({
17809         /**
17810              * @event selectionchange
17811              * Fires when the selection changes
17812              * @param {SelectionModel} this
17813              */
17814             "selectionchange" : true,
17815         /**
17816              * @event afterselectionchange
17817              * Fires after the selection changes (eg. by key press or clicking)
17818              * @param {SelectionModel} this
17819              */
17820             "afterselectionchange" : true,
17821         /**
17822              * @event beforerowselect
17823              * Fires when a row is selected being selected, return false to cancel.
17824              * @param {SelectionModel} this
17825              * @param {Number} rowIndex The selected index
17826              * @param {Boolean} keepExisting False if other selections will be cleared
17827              */
17828             "beforerowselect" : true,
17829         /**
17830              * @event rowselect
17831              * Fires when a row is selected.
17832              * @param {SelectionModel} this
17833              * @param {Number} rowIndex The selected index
17834              * @param {Roo.data.Record} r The record
17835              */
17836             "rowselect" : true,
17837         /**
17838              * @event rowdeselect
17839              * Fires when a row is deselected.
17840              * @param {SelectionModel} this
17841              * @param {Number} rowIndex The selected index
17842              */
17843         "rowdeselect" : true
17844     });
17845     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
17846     this.locked = false;
17847 };
17848
17849 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
17850     /**
17851      * @cfg {Boolean} singleSelect
17852      * True to allow selection of only one row at a time (defaults to false)
17853      */
17854     singleSelect : false,
17855
17856     // private
17857     initEvents : function(){
17858
17859         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
17860             this.grid.on("mousedown", this.handleMouseDown, this);
17861         }else{ // allow click to work like normal
17862             this.grid.on("rowclick", this.handleDragableRowClick, this);
17863         }
17864
17865         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
17866             "up" : function(e){
17867                 if(!e.shiftKey){
17868                     this.selectPrevious(e.shiftKey);
17869                 }else if(this.last !== false && this.lastActive !== false){
17870                     var last = this.last;
17871                     this.selectRange(this.last,  this.lastActive-1);
17872                     this.grid.getView().focusRow(this.lastActive);
17873                     if(last !== false){
17874                         this.last = last;
17875                     }
17876                 }else{
17877                     this.selectFirstRow();
17878                 }
17879                 this.fireEvent("afterselectionchange", this);
17880             },
17881             "down" : function(e){
17882                 if(!e.shiftKey){
17883                     this.selectNext(e.shiftKey);
17884                 }else if(this.last !== false && this.lastActive !== false){
17885                     var last = this.last;
17886                     this.selectRange(this.last,  this.lastActive+1);
17887                     this.grid.getView().focusRow(this.lastActive);
17888                     if(last !== false){
17889                         this.last = last;
17890                     }
17891                 }else{
17892                     this.selectFirstRow();
17893                 }
17894                 this.fireEvent("afterselectionchange", this);
17895             },
17896             scope: this
17897         });
17898
17899         var view = this.grid.view;
17900         view.on("refresh", this.onRefresh, this);
17901         view.on("rowupdated", this.onRowUpdated, this);
17902         view.on("rowremoved", this.onRemove, this);
17903     },
17904
17905     // private
17906     onRefresh : function(){
17907         var ds = this.grid.dataSource, i, v = this.grid.view;
17908         var s = this.selections;
17909         s.each(function(r){
17910             if((i = ds.indexOfId(r.id)) != -1){
17911                 v.onRowSelect(i);
17912             }else{
17913                 s.remove(r);
17914             }
17915         });
17916     },
17917
17918     // private
17919     onRemove : function(v, index, r){
17920         this.selections.remove(r);
17921     },
17922
17923     // private
17924     onRowUpdated : function(v, index, r){
17925         if(this.isSelected(r)){
17926             v.onRowSelect(index);
17927         }
17928     },
17929
17930     /**
17931      * Select records.
17932      * @param {Array} records The records to select
17933      * @param {Boolean} keepExisting (optional) True to keep existing selections
17934      */
17935     selectRecords : function(records, keepExisting){
17936         if(!keepExisting){
17937             this.clearSelections();
17938         }
17939         var ds = this.grid.dataSource;
17940         for(var i = 0, len = records.length; i < len; i++){
17941             this.selectRow(ds.indexOf(records[i]), true);
17942         }
17943     },
17944
17945     /**
17946      * Gets the number of selected rows.
17947      * @return {Number}
17948      */
17949     getCount : function(){
17950         return this.selections.length;
17951     },
17952
17953     /**
17954      * Selects the first row in the grid.
17955      */
17956     selectFirstRow : function(){
17957         this.selectRow(0);
17958     },
17959
17960     /**
17961      * Select the last row.
17962      * @param {Boolean} keepExisting (optional) True to keep existing selections
17963      */
17964     selectLastRow : function(keepExisting){
17965         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
17966     },
17967
17968     /**
17969      * Selects the row immediately following the last selected row.
17970      * @param {Boolean} keepExisting (optional) True to keep existing selections
17971      */
17972     selectNext : function(keepExisting){
17973         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
17974             this.selectRow(this.last+1, keepExisting);
17975             this.grid.getView().focusRow(this.last);
17976         }
17977     },
17978
17979     /**
17980      * Selects the row that precedes the last selected row.
17981      * @param {Boolean} keepExisting (optional) True to keep existing selections
17982      */
17983     selectPrevious : function(keepExisting){
17984         if(this.last){
17985             this.selectRow(this.last-1, keepExisting);
17986             this.grid.getView().focusRow(this.last);
17987         }
17988     },
17989
17990     /**
17991      * Returns the selected records
17992      * @return {Array} Array of selected records
17993      */
17994     getSelections : function(){
17995         return [].concat(this.selections.items);
17996     },
17997
17998     /**
17999      * Returns the first selected record.
18000      * @return {Record}
18001      */
18002     getSelected : function(){
18003         return this.selections.itemAt(0);
18004     },
18005
18006
18007     /**
18008      * Clears all selections.
18009      */
18010     clearSelections : function(fast){
18011         if(this.locked) return;
18012         if(fast !== true){
18013             var ds = this.grid.dataSource;
18014             var s = this.selections;
18015             s.each(function(r){
18016                 this.deselectRow(ds.indexOfId(r.id));
18017             }, this);
18018             s.clear();
18019         }else{
18020             this.selections.clear();
18021         }
18022         this.last = false;
18023     },
18024
18025
18026     /**
18027      * Selects all rows.
18028      */
18029     selectAll : function(){
18030         if(this.locked) return;
18031         this.selections.clear();
18032         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
18033             this.selectRow(i, true);
18034         }
18035     },
18036
18037     /**
18038      * Returns True if there is a selection.
18039      * @return {Boolean}
18040      */
18041     hasSelection : function(){
18042         return this.selections.length > 0;
18043     },
18044
18045     /**
18046      * Returns True if the specified row is selected.
18047      * @param {Number/Record} record The record or index of the record to check
18048      * @return {Boolean}
18049      */
18050     isSelected : function(index){
18051         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
18052         return (r && this.selections.key(r.id) ? true : false);
18053     },
18054
18055     /**
18056      * Returns True if the specified record id is selected.
18057      * @param {String} id The id of record to check
18058      * @return {Boolean}
18059      */
18060     isIdSelected : function(id){
18061         return (this.selections.key(id) ? true : false);
18062     },
18063
18064     // private
18065     handleMouseDown : function(e, t){
18066         var view = this.grid.getView(), rowIndex;
18067         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
18068             return;
18069         };
18070         if(e.shiftKey && this.last !== false){
18071             var last = this.last;
18072             this.selectRange(last, rowIndex, e.ctrlKey);
18073             this.last = last; // reset the last
18074             view.focusRow(rowIndex);
18075         }else{
18076             var isSelected = this.isSelected(rowIndex);
18077             if(e.button !== 0 && isSelected){
18078                 view.focusRow(rowIndex);
18079             }else if(e.ctrlKey && isSelected){
18080                 this.deselectRow(rowIndex);
18081             }else if(!isSelected){
18082                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
18083                 view.focusRow(rowIndex);
18084             }
18085         }
18086         this.fireEvent("afterselectionchange", this);
18087     },
18088     // private
18089     handleDragableRowClick :  function(grid, rowIndex, e) 
18090     {
18091         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
18092             this.selectRow(rowIndex, false);
18093             grid.view.focusRow(rowIndex);
18094              this.fireEvent("afterselectionchange", this);
18095         }
18096     },
18097     
18098     /**
18099      * Selects multiple rows.
18100      * @param {Array} rows Array of the indexes of the row to select
18101      * @param {Boolean} keepExisting (optional) True to keep existing selections
18102      */
18103     selectRows : function(rows, keepExisting){
18104         if(!keepExisting){
18105             this.clearSelections();
18106         }
18107         for(var i = 0, len = rows.length; i < len; i++){
18108             this.selectRow(rows[i], true);
18109         }
18110     },
18111
18112     /**
18113      * Selects a range of rows. All rows in between startRow and endRow are also selected.
18114      * @param {Number} startRow The index of the first row in the range
18115      * @param {Number} endRow The index of the last row in the range
18116      * @param {Boolean} keepExisting (optional) True to retain existing selections
18117      */
18118     selectRange : function(startRow, endRow, keepExisting){
18119         if(this.locked) return;
18120         if(!keepExisting){
18121             this.clearSelections();
18122         }
18123         if(startRow <= endRow){
18124             for(var i = startRow; i <= endRow; i++){
18125                 this.selectRow(i, true);
18126             }
18127         }else{
18128             for(var i = startRow; i >= endRow; i--){
18129                 this.selectRow(i, true);
18130             }
18131         }
18132     },
18133
18134     /**
18135      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
18136      * @param {Number} startRow The index of the first row in the range
18137      * @param {Number} endRow The index of the last row in the range
18138      */
18139     deselectRange : function(startRow, endRow, preventViewNotify){
18140         if(this.locked) return;
18141         for(var i = startRow; i <= endRow; i++){
18142             this.deselectRow(i, preventViewNotify);
18143         }
18144     },
18145
18146     /**
18147      * Selects a row.
18148      * @param {Number} row The index of the row to select
18149      * @param {Boolean} keepExisting (optional) True to keep existing selections
18150      */
18151     selectRow : function(index, keepExisting, preventViewNotify){
18152         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
18153         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
18154             if(!keepExisting || this.singleSelect){
18155                 this.clearSelections();
18156             }
18157             var r = this.grid.dataSource.getAt(index);
18158             this.selections.add(r);
18159             this.last = this.lastActive = index;
18160             if(!preventViewNotify){
18161                 this.grid.getView().onRowSelect(index);
18162             }
18163             this.fireEvent("rowselect", this, index, r);
18164             this.fireEvent("selectionchange", this);
18165         }
18166     },
18167
18168     /**
18169      * Deselects a row.
18170      * @param {Number} row The index of the row to deselect
18171      */
18172     deselectRow : function(index, preventViewNotify){
18173         if(this.locked) return;
18174         if(this.last == index){
18175             this.last = false;
18176         }
18177         if(this.lastActive == index){
18178             this.lastActive = false;
18179         }
18180         var r = this.grid.dataSource.getAt(index);
18181         this.selections.remove(r);
18182         if(!preventViewNotify){
18183             this.grid.getView().onRowDeselect(index);
18184         }
18185         this.fireEvent("rowdeselect", this, index);
18186         this.fireEvent("selectionchange", this);
18187     },
18188
18189     // private
18190     restoreLast : function(){
18191         if(this._last){
18192             this.last = this._last;
18193         }
18194     },
18195
18196     // private
18197     acceptsNav : function(row, col, cm){
18198         return !cm.isHidden(col) && cm.isCellEditable(col, row);
18199     },
18200
18201     // private
18202     onEditorKey : function(field, e){
18203         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
18204         if(k == e.TAB){
18205             e.stopEvent();
18206             ed.completeEdit();
18207             if(e.shiftKey){
18208                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
18209             }else{
18210                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
18211             }
18212         }else if(k == e.ENTER && !e.ctrlKey){
18213             e.stopEvent();
18214             ed.completeEdit();
18215             if(e.shiftKey){
18216                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
18217             }else{
18218                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
18219             }
18220         }else if(k == e.ESC){
18221             ed.cancelEdit();
18222         }
18223         if(newCell){
18224             g.startEditing(newCell[0], newCell[1]);
18225         }
18226     }
18227 });/*
18228  * Based on:
18229  * Ext JS Library 1.1.1
18230  * Copyright(c) 2006-2007, Ext JS, LLC.
18231  *
18232  * Originally Released Under LGPL - original licence link has changed is not relivant.
18233  *
18234  * Fork - LGPL
18235  * <script type="text/javascript">
18236  */
18237  
18238 /**
18239  * @class Roo.bootstrap.PagingToolbar
18240  * @extends Roo.Row
18241  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
18242  * @constructor
18243  * Create a new PagingToolbar
18244  * @param {Object} config The config object
18245  */
18246 Roo.bootstrap.PagingToolbar = function(config)
18247 {
18248     // old args format still supported... - xtype is prefered..
18249         // created from xtype...
18250     var ds = config.dataSource;
18251     this.toolbarItems = [];
18252     if (config.items) {
18253         this.toolbarItems = config.items;
18254 //        config.items = [];
18255     }
18256     
18257     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
18258     this.ds = ds;
18259     this.cursor = 0;
18260     if (ds) { 
18261         this.bind(ds);
18262     }
18263     
18264     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
18265     
18266 };
18267
18268 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
18269     /**
18270      * @cfg {Roo.data.Store} dataSource
18271      * The underlying data store providing the paged data
18272      */
18273     /**
18274      * @cfg {String/HTMLElement/Element} container
18275      * container The id or element that will contain the toolbar
18276      */
18277     /**
18278      * @cfg {Boolean} displayInfo
18279      * True to display the displayMsg (defaults to false)
18280      */
18281     /**
18282      * @cfg {Number} pageSize
18283      * The number of records to display per page (defaults to 20)
18284      */
18285     pageSize: 20,
18286     /**
18287      * @cfg {String} displayMsg
18288      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
18289      */
18290     displayMsg : 'Displaying {0} - {1} of {2}',
18291     /**
18292      * @cfg {String} emptyMsg
18293      * The message to display when no records are found (defaults to "No data to display")
18294      */
18295     emptyMsg : 'No data to display',
18296     /**
18297      * Customizable piece of the default paging text (defaults to "Page")
18298      * @type String
18299      */
18300     beforePageText : "Page",
18301     /**
18302      * Customizable piece of the default paging text (defaults to "of %0")
18303      * @type String
18304      */
18305     afterPageText : "of {0}",
18306     /**
18307      * Customizable piece of the default paging text (defaults to "First Page")
18308      * @type String
18309      */
18310     firstText : "First Page",
18311     /**
18312      * Customizable piece of the default paging text (defaults to "Previous Page")
18313      * @type String
18314      */
18315     prevText : "Previous Page",
18316     /**
18317      * Customizable piece of the default paging text (defaults to "Next Page")
18318      * @type String
18319      */
18320     nextText : "Next Page",
18321     /**
18322      * Customizable piece of the default paging text (defaults to "Last Page")
18323      * @type String
18324      */
18325     lastText : "Last Page",
18326     /**
18327      * Customizable piece of the default paging text (defaults to "Refresh")
18328      * @type String
18329      */
18330     refreshText : "Refresh",
18331
18332     buttons : false,
18333     // private
18334     onRender : function(ct, position) 
18335     {
18336         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
18337         this.navgroup.parentId = this.id;
18338         this.navgroup.onRender(this.el, null);
18339         // add the buttons to the navgroup
18340         
18341         if(this.displayInfo){
18342             Roo.log(this.el.select('ul.navbar-nav',true).first());
18343             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
18344             this.displayEl = this.el.select('.x-paging-info', true).first();
18345 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
18346 //            this.displayEl = navel.el.select('span',true).first();
18347         }
18348         
18349         var _this = this;
18350         
18351         if(this.buttons){
18352             Roo.each(_this.buttons, function(e){
18353                Roo.factory(e).onRender(_this.el, null);
18354             });
18355         }
18356             
18357         Roo.each(_this.toolbarItems, function(e) {
18358             _this.navgroup.addItem(e);
18359         });
18360         
18361         this.first = this.navgroup.addItem({
18362             tooltip: this.firstText,
18363             cls: "prev",
18364             icon : 'fa fa-backward',
18365             disabled: true,
18366             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
18367         });
18368         
18369         this.prev =  this.navgroup.addItem({
18370             tooltip: this.prevText,
18371             cls: "prev",
18372             icon : 'fa fa-step-backward',
18373             disabled: true,
18374             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
18375         });
18376     //this.addSeparator();
18377         
18378         
18379         var field = this.navgroup.addItem( {
18380             tagtype : 'span',
18381             cls : 'x-paging-position',
18382             
18383             html : this.beforePageText  +
18384                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
18385                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
18386          } ); //?? escaped?
18387         
18388         this.field = field.el.select('input', true).first();
18389         this.field.on("keydown", this.onPagingKeydown, this);
18390         this.field.on("focus", function(){this.dom.select();});
18391     
18392     
18393         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
18394         //this.field.setHeight(18);
18395         //this.addSeparator();
18396         this.next = this.navgroup.addItem({
18397             tooltip: this.nextText,
18398             cls: "next",
18399             html : ' <i class="fa fa-step-forward">',
18400             disabled: true,
18401             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
18402         });
18403         this.last = this.navgroup.addItem({
18404             tooltip: this.lastText,
18405             icon : 'fa fa-forward',
18406             cls: "next",
18407             disabled: true,
18408             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
18409         });
18410     //this.addSeparator();
18411         this.loading = this.navgroup.addItem({
18412             tooltip: this.refreshText,
18413             icon: 'fa fa-refresh',
18414             
18415             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
18416         });
18417
18418     },
18419
18420     // private
18421     updateInfo : function(){
18422         if(this.displayEl){
18423             var count = this.ds.getCount();
18424             var msg = count == 0 ?
18425                 this.emptyMsg :
18426                 String.format(
18427                     this.displayMsg,
18428                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
18429                 );
18430             this.displayEl.update(msg);
18431         }
18432     },
18433
18434     // private
18435     onLoad : function(ds, r, o){
18436        this.cursor = o.params ? o.params.start : 0;
18437        var d = this.getPageData(),
18438             ap = d.activePage,
18439             ps = d.pages;
18440         
18441        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
18442        this.field.dom.value = ap;
18443        this.first.setDisabled(ap == 1);
18444        this.prev.setDisabled(ap == 1);
18445        this.next.setDisabled(ap == ps);
18446        this.last.setDisabled(ap == ps);
18447        this.loading.enable();
18448        this.updateInfo();
18449     },
18450
18451     // private
18452     getPageData : function(){
18453         var total = this.ds.getTotalCount();
18454         return {
18455             total : total,
18456             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
18457             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
18458         };
18459     },
18460
18461     // private
18462     onLoadError : function(){
18463         this.loading.enable();
18464     },
18465
18466     // private
18467     onPagingKeydown : function(e){
18468         var k = e.getKey();
18469         var d = this.getPageData();
18470         if(k == e.RETURN){
18471             var v = this.field.dom.value, pageNum;
18472             if(!v || isNaN(pageNum = parseInt(v, 10))){
18473                 this.field.dom.value = d.activePage;
18474                 return;
18475             }
18476             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
18477             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
18478             e.stopEvent();
18479         }
18480         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))
18481         {
18482           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
18483           this.field.dom.value = pageNum;
18484           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
18485           e.stopEvent();
18486         }
18487         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
18488         {
18489           var v = this.field.dom.value, pageNum; 
18490           var increment = (e.shiftKey) ? 10 : 1;
18491           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
18492             increment *= -1;
18493           if(!v || isNaN(pageNum = parseInt(v, 10))) {
18494             this.field.dom.value = d.activePage;
18495             return;
18496           }
18497           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
18498           {
18499             this.field.dom.value = parseInt(v, 10) + increment;
18500             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
18501             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
18502           }
18503           e.stopEvent();
18504         }
18505     },
18506
18507     // private
18508     beforeLoad : function(){
18509         if(this.loading){
18510             this.loading.disable();
18511         }
18512     },
18513
18514     // private
18515     onClick : function(which){
18516         var ds = this.ds;
18517         if (!ds) {
18518             return;
18519         }
18520         switch(which){
18521             case "first":
18522                 ds.load({params:{start: 0, limit: this.pageSize}});
18523             break;
18524             case "prev":
18525                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
18526             break;
18527             case "next":
18528                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
18529             break;
18530             case "last":
18531                 var total = ds.getTotalCount();
18532                 var extra = total % this.pageSize;
18533                 var lastStart = extra ? (total - extra) : total-this.pageSize;
18534                 ds.load({params:{start: lastStart, limit: this.pageSize}});
18535             break;
18536             case "refresh":
18537                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
18538             break;
18539         }
18540     },
18541
18542     /**
18543      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
18544      * @param {Roo.data.Store} store The data store to unbind
18545      */
18546     unbind : function(ds){
18547         ds.un("beforeload", this.beforeLoad, this);
18548         ds.un("load", this.onLoad, this);
18549         ds.un("loadexception", this.onLoadError, this);
18550         ds.un("remove", this.updateInfo, this);
18551         ds.un("add", this.updateInfo, this);
18552         this.ds = undefined;
18553     },
18554
18555     /**
18556      * Binds the paging toolbar to the specified {@link Roo.data.Store}
18557      * @param {Roo.data.Store} store The data store to bind
18558      */
18559     bind : function(ds){
18560         ds.on("beforeload", this.beforeLoad, this);
18561         ds.on("load", this.onLoad, this);
18562         ds.on("loadexception", this.onLoadError, this);
18563         ds.on("remove", this.updateInfo, this);
18564         ds.on("add", this.updateInfo, this);
18565         this.ds = ds;
18566     }
18567 });/*
18568  * - LGPL
18569  *
18570  * element
18571  * 
18572  */
18573
18574 /**
18575  * @class Roo.bootstrap.MessageBar
18576  * @extends Roo.bootstrap.Component
18577  * Bootstrap MessageBar class
18578  * @cfg {String} html contents of the MessageBar
18579  * @cfg {String} weight (info | success | warning | danger) default info
18580  * @cfg {String} beforeClass insert the bar before the given class
18581  * @cfg {Boolean} closable (true | false) default false
18582  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
18583  * 
18584  * @constructor
18585  * Create a new Element
18586  * @param {Object} config The config object
18587  */
18588
18589 Roo.bootstrap.MessageBar = function(config){
18590     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
18591 };
18592
18593 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
18594     
18595     html: '',
18596     weight: 'info',
18597     closable: false,
18598     fixed: false,
18599     beforeClass: 'bootstrap-sticky-wrap',
18600     
18601     getAutoCreate : function(){
18602         
18603         var cfg = {
18604             tag: 'div',
18605             cls: 'alert alert-dismissable alert-' + this.weight,
18606             cn: [
18607                 {
18608                     tag: 'span',
18609                     cls: 'message',
18610                     html: this.html || ''
18611                 }
18612             ]
18613         }
18614         
18615         if(this.fixed){
18616             cfg.cls += ' alert-messages-fixed';
18617         }
18618         
18619         if(this.closable){
18620             cfg.cn.push({
18621                 tag: 'button',
18622                 cls: 'close',
18623                 html: 'x'
18624             });
18625         }
18626         
18627         return cfg;
18628     },
18629     
18630     onRender : function(ct, position)
18631     {
18632         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
18633         
18634         if(!this.el){
18635             var cfg = Roo.apply({},  this.getAutoCreate());
18636             cfg.id = Roo.id();
18637             
18638             if (this.cls) {
18639                 cfg.cls += ' ' + this.cls;
18640             }
18641             if (this.style) {
18642                 cfg.style = this.style;
18643             }
18644             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
18645             
18646             this.el.setVisibilityMode(Roo.Element.DISPLAY);
18647         }
18648         
18649         this.el.select('>button.close').on('click', this.hide, this);
18650         
18651     },
18652     
18653     show : function()
18654     {
18655         if (!this.rendered) {
18656             this.render();
18657         }
18658         
18659         this.el.show();
18660         
18661         this.fireEvent('show', this);
18662         
18663     },
18664     
18665     hide : function()
18666     {
18667         if (!this.rendered) {
18668             this.render();
18669         }
18670         
18671         this.el.hide();
18672         
18673         this.fireEvent('hide', this);
18674     },
18675     
18676     update : function()
18677     {
18678 //        var e = this.el.dom.firstChild;
18679 //        
18680 //        if(this.closable){
18681 //            e = e.nextSibling;
18682 //        }
18683 //        
18684 //        e.data = this.html || '';
18685
18686         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
18687     }
18688    
18689 });
18690
18691  
18692
18693      /*
18694  * - LGPL
18695  *
18696  * Graph
18697  * 
18698  */
18699
18700
18701 /**
18702  * @class Roo.bootstrap.Graph
18703  * @extends Roo.bootstrap.Component
18704  * Bootstrap Graph class
18705 > Prameters
18706  -sm {number} sm 4
18707  -md {number} md 5
18708  @cfg {String} graphtype  bar | vbar | pie
18709  @cfg {number} g_x coodinator | centre x (pie)
18710  @cfg {number} g_y coodinator | centre y (pie)
18711  @cfg {number} g_r radius (pie)
18712  @cfg {number} g_height height of the chart (respected by all elements in the set)
18713  @cfg {number} g_width width of the chart (respected by all elements in the set)
18714  @cfg {Object} title The title of the chart
18715     
18716  -{Array}  values
18717  -opts (object) options for the chart 
18718      o {
18719      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
18720      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
18721      o vgutter (number)
18722      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.
18723      o stacked (boolean) whether or not to tread values as in a stacked bar chart
18724      o to
18725      o stretch (boolean)
18726      o }
18727  -opts (object) options for the pie
18728      o{
18729      o cut
18730      o startAngle (number)
18731      o endAngle (number)
18732      } 
18733  *
18734  * @constructor
18735  * Create a new Input
18736  * @param {Object} config The config object
18737  */
18738
18739 Roo.bootstrap.Graph = function(config){
18740     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
18741     
18742     this.addEvents({
18743         // img events
18744         /**
18745          * @event click
18746          * The img click event for the img.
18747          * @param {Roo.EventObject} e
18748          */
18749         "click" : true
18750     });
18751 };
18752
18753 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
18754     
18755     sm: 4,
18756     md: 5,
18757     graphtype: 'bar',
18758     g_height: 250,
18759     g_width: 400,
18760     g_x: 50,
18761     g_y: 50,
18762     g_r: 30,
18763     opts:{
18764         //g_colors: this.colors,
18765         g_type: 'soft',
18766         g_gutter: '20%'
18767
18768     },
18769     title : false,
18770
18771     getAutoCreate : function(){
18772         
18773         var cfg = {
18774             tag: 'div',
18775             html : null
18776         }
18777         
18778         
18779         return  cfg;
18780     },
18781
18782     onRender : function(ct,position){
18783         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
18784         this.raphael = Raphael(this.el.dom);
18785         
18786                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
18787                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
18788                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
18789                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
18790                 /*
18791                 r.text(160, 10, "Single Series Chart").attr(txtattr);
18792                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
18793                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
18794                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
18795                 
18796                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
18797                 r.barchart(330, 10, 300, 220, data1);
18798                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
18799                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
18800                 */
18801                 
18802                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
18803                 // r.barchart(30, 30, 560, 250,  xdata, {
18804                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
18805                 //     axis : "0 0 1 1",
18806                 //     axisxlabels :  xdata
18807                 //     //yvalues : cols,
18808                    
18809                 // });
18810 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
18811 //        
18812 //        this.load(null,xdata,{
18813 //                axis : "0 0 1 1",
18814 //                axisxlabels :  xdata
18815 //                });
18816
18817     },
18818
18819     load : function(graphtype,xdata,opts){
18820         this.raphael.clear();
18821         if(!graphtype) {
18822             graphtype = this.graphtype;
18823         }
18824         if(!opts){
18825             opts = this.opts;
18826         }
18827         var r = this.raphael,
18828             fin = function () {
18829                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
18830             },
18831             fout = function () {
18832                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
18833             },
18834             pfin = function() {
18835                 this.sector.stop();
18836                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
18837
18838                 if (this.label) {
18839                     this.label[0].stop();
18840                     this.label[0].attr({ r: 7.5 });
18841                     this.label[1].attr({ "font-weight": 800 });
18842                 }
18843             },
18844             pfout = function() {
18845                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
18846
18847                 if (this.label) {
18848                     this.label[0].animate({ r: 5 }, 500, "bounce");
18849                     this.label[1].attr({ "font-weight": 400 });
18850                 }
18851             };
18852
18853         switch(graphtype){
18854             case 'bar':
18855                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
18856                 break;
18857             case 'hbar':
18858                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
18859                 break;
18860             case 'pie':
18861 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
18862 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
18863 //            
18864                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
18865                 
18866                 break;
18867
18868         }
18869         
18870         if(this.title){
18871             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
18872         }
18873         
18874     },
18875     
18876     setTitle: function(o)
18877     {
18878         this.title = o;
18879     },
18880     
18881     initEvents: function() {
18882         
18883         if(!this.href){
18884             this.el.on('click', this.onClick, this);
18885         }
18886     },
18887     
18888     onClick : function(e)
18889     {
18890         Roo.log('img onclick');
18891         this.fireEvent('click', this, e);
18892     }
18893    
18894 });
18895
18896  
18897 /*
18898  * - LGPL
18899  *
18900  * numberBox
18901  * 
18902  */
18903 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
18904
18905 /**
18906  * @class Roo.bootstrap.dash.NumberBox
18907  * @extends Roo.bootstrap.Component
18908  * Bootstrap NumberBox class
18909  * @cfg {String} bgcolor Box background color, such as (aqua | green | yellow | red etc..) Default aqua
18910  * @cfg {String} headline Box headline
18911  * @cfg {String} content Box content
18912  * @cfg {String} icon Box icon
18913  * @cfg {String} footer Footer text
18914  * @cfg {String} fhref Footer href
18915  * 
18916  * @constructor
18917  * Create a new NumberBox
18918  * @param {Object} config The config object
18919  */
18920
18921
18922 Roo.bootstrap.dash.NumberBox = function(config){
18923     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
18924     
18925 };
18926
18927 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
18928     
18929     bgcolor : 'aqua',
18930     headline : '',
18931     content : '',
18932     icon : '',
18933     footer : '',
18934     fhref : '',
18935     ficon : '',
18936     
18937     getAutoCreate : function(){
18938         
18939         var cfg = {
18940             tag : 'div',
18941             cls : 'small-box bg-' + this.bgcolor,
18942             cn : [
18943                 {
18944                     tag : 'div',
18945                     cls : 'inner',
18946                     cn :[
18947                         {
18948                             tag : 'h3',
18949                             cls : 'roo-headline',
18950                             html : this.headline
18951                         },
18952                         {
18953                             tag : 'p',
18954                             cls : 'roo-content',
18955                             html : this.content
18956                         }
18957                     ]
18958                 }
18959             ]
18960         }
18961         
18962         if(this.icon){
18963             cfg.cn.push({
18964                 tag : 'div',
18965                 cls : 'icon',
18966                 cn :[
18967                     {
18968                         tag : 'i',
18969                         cls : 'ion ' + this.icon
18970                     }
18971                 ]
18972             });
18973         }
18974         
18975         if(this.footer){
18976             var footer = {
18977                 tag : 'a',
18978                 cls : 'small-box-footer',
18979                 href : this.fhref || '#',
18980                 html : this.footer
18981             };
18982             
18983             cfg.cn.push(footer);
18984             
18985         }
18986         
18987         return  cfg;
18988     },
18989
18990     onRender : function(ct,position){
18991         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
18992
18993
18994        
18995                 
18996     },
18997
18998     setHeadline: function (value)
18999     {
19000         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
19001     },
19002     
19003     setFooter: function (value, href)
19004     {
19005         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
19006         
19007         if(href){
19008             this.el.select('a.small-box-footer',true).first().attr('href', href);
19009         }
19010         
19011     },
19012
19013     setContent: function (value)
19014     {
19015         this.el.select('.roo-content',true).first().dom.innerHTML = value;
19016     },
19017
19018     initEvents: function() 
19019     {   
19020         
19021     }
19022     
19023 });
19024
19025  
19026 /*
19027  * - LGPL
19028  *
19029  * TabBox
19030  * 
19031  */
19032 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19033
19034 /**
19035  * @class Roo.bootstrap.dash.TabBox
19036  * @extends Roo.bootstrap.Component
19037  * Bootstrap TabBox class
19038  * @cfg {String} title Title of the TabBox
19039  * @cfg {String} icon Icon of the TabBox
19040  * 
19041  * @constructor
19042  * Create a new TabBox
19043  * @param {Object} config The config object
19044  */
19045
19046
19047 Roo.bootstrap.dash.TabBox = function(config){
19048     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
19049     
19050 };
19051
19052 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
19053
19054     title : '',
19055     icon : false,
19056     
19057     getChildContainer : function()
19058     {
19059         return this.el.select('.tab-content', true).first();
19060     },
19061     
19062     getAutoCreate : function(){
19063         
19064         var header = {
19065             tag: 'li',
19066             cls: 'pull-left header',
19067             html: this.title,
19068             cn : []
19069         };
19070         
19071         if(this.icon){
19072             header.cn.push({
19073                 tag: 'i',
19074                 cls: 'fa ' + this.icon
19075             });
19076         }
19077         
19078         
19079         var cfg = {
19080             tag: 'div',
19081             cls: 'nav-tabs-custom',
19082             cn: [
19083                 {
19084                     tag: 'ul',
19085                     cls: 'nav nav-tabs pull-right',
19086                     cn: [
19087                         header
19088                     ]
19089                 },
19090                 {
19091                     tag: 'div',
19092                     cls: 'tab-content no-padding',
19093                     cn: []
19094                 }
19095             ]
19096         }
19097
19098         return  cfg;
19099     },
19100     
19101     setTitle : function(value)
19102     {
19103         this.el.select('.header', true).first().dom.innerHTML = value;
19104     }
19105     
19106 });
19107
19108  
19109 /*
19110  * - LGPL
19111  *
19112  * Tab pane
19113  * 
19114  */
19115 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19116 /**
19117  * @class Roo.bootstrap.TabPane
19118  * @extends Roo.bootstrap.Component
19119  * Bootstrap TabPane class
19120  * @cfg {Boolean} active (false | true) Default false
19121
19122  * 
19123  * @constructor
19124  * Create a new TabPane
19125  * @param {Object} config The config object
19126  */
19127
19128 Roo.bootstrap.dash.TabPane = function(config){
19129     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
19130     
19131 };
19132
19133 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
19134     
19135     active : false,
19136 //    
19137 //    getBox : function()
19138 //    {
19139 //        return this.el.findParent('.nav-tabs-custom', false, true);
19140 //    },
19141     
19142     getAutoCreate : function() 
19143     {
19144         var cfg = {
19145             tag: 'div',
19146             cls: 'tab-pane'
19147         }
19148         
19149         if(this.active){
19150             cfg.cls += ' active';
19151         }
19152         
19153         return cfg;
19154     }
19155     
19156     
19157 });
19158
19159  
19160
19161
19162  /*
19163  * - LGPL
19164  *
19165  * menu
19166  * 
19167  */
19168 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
19169
19170 /**
19171  * @class Roo.bootstrap.menu.Menu
19172  * @extends Roo.bootstrap.Component
19173  * Bootstrap Menu class - container for Menu
19174  * @cfg {String} html Text of the menu
19175  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
19176  * @cfg {String} icon Font awesome icon
19177  * @cfg {String} pos Menu align to (top | bottom) default bottom
19178  * 
19179  * 
19180  * @constructor
19181  * Create a new Menu
19182  * @param {Object} config The config object
19183  */
19184
19185
19186 Roo.bootstrap.menu.Menu = function(config){
19187     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
19188     
19189     this.addEvents({
19190         /**
19191          * @event beforeshow
19192          * Fires before this menu is displayed
19193          * @param {Roo.bootstrap.menu.Menu} this
19194          */
19195         beforeshow : true,
19196         /**
19197          * @event beforehide
19198          * Fires before this menu is hidden
19199          * @param {Roo.bootstrap.menu.Menu} this
19200          */
19201         beforehide : true,
19202         /**
19203          * @event show
19204          * Fires after this menu is displayed
19205          * @param {Roo.bootstrap.menu.Menu} this
19206          */
19207         show : true,
19208         /**
19209          * @event hide
19210          * Fires after this menu is hidden
19211          * @param {Roo.bootstrap.menu.Menu} this
19212          */
19213         hide : true,
19214         /**
19215          * @event click
19216          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19217          * @param {Roo.bootstrap.menu.Menu} this
19218          * @param {Roo.EventObject} e
19219          */
19220         click : true
19221     });
19222     
19223 };
19224
19225 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
19226     
19227     submenu : false,
19228     html : '',
19229     weight : 'default',
19230     icon : false,
19231     pos : 'bottom',
19232     
19233     
19234     getChildContainer : function() {
19235         if(this.isSubMenu){
19236             return this.el;
19237         }
19238         
19239         return this.el.select('ul.dropdown-menu', true).first();  
19240     },
19241     
19242     getAutoCreate : function()
19243     {
19244         var text = [
19245             {
19246                 tag : 'span',
19247                 cls : 'roo-menu-text',
19248                 html : this.html
19249             }
19250         ];
19251         
19252         if(this.icon){
19253             text.unshift({
19254                 tag : 'i',
19255                 cls : 'fa ' + this.icon
19256             })
19257         }
19258         
19259         
19260         var cfg = {
19261             tag : 'div',
19262             cls : 'btn-group',
19263             cn : [
19264                 {
19265                     tag : 'button',
19266                     cls : 'dropdown-button btn btn-' + this.weight,
19267                     cn : text
19268                 },
19269                 {
19270                     tag : 'button',
19271                     cls : 'dropdown-toggle btn btn-' + this.weight,
19272                     cn : [
19273                         {
19274                             tag : 'span',
19275                             cls : 'caret'
19276                         }
19277                     ]
19278                 },
19279                 {
19280                     tag : 'ul',
19281                     cls : 'dropdown-menu'
19282                 }
19283             ]
19284             
19285         };
19286         
19287         if(this.pos == 'top'){
19288             cfg.cls += ' dropup';
19289         }
19290         
19291         if(this.isSubMenu){
19292             cfg = {
19293                 tag : 'ul',
19294                 cls : 'dropdown-menu'
19295             }
19296         }
19297         
19298         return cfg;
19299     },
19300     
19301     onRender : function(ct, position)
19302     {
19303         this.isSubMenu = ct.hasClass('dropdown-submenu');
19304         
19305         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
19306     },
19307     
19308     initEvents : function() 
19309     {
19310         if(this.isSubMenu){
19311             return;
19312         }
19313         
19314         this.hidden = true;
19315         
19316         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
19317         this.triggerEl.on('click', this.onTriggerPress, this);
19318         
19319         this.buttonEl = this.el.select('button.dropdown-button', true).first();
19320         this.buttonEl.on('click', this.onClick, this);
19321         
19322     },
19323     
19324     list : function()
19325     {
19326         if(this.isSubMenu){
19327             return this.el;
19328         }
19329         
19330         return this.el.select('ul.dropdown-menu', true).first();
19331     },
19332     
19333     onClick : function(e)
19334     {
19335         this.fireEvent("click", this, e);
19336     },
19337     
19338     onTriggerPress  : function(e)
19339     {   
19340         if (this.isVisible()) {
19341             this.hide();
19342         } else {
19343             this.show();
19344         }
19345     },
19346     
19347     isVisible : function(){
19348         return !this.hidden;
19349     },
19350     
19351     show : function()
19352     {
19353         this.fireEvent("beforeshow", this);
19354         
19355         this.hidden = false;
19356         this.el.addClass('open');
19357         
19358         Roo.get(document).on("mouseup", this.onMouseUp, this);
19359         
19360         this.fireEvent("show", this);
19361         
19362         
19363     },
19364     
19365     hide : function()
19366     {
19367         this.fireEvent("beforehide", this);
19368         
19369         this.hidden = true;
19370         this.el.removeClass('open');
19371         
19372         Roo.get(document).un("mouseup", this.onMouseUp);
19373         
19374         this.fireEvent("hide", this);
19375     },
19376     
19377     onMouseUp : function()
19378     {
19379         this.hide();
19380     }
19381     
19382 });
19383
19384  
19385  /*
19386  * - LGPL
19387  *
19388  * menu item
19389  * 
19390  */
19391 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
19392
19393 /**
19394  * @class Roo.bootstrap.menu.Item
19395  * @extends Roo.bootstrap.Component
19396  * Bootstrap MenuItem class
19397  * @cfg {Boolean} submenu (true | false) default false
19398  * @cfg {String} html text of the item
19399  * @cfg {String} href the link
19400  * @cfg {Boolean} disable (true | false) default false
19401  * @cfg {Boolean} preventDefault (true | false) default true
19402  * @cfg {String} icon Font awesome icon
19403  * @cfg {String} pos Submenu align to (left | right) default right 
19404  * 
19405  * 
19406  * @constructor
19407  * Create a new Item
19408  * @param {Object} config The config object
19409  */
19410
19411
19412 Roo.bootstrap.menu.Item = function(config){
19413     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
19414     this.addEvents({
19415         /**
19416          * @event mouseover
19417          * Fires when the mouse is hovering over this menu
19418          * @param {Roo.bootstrap.menu.Item} this
19419          * @param {Roo.EventObject} e
19420          */
19421         mouseover : true,
19422         /**
19423          * @event mouseout
19424          * Fires when the mouse exits this menu
19425          * @param {Roo.bootstrap.menu.Item} this
19426          * @param {Roo.EventObject} e
19427          */
19428         mouseout : true,
19429         // raw events
19430         /**
19431          * @event click
19432          * The raw click event for the entire grid.
19433          * @param {Roo.EventObject} e
19434          */
19435         click : true
19436     });
19437 };
19438
19439 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
19440     
19441     submenu : false,
19442     href : '',
19443     html : '',
19444     preventDefault: true,
19445     disable : false,
19446     icon : false,
19447     pos : 'right',
19448     
19449     getAutoCreate : function()
19450     {
19451         var text = [
19452             {
19453                 tag : 'span',
19454                 cls : 'roo-menu-item-text',
19455                 html : this.html
19456             }
19457         ];
19458         
19459         if(this.icon){
19460             text.unshift({
19461                 tag : 'i',
19462                 cls : 'fa ' + this.icon
19463             })
19464         }
19465         
19466         var cfg = {
19467             tag : 'li',
19468             cn : [
19469                 {
19470                     tag : 'a',
19471                     href : this.href || '#',
19472                     cn : text
19473                 }
19474             ]
19475         };
19476         
19477         if(this.disable){
19478             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
19479         }
19480         
19481         if(this.submenu){
19482             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
19483             
19484             if(this.pos == 'left'){
19485                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
19486             }
19487         }
19488         
19489         return cfg;
19490     },
19491     
19492     initEvents : function() 
19493     {
19494         this.el.on('mouseover', this.onMouseOver, this);
19495         this.el.on('mouseout', this.onMouseOut, this);
19496         
19497         this.el.select('a', true).first().on('click', this.onClick, this);
19498         
19499     },
19500     
19501     onClick : function(e)
19502     {
19503         if(this.preventDefault){
19504             e.preventDefault();
19505         }
19506         
19507         this.fireEvent("click", this, e);
19508     },
19509     
19510     onMouseOver : function(e)
19511     {
19512         if(this.submenu && this.pos == 'left'){
19513             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
19514         }
19515         
19516         this.fireEvent("mouseover", this, e);
19517     },
19518     
19519     onMouseOut : function(e)
19520     {
19521         this.fireEvent("mouseout", this, e);
19522     }
19523 });
19524
19525  
19526
19527  /*
19528  * - LGPL
19529  *
19530  * menu separator
19531  * 
19532  */
19533 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
19534
19535 /**
19536  * @class Roo.bootstrap.menu.Separator
19537  * @extends Roo.bootstrap.Component
19538  * Bootstrap Separator class
19539  * 
19540  * @constructor
19541  * Create a new Separator
19542  * @param {Object} config The config object
19543  */
19544
19545
19546 Roo.bootstrap.menu.Separator = function(config){
19547     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
19548 };
19549
19550 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
19551     
19552     getAutoCreate : function(){
19553         var cfg = {
19554             tag : 'li',
19555             cls: 'divider'
19556         };
19557         
19558         return cfg;
19559     }
19560    
19561 });
19562
19563  
19564
19565