roojs-bootstrap.js
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * 
20  * @constructor
21  * Do not use directly - it does not do anything..
22  * @param {Object} config The config object
23  */
24
25
26
27 Roo.bootstrap.Component = function(config){
28     Roo.bootstrap.Component.superclass.constructor.call(this, config);
29 };
30
31 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
32     
33     
34     allowDomMove : false, // to stop relocations in parent onRender...
35     
36     cls : false,
37     
38     style : false,
39     
40     autoCreate : false,
41     
42     initEvents : function() {  },
43     
44     xattr : false,
45     
46     parentId : false,
47     
48     can_build_overlaid : true,
49     
50     dataId : false,
51     
52     name : false,
53     
54     parent: function() {
55         // returns the parent component..
56         return Roo.ComponentMgr.get(this.parentId)
57         
58         
59     },
60     
61     // private
62     onRender : function(ct, position)
63     {
64        // Roo.log("Call onRender: " + this.xtype);
65         
66         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
67         
68         if(this.el){
69             if (this.el.attr('xtype')) {
70                 this.el.attr('xtypex', this.el.attr('xtype'));
71                 this.el.dom.removeAttribute('xtype');
72                 
73                 this.initEvents();
74             }
75             
76             return;
77         }
78         
79          
80         
81         var cfg = Roo.apply({},  this.getAutoCreate());
82         cfg.id = Roo.id();
83         
84         // fill in the extra attributes 
85         if (this.xattr && typeof(this.xattr) =='object') {
86             for (var i in this.xattr) {
87                 cfg[i] = this.xattr[i];
88             }
89         }
90         
91         if(this.dataId){
92             cfg.dataId = this.dataId;
93         }
94         
95         if (this.cls) {
96             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
97         }
98         
99         if (this.style) { // fixme needs to support more complex style data.
100             cfg.style = this.style;
101         }
102         
103         if(this.name){
104             cfg.name = this.name;
105         }
106         
107         this.el = ct.createChild(cfg, position);
108         
109         if(this.tabIndex !== undefined){
110             this.el.dom.setAttribute('tabIndex', this.tabIndex);
111         }
112         this.initEvents();
113         
114         
115     },
116     
117     getChildContainer : function()
118     {
119         return this.el;
120     },
121     
122     
123     addxtype  : function(tree,cntr)
124     {
125         var cn = this;
126         
127         cn = Roo.factory(tree);
128            
129         cn.parentType = this.xtype; //??
130         cn.parentId = this.id;
131         
132         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
133         
134         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
135         
136         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
137         
138         var build_from_html =  Roo.XComponent.build_from_html;
139           
140         var is_body  = (tree.xtype == 'Body') ;
141           
142         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
143           
144         var self_cntr_el = Roo.get(this[cntr]());
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]());
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]());
240              }
241             // then add the element..
242         }
243         
244         
245         // handle the kids..
246         
247         var nitems = [];
248         if (typeof (tree.menu) != 'undefined') {
249             tree.menu.parentType = cn.xtype;
250             tree.menu.triggerEl = cn.el;
251             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
252             
253         }
254         
255         if (!tree.items || !tree.items.length) {
256             cn.items = nitems;
257             return cn;
258         }
259         var items = tree.items;
260         delete tree.items;
261         
262         //Roo.log(items.length);
263             // add the items..
264         for(var i =0;i < items.length;i++) {
265             nitems.push(cn.addxtype(Roo.apply({}, items[i])));
266         }
267         
268         cn.items = nitems;
269         
270         return cn;
271     }
272     
273     
274     
275     
276 });
277
278  /*
279  * - LGPL
280  *
281  * Body
282  * 
283  */
284
285 /**
286  * @class Roo.bootstrap.Body
287  * @extends Roo.bootstrap.Component
288  * Bootstrap Body class
289  * 
290  * @constructor
291  * Create a new body
292  * @param {Object} config The config object
293  */
294
295 Roo.bootstrap.Body = function(config){
296     Roo.bootstrap.Body.superclass.constructor.call(this, config);
297     this.el = Roo.get(document.body);
298     if (this.cls && this.cls.length) {
299         Roo.get(document.body).addClass(this.cls);
300     }
301 };
302
303 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
304       
305         autoCreate : {
306         cls: 'container'
307     },
308     onRender : function(ct, position)
309     {
310        /* Roo.log("Roo.bootstrap.Body - onRender");
311         if (this.cls && this.cls.length) {
312             Roo.get(document.body).addClass(this.cls);
313         }
314         // style??? xttr???
315         */
316     }
317     
318     
319  
320    
321 });
322
323  /*
324  * - LGPL
325  *
326  * button group
327  * 
328  */
329
330
331 /**
332  * @class Roo.bootstrap.ButtonGroup
333  * @extends Roo.bootstrap.Component
334  * Bootstrap ButtonGroup class
335  * @cfg {String} size lg | sm | xs (default empty normal)
336  * @cfg {String} align vertical | justified  (default none)
337  * @cfg {String} direction up | down (default down)
338  * @cfg {Boolean} toolbar false | true
339  * @cfg {Boolean} btn true | false
340  * 
341  * 
342  * @constructor
343  * Create a new Input
344  * @param {Object} config The config object
345  */
346
347 Roo.bootstrap.ButtonGroup = function(config){
348     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
349 };
350
351 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
352     
353     size: '',
354     align: '',
355     direction: '',
356     toolbar: false,
357     btn: true,
358
359     getAutoCreate : function(){
360         var cfg = {
361             cls: 'btn-group',
362             html : null
363         }
364         
365         cfg.html = this.html || cfg.html;
366         
367         if (this.toolbar) {
368             cfg = {
369                 cls: 'btn-toolbar',
370                 html: null
371             }
372             
373             return cfg;
374         }
375         
376         if (['vertical','justified'].indexOf(this.align)!==-1) {
377             cfg.cls = 'btn-group-' + this.align;
378             
379             if (this.align == 'justified') {
380                 console.log(this.items);
381             }
382         }
383         
384         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
385             cfg.cls += ' btn-group-' + this.size;
386         }
387         
388         if (this.direction == 'up') {
389             cfg.cls += ' dropup' ;
390         }
391         
392         return cfg;
393     }
394    
395 });
396
397  /*
398  * - LGPL
399  *
400  * button
401  * 
402  */
403
404 /**
405  * @class Roo.bootstrap.Button
406  * @extends Roo.bootstrap.Component
407  * Bootstrap Button class
408  * @cfg {String} html The button content
409  * @cfg {String} weight default (or empty) | primary | success | info | warning | danger | link
410  * @cfg {String} size empty | lg | sm | xs
411  * @cfg {String} tag empty | a | input | submit
412  * @cfg {String} href empty or href
413  * @cfg {Boolean} disabled false | true
414  * @cfg {Boolean} isClose false | true
415  * @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
416  * @cfg {String} badge text for badge
417  * @cfg {String} theme default (or empty) | glow
418  * @cfg {Boolean} inverse false | true
419  * @cfg {Boolean} toggle false | true
420  * @cfg {String} ontext text for on toggle state
421  * @cfg {String} offtext text for off toggle state
422  * @cfg {Boolean} defaulton true | false
423  * @cfg {Boolean} preventDefault (true | false) default true
424  * @cfg {Boolean} removeClass true | false remove the standard class..
425  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
426  * 
427  * @constructor
428  * Create a new button
429  * @param {Object} config The config object
430  */
431
432
433 Roo.bootstrap.Button = function(config){
434     Roo.bootstrap.Button.superclass.constructor.call(this, config);
435     this.addEvents({
436         // raw events
437         /**
438          * @event click
439          * When a butotn is pressed
440          * @param {Roo.EventObject} e
441          */
442         "click" : true,
443          /**
444          * @event toggle
445          * After the button has been toggles
446          * @param {Roo.EventObject} e
447          * @param {boolean} pressed (also available as button.pressed)
448          */
449         "toggle" : true
450     });
451 };
452
453 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
454     html: false,
455     active: false,
456     weight: '',
457     size: '',
458     tag: 'button',
459     href: '',
460     disabled: false,
461     isClose: false,
462     glyphicon: '',
463     badge: '',
464     theme: 'default',
465     inverse: false,
466     
467     toggle: false,
468     ontext: 'ON',
469     offtext: 'OFF',
470     defaulton: true,
471     preventDefault: true,
472     removeClass: false,
473     name: false,
474     target: false,
475     
476     
477     pressed : null,
478      
479     
480     getAutoCreate : function(){
481         
482         var cfg = {
483             tag : 'button',
484             cls : 'roo-button',
485             html: ''
486         };
487         
488         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
489             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
490             this.tag = 'button';
491         } else {
492             cfg.tag = this.tag;
493         }
494         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
495         
496         if (this.toggle == true) {
497             cfg={
498                 tag: 'div',
499                 cls: 'slider-frame roo-button',
500                 cn: [
501                     {
502                         tag: 'span',
503                         'data-on-text':'ON',
504                         'data-off-text':'OFF',
505                         cls: 'slider-button',
506                         html: this.offtext
507                     }
508                 ]
509             };
510             
511             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
512                 cfg.cls += ' '+this.weight;
513             }
514             
515             return cfg;
516         }
517         
518         if (this.isClose) {
519             cfg.cls += ' close';
520             
521             cfg["aria-hidden"] = true;
522             
523             cfg.html = "&times;";
524             
525             return cfg;
526         }
527         
528          
529         if (this.theme==='default') {
530             cfg.cls = 'btn roo-button';
531             
532             //if (this.parentType != 'Navbar') {
533             this.weight = this.weight.length ?  this.weight : 'default';
534             //}
535             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
536                 
537                 cfg.cls += ' btn-' + this.weight;
538             }
539         } else if (this.theme==='glow') {
540             
541             cfg.tag = 'a';
542             cfg.cls = 'btn-glow roo-button';
543             
544             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
545                 
546                 cfg.cls += ' ' + this.weight;
547             }
548         }
549    
550         
551         if (this.inverse) {
552             this.cls += ' inverse';
553         }
554         
555         
556         if (this.active) {
557             cfg.cls += ' active';
558         }
559         
560         if (this.disabled) {
561             cfg.disabled = 'disabled';
562         }
563         
564         if (this.items) {
565             Roo.log('changing to ul' );
566             cfg.tag = 'ul';
567             this.glyphicon = 'caret';
568         }
569         
570         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
571          
572         //gsRoo.log(this.parentType);
573         if (this.parentType === 'Navbar' && !this.parent().bar) {
574             Roo.log('changing to li?');
575             
576             cfg.tag = 'li';
577             
578             cfg.cls = '';
579             cfg.cn =  [{
580                 tag : 'a',
581                 cls : 'roo-button',
582                 html : this.html,
583                 href : this.href || '#'
584             }];
585             if (this.menu) {
586                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
587                 cfg.cls += ' dropdown';
588             }   
589             
590             delete cfg.html;
591             
592         }
593         
594        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
595         
596         if (this.glyphicon) {
597             cfg.html = ' ' + cfg.html;
598             
599             cfg.cn = [
600                 {
601                     tag: 'span',
602                     cls: 'glyphicon glyphicon-' + this.glyphicon
603                 }
604             ];
605         }
606         
607         if (this.badge) {
608             cfg.html += ' ';
609             
610             cfg.tag = 'a';
611             
612 //            cfg.cls='btn roo-button';
613             
614             cfg.href=this.href;
615             
616             var value = cfg.html;
617             
618             if(this.glyphicon){
619                 value = {
620                             tag: 'span',
621                             cls: 'glyphicon glyphicon-' + this.glyphicon,
622                             html: this.html
623                         };
624                 
625             }
626             
627             cfg.cn = [
628                 value,
629                 {
630                     tag: 'span',
631                     cls: 'badge',
632                     html: this.badge
633                 }
634             ];
635             
636             cfg.html='';
637         }
638         
639         if (this.menu) {
640             cfg.cls += ' dropdown';
641             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
642         }
643         
644         if (cfg.tag !== 'a' && this.href !== '') {
645             throw "Tag must be a to set href.";
646         } else if (this.href.length > 0) {
647             cfg.href = this.href;
648         }
649         
650         if(this.removeClass){
651             cfg.cls = '';
652         }
653         
654         if(this.target){
655             cfg.target = this.target;
656         }
657         
658         return cfg;
659     },
660     initEvents: function() {
661        // Roo.log('init events?');
662 //        Roo.log(this.el.dom);
663        if (this.el.hasClass('roo-button')) {
664             this.el.on('click', this.onClick, this);
665        } else {
666             this.el.select('.roo-button').on('click', this.onClick, this);
667        }
668        
669        if(this.removeClass){
670            this.el.on('click', this.onClick, this);
671        }
672        
673        this.el.enableDisplayMode();
674         
675     },
676     onClick : function(e)
677     {
678         if (this.disabled) {
679             return;
680         }
681         
682         Roo.log('button on click ');
683         if(this.preventDefault){
684             e.preventDefault();
685         }
686         if (this.pressed === true || this.pressed === false) {
687             this.pressed = !this.pressed;
688             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
689             this.fireEvent('toggle', this, e, this.pressed);
690         }
691         
692         
693         this.fireEvent('click', this, e);
694     },
695     
696     /**
697      * Enables this button
698      */
699     enable : function()
700     {
701         this.disabled = false;
702         this.el.removeClass('disabled');
703     },
704     
705     /**
706      * Disable this button
707      */
708     disable : function()
709     {
710         this.disabled = true;
711         this.el.addClass('disabled');
712     },
713      /**
714      * sets the active state on/off, 
715      * @param {Boolean} state (optional) Force a particular state
716      */
717     setActive : function(v) {
718         
719         this.el[v ? 'addClass' : 'removeClass']('active');
720     },
721      /**
722      * toggles the current active state 
723      */
724     toggleActive : function()
725     {
726        var active = this.el.hasClass('active');
727        this.setActive(!active);
728        
729         
730     },
731     setText : function(str)
732     {
733         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
734     },
735     hide: function() {
736        
737      
738         this.el.hide();   
739     },
740     show: function() {
741        
742         this.el.show();   
743     }
744     
745     
746 });
747
748  /*
749  * - LGPL
750  *
751  * column
752  * 
753  */
754
755 /**
756  * @class Roo.bootstrap.Column
757  * @extends Roo.bootstrap.Component
758  * Bootstrap Column class
759  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
760  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
761  * @cfg {Number} md colspan out of 12 for computer-sized screens
762  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
763  * @cfg {String} html content of column.
764  * 
765  * @constructor
766  * Create a new Column
767  * @param {Object} config The config object
768  */
769
770 Roo.bootstrap.Column = function(config){
771     Roo.bootstrap.Column.superclass.constructor.call(this, config);
772 };
773
774 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
775     
776     xs: null,
777     sm: null,
778     md: null,
779     lg: null,
780     html: '',
781     offset: 0,
782     
783     getAutoCreate : function(){
784         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
785         
786         cfg = {
787             tag: 'div',
788             cls: 'column'
789         };
790         
791         var settings=this;
792         ['xs','sm','md','lg'].map(function(size){
793             if (settings[size]) {
794                 cfg.cls += ' col-' + size + '-' + settings[size];
795             }
796         });
797         if (this.html.length) {
798             cfg.html = this.html;
799         }
800         
801         return cfg;
802     }
803    
804 });
805
806  
807
808  /*
809  * - LGPL
810  *
811  * page container.
812  * 
813  */
814
815
816 /**
817  * @class Roo.bootstrap.Container
818  * @extends Roo.bootstrap.Component
819  * Bootstrap Container class
820  * @cfg {Boolean} jumbotron is it a jumbotron element
821  * @cfg {String} html content of element
822  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
823  * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
824  * @cfg {String} header content of header (for panel)
825  * @cfg {String} footer content of footer (for panel)
826  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
827  * @cfg {String} tag (header|aside|section) type of HTML tag.
828
829  *     
830  * @constructor
831  * Create a new Container
832  * @param {Object} config The config object
833  */
834
835 Roo.bootstrap.Container = function(config){
836     Roo.bootstrap.Container.superclass.constructor.call(this, config);
837 };
838
839 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
840     
841     jumbotron : false,
842     well: '',
843     panel : '',
844     header: '',
845     footer : '',
846     sticky: '',
847     tag : false,
848   
849      
850     getChildContainer : function() {
851         
852         if(!this.el){
853             return false;
854         }
855         
856         if (this.panel.length) {
857             return this.el.select('.panel-body',true).first();
858         }
859         
860         return this.el;
861     },
862     
863     
864     getAutoCreate : function(){
865         
866         var cfg = {
867             tag : this.tag || 'div',
868             html : '',
869             cls : ''
870         };
871         if (this.jumbotron) {
872             cfg.cls = 'jumbotron';
873         }
874         // - this is applied by the parent..
875         //if (this.cls) {
876         //    cfg.cls = this.cls + '';
877         //}
878         
879         if (this.sticky.length) {
880             
881             var bd = Roo.get(document.body);
882             if (!bd.hasClass('bootstrap-sticky')) {
883                 bd.addClass('bootstrap-sticky');
884                 Roo.select('html',true).setStyle('height', '100%');
885             }
886              
887             cfg.cls += 'bootstrap-sticky-' + this.sticky;
888         }
889         
890         
891         if (this.well.length) {
892             switch (this.well) {
893                 case 'lg':
894                 case 'sm':
895                     cfg.cls +=' well well-' +this.well;
896                     break;
897                 default:
898                     cfg.cls +=' well';
899                     break;
900             }
901         }
902         
903         var body = cfg;
904         
905         if (this.panel.length) {
906             cfg.cls += ' panel panel-' + this.panel;
907             cfg.cn = [];
908             if (this.header.length) {
909                 cfg.cn.push({
910                     
911                     cls : 'panel-heading',
912                     cn : [{
913                         tag: 'h3',
914                         cls : 'panel-title',
915                         html : this.header
916                     }]
917                     
918                 });
919             }
920             body = false;
921             cfg.cn.push({
922                 cls : 'panel-body',
923                 html : this.html
924             });
925             
926             
927             if (this.footer.length) {
928                 cfg.cn.push({
929                     cls : 'panel-footer',
930                     html : this.footer
931                     
932                 });
933             }
934             
935         }
936         
937         if (body) {
938             body.html = this.html || cfg.html;
939         }
940         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
941             cfg.cls =  'container';
942         }
943         
944         return cfg;
945     }
946    
947 });
948
949  /*
950  * - LGPL
951  *
952  * image
953  * 
954  */
955
956
957 /**
958  * @class Roo.bootstrap.Img
959  * @extends Roo.bootstrap.Component
960  * Bootstrap Img class
961  * @cfg {Boolean} imgResponsive false | true
962  * @cfg {String} border rounded | circle | thumbnail
963  * @cfg {String} src image source
964  * @cfg {String} alt image alternative text
965  * @cfg {String} href a tag href
966  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
967  * 
968  * @constructor
969  * Create a new Input
970  * @param {Object} config The config object
971  */
972
973 Roo.bootstrap.Img = function(config){
974     Roo.bootstrap.Img.superclass.constructor.call(this, config);
975     
976     this.addEvents({
977         // img events
978         /**
979          * @event click
980          * The img click event for the img.
981          * @param {Roo.EventObject} e
982          */
983         "click" : true
984     });
985 };
986
987 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
988     
989     imgResponsive: true,
990     border: '',
991     src: '',
992     href: false,
993     target: false,
994
995     getAutoCreate : function(){
996         
997         var cfg = {
998             tag: 'img',
999             cls: (this.imgResponsive) ? 'img-responsive' : '',
1000             html : null
1001         }
1002         
1003         cfg.html = this.html || cfg.html;
1004         
1005         cfg.src = this.src || cfg.src;
1006         
1007         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1008             cfg.cls += ' img-' + this.border;
1009         }
1010         
1011         if(this.alt){
1012             cfg.alt = this.alt;
1013         }
1014         
1015         if(this.href){
1016             var a = {
1017                 tag: 'a',
1018                 href: this.href,
1019                 cn: [
1020                     cfg
1021                 ]
1022             }
1023             
1024             if(this.target){
1025                 a.target = this.target;
1026             }
1027             
1028         }
1029         
1030         
1031         return (this.href) ? a : cfg;
1032     },
1033     
1034     initEvents: function() {
1035         
1036         if(!this.href){
1037             this.el.on('click', this.onClick, this);
1038         }
1039     },
1040     
1041     onClick : function(e)
1042     {
1043         Roo.log('img onclick');
1044         this.fireEvent('click', this, e);
1045     }
1046    
1047 });
1048
1049  /*
1050  * - LGPL
1051  *
1052  * image
1053  * 
1054  */
1055
1056
1057 /**
1058  * @class Roo.bootstrap.Link
1059  * @extends Roo.bootstrap.Component
1060  * Bootstrap Link Class
1061  * @cfg {String} alt image alternative text
1062  * @cfg {String} href a tag href
1063  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1064  * @cfg {String} html the content of the link.
1065
1066  * 
1067  * @constructor
1068  * Create a new Input
1069  * @param {Object} config The config object
1070  */
1071
1072 Roo.bootstrap.Link = function(config){
1073     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1074     
1075     this.addEvents({
1076         // img events
1077         /**
1078          * @event click
1079          * The img click event for the img.
1080          * @param {Roo.EventObject} e
1081          */
1082         "click" : true
1083     });
1084 };
1085
1086 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1087     
1088     href: false,
1089     target: false,
1090
1091     getAutoCreate : function(){
1092         
1093         var cfg = {
1094             tag: 'a',
1095             html : this.html || 'html-missing'
1096         }
1097         
1098         
1099         if(this.alt){
1100             cfg.alt = this.alt;
1101         }
1102         cfg.href = this.href || '#';
1103         if(this.target){
1104             cfg.target = this.target;
1105         }
1106         
1107         return cfg;
1108     },
1109     
1110     initEvents: function() {
1111         
1112         if(!this.href){
1113             this.el.on('click', this.onClick, this);
1114         }
1115     },
1116     
1117     onClick : function(e)
1118     {
1119         //Roo.log('img onclick');
1120         this.fireEvent('click', this, e);
1121     }
1122    
1123 });
1124
1125  /*
1126  * - LGPL
1127  *
1128  * header
1129  * 
1130  */
1131
1132 /**
1133  * @class Roo.bootstrap.Header
1134  * @extends Roo.bootstrap.Component
1135  * Bootstrap Header class
1136  * @cfg {String} html content of header
1137  * @cfg {Number} level (1|2|3|4|5|6) default 1
1138  * 
1139  * @constructor
1140  * Create a new Header
1141  * @param {Object} config The config object
1142  */
1143
1144
1145 Roo.bootstrap.Header  = function(config){
1146     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1147 };
1148
1149 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1150     
1151     //href : false,
1152     html : false,
1153     level : 1,
1154     
1155     
1156     
1157     getAutoCreate : function(){
1158         
1159         var cfg = {
1160             tag: 'h' + (1 *this.level),
1161             html: this.html || 'fill in html'
1162         } ;
1163         
1164         return cfg;
1165     }
1166    
1167 });
1168
1169  
1170
1171  /*
1172  * Based on:
1173  * Ext JS Library 1.1.1
1174  * Copyright(c) 2006-2007, Ext JS, LLC.
1175  *
1176  * Originally Released Under LGPL - original licence link has changed is not relivant.
1177  *
1178  * Fork - LGPL
1179  * <script type="text/javascript">
1180  */
1181  
1182 /**
1183  * @class Roo.bootstrap.MenuMgr
1184  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1185  * @singleton
1186  */
1187 Roo.bootstrap.MenuMgr = function(){
1188    var menus, active, groups = {}, attached = false, lastShow = new Date();
1189
1190    // private - called when first menu is created
1191    function init(){
1192        menus = {};
1193        active = new Roo.util.MixedCollection();
1194        Roo.get(document).addKeyListener(27, function(){
1195            if(active.length > 0){
1196                hideAll();
1197            }
1198        });
1199    }
1200
1201    // private
1202    function hideAll(){
1203        if(active && active.length > 0){
1204            var c = active.clone();
1205            c.each(function(m){
1206                m.hide();
1207            });
1208        }
1209    }
1210
1211    // private
1212    function onHide(m){
1213        active.remove(m);
1214        if(active.length < 1){
1215            Roo.get(document).un("mouseup", onMouseDown);
1216             
1217            attached = false;
1218        }
1219    }
1220
1221    // private
1222    function onShow(m){
1223        var last = active.last();
1224        lastShow = new Date();
1225        active.add(m);
1226        if(!attached){
1227           Roo.get(document).on("mouseup", onMouseDown);
1228            
1229            attached = true;
1230        }
1231        if(m.parentMenu){
1232           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1233           m.parentMenu.activeChild = m;
1234        }else if(last && last.isVisible()){
1235           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1236        }
1237    }
1238
1239    // private
1240    function onBeforeHide(m){
1241        if(m.activeChild){
1242            m.activeChild.hide();
1243        }
1244        if(m.autoHideTimer){
1245            clearTimeout(m.autoHideTimer);
1246            delete m.autoHideTimer;
1247        }
1248    }
1249
1250    // private
1251    function onBeforeShow(m){
1252        var pm = m.parentMenu;
1253        if(!pm && !m.allowOtherMenus){
1254            hideAll();
1255        }else if(pm && pm.activeChild && active != m){
1256            pm.activeChild.hide();
1257        }
1258    }
1259
1260    // private
1261    function onMouseDown(e){
1262         Roo.log("on MouseDown");
1263         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
1264            hideAll();
1265         }
1266         
1267         
1268    }
1269
1270    // private
1271    function onBeforeCheck(mi, state){
1272        if(state){
1273            var g = groups[mi.group];
1274            for(var i = 0, l = g.length; i < l; i++){
1275                if(g[i] != mi){
1276                    g[i].setChecked(false);
1277                }
1278            }
1279        }
1280    }
1281
1282    return {
1283
1284        /**
1285         * Hides all menus that are currently visible
1286         */
1287        hideAll : function(){
1288             hideAll();  
1289        },
1290
1291        // private
1292        register : function(menu){
1293            if(!menus){
1294                init();
1295            }
1296            menus[menu.id] = menu;
1297            menu.on("beforehide", onBeforeHide);
1298            menu.on("hide", onHide);
1299            menu.on("beforeshow", onBeforeShow);
1300            menu.on("show", onShow);
1301            var g = menu.group;
1302            if(g && menu.events["checkchange"]){
1303                if(!groups[g]){
1304                    groups[g] = [];
1305                }
1306                groups[g].push(menu);
1307                menu.on("checkchange", onCheck);
1308            }
1309        },
1310
1311         /**
1312          * Returns a {@link Roo.menu.Menu} object
1313          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1314          * be used to generate and return a new Menu instance.
1315          */
1316        get : function(menu){
1317            if(typeof menu == "string"){ // menu id
1318                return menus[menu];
1319            }else if(menu.events){  // menu instance
1320                return menu;
1321            }
1322            /*else if(typeof menu.length == 'number'){ // array of menu items?
1323                return new Roo.bootstrap.Menu({items:menu});
1324            }else{ // otherwise, must be a config
1325                return new Roo.bootstrap.Menu(menu);
1326            }
1327            */
1328            return false;
1329        },
1330
1331        // private
1332        unregister : function(menu){
1333            delete menus[menu.id];
1334            menu.un("beforehide", onBeforeHide);
1335            menu.un("hide", onHide);
1336            menu.un("beforeshow", onBeforeShow);
1337            menu.un("show", onShow);
1338            var g = menu.group;
1339            if(g && menu.events["checkchange"]){
1340                groups[g].remove(menu);
1341                menu.un("checkchange", onCheck);
1342            }
1343        },
1344
1345        // private
1346        registerCheckable : function(menuItem){
1347            var g = menuItem.group;
1348            if(g){
1349                if(!groups[g]){
1350                    groups[g] = [];
1351                }
1352                groups[g].push(menuItem);
1353                menuItem.on("beforecheckchange", onBeforeCheck);
1354            }
1355        },
1356
1357        // private
1358        unregisterCheckable : function(menuItem){
1359            var g = menuItem.group;
1360            if(g){
1361                groups[g].remove(menuItem);
1362                menuItem.un("beforecheckchange", onBeforeCheck);
1363            }
1364        }
1365    };
1366 }();/*
1367  * - LGPL
1368  *
1369  * menu
1370  * 
1371  */
1372
1373 /**
1374  * @class Roo.bootstrap.Menu
1375  * @extends Roo.bootstrap.Component
1376  * Bootstrap Menu class - container for MenuItems
1377  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1378  * 
1379  * @constructor
1380  * Create a new Menu
1381  * @param {Object} config The config object
1382  */
1383
1384
1385 Roo.bootstrap.Menu = function(config){
1386     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1387     if (this.registerMenu) {
1388         Roo.bootstrap.MenuMgr.register(this);
1389     }
1390     this.addEvents({
1391         /**
1392          * @event beforeshow
1393          * Fires before this menu is displayed
1394          * @param {Roo.menu.Menu} this
1395          */
1396         beforeshow : true,
1397         /**
1398          * @event beforehide
1399          * Fires before this menu is hidden
1400          * @param {Roo.menu.Menu} this
1401          */
1402         beforehide : true,
1403         /**
1404          * @event show
1405          * Fires after this menu is displayed
1406          * @param {Roo.menu.Menu} this
1407          */
1408         show : true,
1409         /**
1410          * @event hide
1411          * Fires after this menu is hidden
1412          * @param {Roo.menu.Menu} this
1413          */
1414         hide : true,
1415         /**
1416          * @event click
1417          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1418          * @param {Roo.menu.Menu} this
1419          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1420          * @param {Roo.EventObject} e
1421          */
1422         click : true,
1423         /**
1424          * @event mouseover
1425          * Fires when the mouse is hovering over this menu
1426          * @param {Roo.menu.Menu} this
1427          * @param {Roo.EventObject} e
1428          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1429          */
1430         mouseover : true,
1431         /**
1432          * @event mouseout
1433          * Fires when the mouse exits this menu
1434          * @param {Roo.menu.Menu} this
1435          * @param {Roo.EventObject} e
1436          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1437          */
1438         mouseout : true,
1439         /**
1440          * @event itemclick
1441          * Fires when a menu item contained in this menu is clicked
1442          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1443          * @param {Roo.EventObject} e
1444          */
1445         itemclick: true
1446     });
1447     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1448 };
1449
1450 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1451     
1452    /// html : false,
1453     //align : '',
1454     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1455     type: false,
1456     /**
1457      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1458      */
1459     registerMenu : true,
1460     
1461     menuItems :false, // stores the menu items..
1462     
1463     hidden:true,
1464     
1465     parentMenu : false,
1466     
1467     getChildContainer : function() {
1468         return this.el;  
1469     },
1470     
1471     getAutoCreate : function(){
1472          
1473         //if (['right'].indexOf(this.align)!==-1) {
1474         //    cfg.cn[1].cls += ' pull-right'
1475         //}
1476         
1477         
1478         var cfg = {
1479             tag : 'ul',
1480             cls : 'dropdown-menu' ,
1481             style : 'z-index:1000'
1482             
1483         }
1484         
1485         if (this.type === 'submenu') {
1486             cfg.cls = 'submenu active';
1487         }
1488         if (this.type === 'treeview') {
1489             cfg.cls = 'treeview-menu';
1490         }
1491         
1492         return cfg;
1493     },
1494     initEvents : function() {
1495         
1496        // Roo.log("ADD event");
1497        // Roo.log(this.triggerEl.dom);
1498         this.triggerEl.on('click', this.onTriggerPress, this);
1499         this.triggerEl.addClass('dropdown-toggle');
1500         this.el.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
1501
1502         this.el.on("mouseover", this.onMouseOver, this);
1503         this.el.on("mouseout", this.onMouseOut, this);
1504         
1505         
1506     },
1507     findTargetItem : function(e){
1508         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
1509         if(!t){
1510             return false;
1511         }
1512         //Roo.log(t);         Roo.log(t.id);
1513         if(t && t.id){
1514             //Roo.log(this.menuitems);
1515             return this.menuitems.get(t.id);
1516             
1517             //return this.items.get(t.menuItemId);
1518         }
1519         
1520         return false;
1521     },
1522     onClick : function(e){
1523         Roo.log("menu.onClick");
1524         var t = this.findTargetItem(e);
1525         if(!t){
1526             return;
1527         }
1528         Roo.log(e);
1529         /*
1530         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
1531             if(t == this.activeItem && t.shouldDeactivate(e)){
1532                 this.activeItem.deactivate();
1533                 delete this.activeItem;
1534                 return;
1535             }
1536             if(t.canActivate){
1537                 this.setActiveItem(t, true);
1538             }
1539             return;
1540             
1541             
1542         }
1543         */
1544         Roo.log('pass click event');
1545         
1546         t.onClick(e);
1547         
1548         this.fireEvent("click", this, t, e);
1549         
1550         this.hide();
1551     },
1552      onMouseOver : function(e){
1553         var t  = this.findTargetItem(e);
1554         //Roo.log(t);
1555         //if(t){
1556         //    if(t.canActivate && !t.disabled){
1557         //        this.setActiveItem(t, true);
1558         //    }
1559         //}
1560         
1561         this.fireEvent("mouseover", this, e, t);
1562     },
1563     isVisible : function(){
1564         return !this.hidden;
1565     },
1566      onMouseOut : function(e){
1567         var t  = this.findTargetItem(e);
1568         
1569         //if(t ){
1570         //    if(t == this.activeItem && t.shouldDeactivate(e)){
1571         //        this.activeItem.deactivate();
1572         //        delete this.activeItem;
1573         //    }
1574         //}
1575         this.fireEvent("mouseout", this, e, t);
1576     },
1577     
1578     
1579     /**
1580      * Displays this menu relative to another element
1581      * @param {String/HTMLElement/Roo.Element} element The element to align to
1582      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
1583      * the element (defaults to this.defaultAlign)
1584      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1585      */
1586     show : function(el, pos, parentMenu){
1587         this.parentMenu = parentMenu;
1588         if(!this.el){
1589             this.render();
1590         }
1591         this.fireEvent("beforeshow", this);
1592         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
1593     },
1594      /**
1595      * Displays this menu at a specific xy position
1596      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
1597      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1598      */
1599     showAt : function(xy, parentMenu, /* private: */_e){
1600         this.parentMenu = parentMenu;
1601         if(!this.el){
1602             this.render();
1603         }
1604         if(_e !== false){
1605             this.fireEvent("beforeshow", this);
1606             
1607             //xy = this.el.adjustForConstraints(xy);
1608         }
1609         //this.el.setXY(xy);
1610         //this.el.show();
1611         this.hideMenuItems();
1612         this.hidden = false;
1613         this.triggerEl.addClass('open');
1614         this.focus();
1615         this.fireEvent("show", this);
1616     },
1617     
1618     focus : function(){
1619         return;
1620         if(!this.hidden){
1621             this.doFocus.defer(50, this);
1622         }
1623     },
1624
1625     doFocus : function(){
1626         if(!this.hidden){
1627             this.focusEl.focus();
1628         }
1629     },
1630
1631     /**
1632      * Hides this menu and optionally all parent menus
1633      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
1634      */
1635     hide : function(deep){
1636         
1637         this.hideMenuItems();
1638         if(this.el && this.isVisible()){
1639             this.fireEvent("beforehide", this);
1640             if(this.activeItem){
1641                 this.activeItem.deactivate();
1642                 this.activeItem = null;
1643             }
1644             this.triggerEl.removeClass('open');;
1645             this.hidden = true;
1646             this.fireEvent("hide", this);
1647         }
1648         if(deep === true && this.parentMenu){
1649             this.parentMenu.hide(true);
1650         }
1651     },
1652     
1653     onTriggerPress  : function(e)
1654     {
1655         
1656         Roo.log('trigger press');
1657         //Roo.log(e.getTarget());
1658        // Roo.log(this.triggerEl.dom);
1659         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
1660             return;
1661         }
1662         if (this.isVisible()) {
1663             Roo.log('hide');
1664             this.hide();
1665         } else {
1666             this.show(this.triggerEl, false, false);
1667         }
1668         
1669         
1670     },
1671     
1672          
1673        
1674     
1675     hideMenuItems : function()
1676     {
1677         //$(backdrop).remove()
1678         Roo.select('.open',true).each(function(aa) {
1679             
1680             aa.removeClass('open');
1681           //var parent = getParent($(this))
1682           //var relatedTarget = { relatedTarget: this }
1683           
1684            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
1685           //if (e.isDefaultPrevented()) return
1686            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
1687         })
1688     },
1689     addxtypeChild : function (tree, cntr) {
1690         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
1691           
1692         this.menuitems.add(comp);
1693         return comp;
1694
1695     },
1696     getEl : function()
1697     {
1698         Roo.log(this.el);
1699         return this.el;
1700     }
1701 });
1702
1703  
1704  /*
1705  * - LGPL
1706  *
1707  * menu item
1708  * 
1709  */
1710
1711
1712 /**
1713  * @class Roo.bootstrap.MenuItem
1714  * @extends Roo.bootstrap.Component
1715  * Bootstrap MenuItem class
1716  * @cfg {String} html the menu label
1717  * @cfg {String} href the link
1718  * @cfg {Boolean} preventDefault (true | false) default true
1719  * 
1720  * 
1721  * @constructor
1722  * Create a new MenuItem
1723  * @param {Object} config The config object
1724  */
1725
1726
1727 Roo.bootstrap.MenuItem = function(config){
1728     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
1729     this.addEvents({
1730         // raw events
1731         /**
1732          * @event click
1733          * The raw click event for the entire grid.
1734          * @param {Roo.EventObject} e
1735          */
1736         "click" : true
1737     });
1738 };
1739
1740 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
1741     
1742     href : false,
1743     html : false,
1744     preventDefault: true,
1745     
1746     getAutoCreate : function(){
1747         var cfg= {
1748             tag: 'li',
1749             cls: 'dropdown-menu-item',
1750             cn: [
1751                     {
1752                         tag : 'a',
1753                         href : '#',
1754                         html : 'Link'
1755                     }
1756                 ]
1757         };
1758         if (this.parent().type == 'treeview') {
1759             cfg.cls = 'treeview-menu';
1760         }
1761         
1762         cfg.cn[0].href = this.href || cfg.cn[0].href ;
1763         cfg.cn[0].html = this.html || cfg.cn[0].html ;
1764         return cfg;
1765     },
1766     
1767     initEvents: function() {
1768         
1769         //this.el.select('a').on('click', this.onClick, this);
1770         
1771     },
1772     onClick : function(e)
1773     {
1774         Roo.log('item on click ');
1775         //if(this.preventDefault){
1776         //    e.preventDefault();
1777         //}
1778         //this.parent().hideMenuItems();
1779         
1780         this.fireEvent('click', this, e);
1781     },
1782     getEl : function()
1783     {
1784         return this.el;
1785     }
1786 });
1787
1788  
1789
1790  /*
1791  * - LGPL
1792  *
1793  * menu separator
1794  * 
1795  */
1796
1797
1798 /**
1799  * @class Roo.bootstrap.MenuSeparator
1800  * @extends Roo.bootstrap.Component
1801  * Bootstrap MenuSeparator class
1802  * 
1803  * @constructor
1804  * Create a new MenuItem
1805  * @param {Object} config The config object
1806  */
1807
1808
1809 Roo.bootstrap.MenuSeparator = function(config){
1810     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
1811 };
1812
1813 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
1814     
1815     getAutoCreate : function(){
1816         var cfg = {
1817             cls: 'divider',
1818             tag : 'li'
1819         };
1820         
1821         return cfg;
1822     }
1823    
1824 });
1825
1826  
1827
1828  
1829 /*
1830 <div class="modal fade">
1831   <div class="modal-dialog">
1832     <div class="modal-content">
1833       <div class="modal-header">
1834         <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
1835         <h4 class="modal-title">Modal title</h4>
1836       </div>
1837       <div class="modal-body">
1838         <p>One fine body&hellip;</p>
1839       </div>
1840       <div class="modal-footer">
1841         <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
1842         <button type="button" class="btn btn-primary">Save changes</button>
1843       </div>
1844     </div><!-- /.modal-content -->
1845   </div><!-- /.modal-dialog -->
1846 </div><!-- /.modal -->
1847 */
1848 /*
1849  * - LGPL
1850  *
1851  * page contgainer.
1852  * 
1853  */
1854
1855 /**
1856  * @class Roo.bootstrap.Modal
1857  * @extends Roo.bootstrap.Component
1858  * Bootstrap Modal class
1859  * @cfg {String} title Title of dialog
1860  * @cfg {Boolean} specificTitle (true|false) default false
1861  * @cfg {Array} buttons Array of buttons or standard button set..
1862  * @cfg {String} buttonPosition (left|right|center) default right
1863  * 
1864  * @constructor
1865  * Create a new Modal Dialog
1866  * @param {Object} config The config object
1867  */
1868
1869 Roo.bootstrap.Modal = function(config){
1870     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
1871     this.addEvents({
1872         // raw events
1873         /**
1874          * @event btnclick
1875          * The raw btnclick event for the button
1876          * @param {Roo.EventObject} e
1877          */
1878         "btnclick" : true
1879     });
1880     this.buttons = this.buttons || [];
1881 };
1882
1883 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
1884     
1885     title : 'test dialog',
1886    
1887     buttons : false,
1888     
1889     // set on load...
1890     body:  false,
1891     
1892     specificTitle: false,
1893     
1894     buttonPosition: 'right',
1895     
1896     onRender : function(ct, position)
1897     {
1898         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
1899      
1900         if(!this.el){
1901             var cfg = Roo.apply({},  this.getAutoCreate());
1902             cfg.id = Roo.id();
1903             //if(!cfg.name){
1904             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
1905             //}
1906             //if (!cfg.name.length) {
1907             //    delete cfg.name;
1908            // }
1909             if (this.cls) {
1910                 cfg.cls += ' ' + this.cls;
1911             }
1912             if (this.style) {
1913                 cfg.style = this.style;
1914             }
1915             this.el = Roo.get(document.body).createChild(cfg, position);
1916         }
1917         //var type = this.el.dom.type;
1918         
1919         if(this.tabIndex !== undefined){
1920             this.el.dom.setAttribute('tabIndex', this.tabIndex);
1921         }
1922         
1923         
1924         
1925         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
1926         this.maskEl.enableDisplayMode("block");
1927         this.maskEl.hide();
1928         //this.el.addClass("x-dlg-modal");
1929     
1930         if (this.buttons.length) {
1931             Roo.each(this.buttons, function(bb) {
1932                 b = Roo.apply({}, bb);
1933                 b.xns = b.xns || Roo.bootstrap;
1934                 b.xtype = b.xtype || 'Button';
1935                 if (typeof(b.listeners) == 'undefined') {
1936                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
1937                 }
1938                 
1939                 var btn = Roo.factory(b);
1940                 
1941                 btn.onRender(this.el.select('.modal-footer div').first());
1942                 
1943             },this);
1944         }
1945         // render the children.
1946         var nitems = [];
1947         
1948         if(typeof(this.items) != 'undefined'){
1949             var items = this.items;
1950             delete this.items;
1951
1952             for(var i =0;i < items.length;i++) {
1953                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
1954             }
1955         }
1956         
1957         this.items = nitems;
1958         
1959         this.body = this.el.select('.modal-body',true).first();
1960         this.close = this.el.select('.modal-header .close', true).first();
1961         this.footer = this.el.select('.modal-footer',true).first();
1962         this.initEvents();
1963         //this.el.addClass([this.fieldClass, this.cls]);
1964         
1965     },
1966     getAutoCreate : function(){
1967         
1968         
1969         var bdy = {
1970                 cls : 'modal-body',
1971                 html : this.html || ''
1972         };
1973         
1974         var title = {
1975             tag: 'h4',
1976             cls : 'modal-title',
1977             html : this.title
1978         };
1979         
1980         if(this.specificTitle){
1981             title = this.title;
1982         };
1983         
1984         return modal = {
1985             cls: "modal fade",
1986             style : 'display: none',
1987             cn : [
1988                 {
1989                     cls: "modal-dialog",
1990                     cn : [
1991                         {
1992                             cls : "modal-content",
1993                             cn : [
1994                                 {
1995                                     cls : 'modal-header',
1996                                     cn : [
1997                                         {
1998                                             tag: 'button',
1999                                             cls : 'close',
2000                                             html : '&times'
2001                                         },
2002                                         title
2003                                     ]
2004                                 },
2005                                 bdy,
2006                                 {
2007                                     cls : 'modal-footer',
2008                                     cn : [
2009                                         {
2010                                             tag: 'div',
2011                                             cls: 'btn-' + this.buttonPosition
2012                                         }
2013                                     ]
2014                                     
2015                                 }
2016                                 
2017                                 
2018                             ]
2019                             
2020                         }
2021                     ]
2022                         
2023                 }
2024             ]
2025             
2026             
2027         };
2028           
2029     },
2030     getChildContainer : function() {
2031          
2032          return this.el.select('.modal-body',true).first();
2033         
2034     },
2035     getButtonContainer : function() {
2036          return this.el.select('.modal-footer div',true).first();
2037         
2038     },
2039     initEvents : function()
2040     {
2041         this.el.select('.modal-header .close').on('click', this.hide, this);
2042 //        
2043 //        this.addxtype(this);
2044     },
2045     show : function() {
2046         
2047         if (!this.rendered) {
2048             this.render();
2049         }
2050        
2051         this.el.addClass('on');
2052         this.el.removeClass('fade');
2053         this.el.setStyle('display', 'block');
2054         Roo.get(document.body).addClass("x-body-masked");
2055         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2056         this.maskEl.show();
2057         this.el.setStyle('zIndex', '10001');
2058         this.fireEvent('show', this);
2059         
2060         
2061     },
2062     hide : function()
2063     {
2064         Roo.log('Modal hide?!');
2065         this.maskEl.hide();
2066         Roo.get(document.body).removeClass("x-body-masked");
2067         this.el.removeClass('on');
2068         this.el.addClass('fade');
2069         this.el.setStyle('display', 'none');
2070         this.fireEvent('hide', this);
2071     },
2072     
2073     addButton : function(str, cb)
2074     {
2075          
2076         
2077         var b = Roo.apply({}, { html : str } );
2078         b.xns = b.xns || Roo.bootstrap;
2079         b.xtype = b.xtype || 'Button';
2080         if (typeof(b.listeners) == 'undefined') {
2081             b.listeners = { click : cb.createDelegate(this)  };
2082         }
2083         
2084         var btn = Roo.factory(b);
2085            
2086         btn.onRender(this.el.select('.modal-footer div').first());
2087         
2088         return btn;   
2089        
2090     },
2091     
2092     setDefaultButton : function(btn)
2093     {
2094         //this.el.select('.modal-footer').()
2095     },
2096     resizeTo: function(w,h)
2097     {
2098         // skip..
2099     },
2100     setContentSize  : function(w, h)
2101     {
2102         
2103     },
2104     onButtonClick: function(btn,e)
2105     {
2106         //Roo.log([a,b,c]);
2107         this.fireEvent('btnclick', btn.name, e);
2108     },
2109     setTitle: function(str) {
2110         this.el.select('.modal-title',true).first().dom.innerHTML = str;
2111         
2112     }
2113 });
2114
2115
2116 Roo.apply(Roo.bootstrap.Modal,  {
2117     /**
2118          * Button config that displays a single OK button
2119          * @type Object
2120          */
2121         OK :  [{
2122             name : 'ok',
2123             weight : 'primary',
2124             html : 'OK'
2125         }], 
2126         /**
2127          * Button config that displays Yes and No buttons
2128          * @type Object
2129          */
2130         YESNO : [
2131             {
2132                 name  : 'no',
2133                 html : 'No'
2134             },
2135             {
2136                 name  :'yes',
2137                 weight : 'primary',
2138                 html : 'Yes'
2139             }
2140         ],
2141         
2142         /**
2143          * Button config that displays OK and Cancel buttons
2144          * @type Object
2145          */
2146         OKCANCEL : [
2147             {
2148                name : 'cancel',
2149                 html : 'Cancel'
2150             },
2151             {
2152                 name : 'ok',
2153                 weight : 'primary',
2154                 html : 'OK'
2155             }
2156         ],
2157         /**
2158          * Button config that displays Yes, No and Cancel buttons
2159          * @type Object
2160          */
2161         YESNOCANCEL : [
2162             {
2163                 name : 'yes',
2164                 weight : 'primary',
2165                 html : 'Yes'
2166             },
2167             {
2168                 name : 'no',
2169                 html : 'No'
2170             },
2171             {
2172                 name : 'cancel',
2173                 html : 'Cancel'
2174             }
2175         ]
2176 });
2177  /*
2178  * - LGPL
2179  *
2180  * messagebox - can be used as a replace
2181  * 
2182  */
2183 /**
2184  * @class Roo.MessageBox
2185  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2186  * Example usage:
2187  *<pre><code>
2188 // Basic alert:
2189 Roo.Msg.alert('Status', 'Changes saved successfully.');
2190
2191 // Prompt for user data:
2192 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2193     if (btn == 'ok'){
2194         // process text value...
2195     }
2196 });
2197
2198 // Show a dialog using config options:
2199 Roo.Msg.show({
2200    title:'Save Changes?',
2201    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2202    buttons: Roo.Msg.YESNOCANCEL,
2203    fn: processResult,
2204    animEl: 'elId'
2205 });
2206 </code></pre>
2207  * @singleton
2208  */
2209 Roo.bootstrap.MessageBox = function(){
2210     var dlg, opt, mask, waitTimer;
2211     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2212     var buttons, activeTextEl, bwidth;
2213
2214     
2215     // private
2216     var handleButton = function(button){
2217         dlg.hide();
2218         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2219     };
2220
2221     // private
2222     var handleHide = function(){
2223         if(opt && opt.cls){
2224             dlg.el.removeClass(opt.cls);
2225         }
2226         //if(waitTimer){
2227         //    Roo.TaskMgr.stop(waitTimer);
2228         //    waitTimer = null;
2229         //}
2230     };
2231
2232     // private
2233     var updateButtons = function(b){
2234         var width = 0;
2235         if(!b){
2236             buttons["ok"].hide();
2237             buttons["cancel"].hide();
2238             buttons["yes"].hide();
2239             buttons["no"].hide();
2240             //dlg.footer.dom.style.display = 'none';
2241             return width;
2242         }
2243         dlg.footer.dom.style.display = '';
2244         for(var k in buttons){
2245             if(typeof buttons[k] != "function"){
2246                 if(b[k]){
2247                     buttons[k].show();
2248                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2249                     width += buttons[k].el.getWidth()+15;
2250                 }else{
2251                     buttons[k].hide();
2252                 }
2253             }
2254         }
2255         return width;
2256     };
2257
2258     // private
2259     var handleEsc = function(d, k, e){
2260         if(opt && opt.closable !== false){
2261             dlg.hide();
2262         }
2263         if(e){
2264             e.stopEvent();
2265         }
2266     };
2267
2268     return {
2269         /**
2270          * Returns a reference to the underlying {@link Roo.BasicDialog} element
2271          * @return {Roo.BasicDialog} The BasicDialog element
2272          */
2273         getDialog : function(){
2274            if(!dlg){
2275                 dlg = new Roo.bootstrap.Modal( {
2276                     //draggable: true,
2277                     //resizable:false,
2278                     //constraintoviewport:false,
2279                     //fixedcenter:true,
2280                     //collapsible : false,
2281                     //shim:true,
2282                     //modal: true,
2283                   //  width:400,
2284                   //  height:100,
2285                     //buttonAlign:"center",
2286                     closeClick : function(){
2287                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2288                             handleButton("no");
2289                         }else{
2290                             handleButton("cancel");
2291                         }
2292                     }
2293                 });
2294                 dlg.render();
2295                 dlg.on("hide", handleHide);
2296                 mask = dlg.mask;
2297                 //dlg.addKeyListener(27, handleEsc);
2298                 buttons = {};
2299                 this.buttons = buttons;
2300                 var bt = this.buttonText;
2301                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2302                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2303                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2304                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2305                 Roo.log(buttons)
2306                 bodyEl = dlg.body.createChild({
2307
2308                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2309                         '<textarea class="roo-mb-textarea"></textarea>' +
2310                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
2311                 });
2312                 msgEl = bodyEl.dom.firstChild;
2313                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2314                 textboxEl.enableDisplayMode();
2315                 textboxEl.addKeyListener([10,13], function(){
2316                     if(dlg.isVisible() && opt && opt.buttons){
2317                         if(opt.buttons.ok){
2318                             handleButton("ok");
2319                         }else if(opt.buttons.yes){
2320                             handleButton("yes");
2321                         }
2322                     }
2323                 });
2324                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2325                 textareaEl.enableDisplayMode();
2326                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2327                 progressEl.enableDisplayMode();
2328                 var pf = progressEl.dom.firstChild;
2329                 if (pf) {
2330                     pp = Roo.get(pf.firstChild);
2331                     pp.setHeight(pf.offsetHeight);
2332                 }
2333                 
2334             }
2335             return dlg;
2336         },
2337
2338         /**
2339          * Updates the message box body text
2340          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2341          * the XHTML-compliant non-breaking space character '&amp;#160;')
2342          * @return {Roo.MessageBox} This message box
2343          */
2344         updateText : function(text){
2345             if(!dlg.isVisible() && !opt.width){
2346                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2347             }
2348             msgEl.innerHTML = text || '&#160;';
2349       
2350             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2351             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2352             var w = Math.max(
2353                     Math.min(opt.width || cw , this.maxWidth), 
2354                     Math.max(opt.minWidth || this.minWidth, bwidth)
2355             );
2356             if(opt.prompt){
2357                 activeTextEl.setWidth(w);
2358             }
2359             if(dlg.isVisible()){
2360                 dlg.fixedcenter = false;
2361             }
2362             // to big, make it scroll. = But as usual stupid IE does not support
2363             // !important..
2364             
2365             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2366                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2367                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2368             } else {
2369                 bodyEl.dom.style.height = '';
2370                 bodyEl.dom.style.overflowY = '';
2371             }
2372             if (cw > w) {
2373                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2374             } else {
2375                 bodyEl.dom.style.overflowX = '';
2376             }
2377             
2378             dlg.setContentSize(w, bodyEl.getHeight());
2379             if(dlg.isVisible()){
2380                 dlg.fixedcenter = true;
2381             }
2382             return this;
2383         },
2384
2385         /**
2386          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
2387          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2388          * @param {Number} value Any number between 0 and 1 (e.g., .5)
2389          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2390          * @return {Roo.MessageBox} This message box
2391          */
2392         updateProgress : function(value, text){
2393             if(text){
2394                 this.updateText(text);
2395             }
2396             if (pp) { // weird bug on my firefox - for some reason this is not defined
2397                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2398             }
2399             return this;
2400         },        
2401
2402         /**
2403          * Returns true if the message box is currently displayed
2404          * @return {Boolean} True if the message box is visible, else false
2405          */
2406         isVisible : function(){
2407             return dlg && dlg.isVisible();  
2408         },
2409
2410         /**
2411          * Hides the message box if it is displayed
2412          */
2413         hide : function(){
2414             if(this.isVisible()){
2415                 dlg.hide();
2416             }  
2417         },
2418
2419         /**
2420          * Displays a new message box, or reinitializes an existing message box, based on the config options
2421          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2422          * The following config object properties are supported:
2423          * <pre>
2424 Property    Type             Description
2425 ----------  ---------------  ------------------------------------------------------------------------------------
2426 animEl            String/Element   An id or Element from which the message box should animate as it opens and
2427                                    closes (defaults to undefined)
2428 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
2429                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
2430 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
2431                                    progress and wait dialogs will ignore this property and always hide the
2432                                    close button as they can only be closed programmatically.
2433 cls               String           A custom CSS class to apply to the message box element
2434 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
2435                                    displayed (defaults to 75)
2436 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
2437                                    function will be btn (the name of the button that was clicked, if applicable,
2438                                    e.g. "ok"), and text (the value of the active text field, if applicable).
2439                                    Progress and wait dialogs will ignore this option since they do not respond to
2440                                    user actions and can only be closed programmatically, so any required function
2441                                    should be called by the same code after it closes the dialog.
2442 icon              String           A CSS class that provides a background image to be used as an icon for
2443                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
2444 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
2445 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
2446 modal             Boolean          False to allow user interaction with the page while the message box is
2447                                    displayed (defaults to true)
2448 msg               String           A string that will replace the existing message box body text (defaults
2449                                    to the XHTML-compliant non-breaking space character '&#160;')
2450 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
2451 progress          Boolean          True to display a progress bar (defaults to false)
2452 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
2453 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
2454 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
2455 title             String           The title text
2456 value             String           The string value to set into the active textbox element if displayed
2457 wait              Boolean          True to display a progress bar (defaults to false)
2458 width             Number           The width of the dialog in pixels
2459 </pre>
2460          *
2461          * Example usage:
2462          * <pre><code>
2463 Roo.Msg.show({
2464    title: 'Address',
2465    msg: 'Please enter your address:',
2466    width: 300,
2467    buttons: Roo.MessageBox.OKCANCEL,
2468    multiline: true,
2469    fn: saveAddress,
2470    animEl: 'addAddressBtn'
2471 });
2472 </code></pre>
2473          * @param {Object} config Configuration options
2474          * @return {Roo.MessageBox} This message box
2475          */
2476         show : function(options)
2477         {
2478             
2479             // this causes nightmares if you show one dialog after another
2480             // especially on callbacks..
2481              
2482             if(this.isVisible()){
2483                 
2484                 this.hide();
2485                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
2486                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
2487                 Roo.log("New Dialog Message:" +  options.msg )
2488                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
2489                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
2490                 
2491             }
2492             var d = this.getDialog();
2493             opt = options;
2494             d.setTitle(opt.title || "&#160;");
2495             d.close.setDisplayed(opt.closable !== false);
2496             activeTextEl = textboxEl;
2497             opt.prompt = opt.prompt || (opt.multiline ? true : false);
2498             if(opt.prompt){
2499                 if(opt.multiline){
2500                     textboxEl.hide();
2501                     textareaEl.show();
2502                     textareaEl.setHeight(typeof opt.multiline == "number" ?
2503                         opt.multiline : this.defaultTextHeight);
2504                     activeTextEl = textareaEl;
2505                 }else{
2506                     textboxEl.show();
2507                     textareaEl.hide();
2508                 }
2509             }else{
2510                 textboxEl.hide();
2511                 textareaEl.hide();
2512             }
2513             progressEl.setDisplayed(opt.progress === true);
2514             this.updateProgress(0);
2515             activeTextEl.dom.value = opt.value || "";
2516             if(opt.prompt){
2517                 dlg.setDefaultButton(activeTextEl);
2518             }else{
2519                 var bs = opt.buttons;
2520                 var db = null;
2521                 if(bs && bs.ok){
2522                     db = buttons["ok"];
2523                 }else if(bs && bs.yes){
2524                     db = buttons["yes"];
2525                 }
2526                 dlg.setDefaultButton(db);
2527             }
2528             bwidth = updateButtons(opt.buttons);
2529             this.updateText(opt.msg);
2530             if(opt.cls){
2531                 d.el.addClass(opt.cls);
2532             }
2533             d.proxyDrag = opt.proxyDrag === true;
2534             d.modal = opt.modal !== false;
2535             d.mask = opt.modal !== false ? mask : false;
2536             if(!d.isVisible()){
2537                 // force it to the end of the z-index stack so it gets a cursor in FF
2538                 document.body.appendChild(dlg.el.dom);
2539                 d.animateTarget = null;
2540                 d.show(options.animEl);
2541             }
2542             return this;
2543         },
2544
2545         /**
2546          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
2547          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
2548          * and closing the message box when the process is complete.
2549          * @param {String} title The title bar text
2550          * @param {String} msg The message box body text
2551          * @return {Roo.MessageBox} This message box
2552          */
2553         progress : function(title, msg){
2554             this.show({
2555                 title : title,
2556                 msg : msg,
2557                 buttons: false,
2558                 progress:true,
2559                 closable:false,
2560                 minWidth: this.minProgressWidth,
2561                 modal : true
2562             });
2563             return this;
2564         },
2565
2566         /**
2567          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
2568          * If a callback function is passed it will be called after the user clicks the button, and the
2569          * id of the button that was clicked will be passed as the only parameter to the callback
2570          * (could also be the top-right close button).
2571          * @param {String} title The title bar text
2572          * @param {String} msg The message box body text
2573          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2574          * @param {Object} scope (optional) The scope of the callback function
2575          * @return {Roo.MessageBox} This message box
2576          */
2577         alert : function(title, msg, fn, scope){
2578             this.show({
2579                 title : title,
2580                 msg : msg,
2581                 buttons: this.OK,
2582                 fn: fn,
2583                 scope : scope,
2584                 modal : true
2585             });
2586             return this;
2587         },
2588
2589         /**
2590          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
2591          * interaction while waiting for a long-running process to complete that does not have defined intervals.
2592          * You are responsible for closing the message box when the process is complete.
2593          * @param {String} msg The message box body text
2594          * @param {String} title (optional) The title bar text
2595          * @return {Roo.MessageBox} This message box
2596          */
2597         wait : function(msg, title){
2598             this.show({
2599                 title : title,
2600                 msg : msg,
2601                 buttons: false,
2602                 closable:false,
2603                 progress:true,
2604                 modal:true,
2605                 width:300,
2606                 wait:true
2607             });
2608             waitTimer = Roo.TaskMgr.start({
2609                 run: function(i){
2610                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
2611                 },
2612                 interval: 1000
2613             });
2614             return this;
2615         },
2616
2617         /**
2618          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
2619          * If a callback function is passed it will be called after the user clicks either button, and the id of the
2620          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
2621          * @param {String} title The title bar text
2622          * @param {String} msg The message box body text
2623          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2624          * @param {Object} scope (optional) The scope of the callback function
2625          * @return {Roo.MessageBox} This message box
2626          */
2627         confirm : function(title, msg, fn, scope){
2628             this.show({
2629                 title : title,
2630                 msg : msg,
2631                 buttons: this.YESNO,
2632                 fn: fn,
2633                 scope : scope,
2634                 modal : true
2635             });
2636             return this;
2637         },
2638
2639         /**
2640          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
2641          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
2642          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
2643          * (could also be the top-right close button) and the text that was entered will be passed as the two
2644          * parameters to the callback.
2645          * @param {String} title The title bar text
2646          * @param {String} msg The message box body text
2647          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2648          * @param {Object} scope (optional) The scope of the callback function
2649          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
2650          * property, or the height in pixels to create the textbox (defaults to false / single-line)
2651          * @return {Roo.MessageBox} This message box
2652          */
2653         prompt : function(title, msg, fn, scope, multiline){
2654             this.show({
2655                 title : title,
2656                 msg : msg,
2657                 buttons: this.OKCANCEL,
2658                 fn: fn,
2659                 minWidth:250,
2660                 scope : scope,
2661                 prompt:true,
2662                 multiline: multiline,
2663                 modal : true
2664             });
2665             return this;
2666         },
2667
2668         /**
2669          * Button config that displays a single OK button
2670          * @type Object
2671          */
2672         OK : {ok:true},
2673         /**
2674          * Button config that displays Yes and No buttons
2675          * @type Object
2676          */
2677         YESNO : {yes:true, no:true},
2678         /**
2679          * Button config that displays OK and Cancel buttons
2680          * @type Object
2681          */
2682         OKCANCEL : {ok:true, cancel:true},
2683         /**
2684          * Button config that displays Yes, No and Cancel buttons
2685          * @type Object
2686          */
2687         YESNOCANCEL : {yes:true, no:true, cancel:true},
2688
2689         /**
2690          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
2691          * @type Number
2692          */
2693         defaultTextHeight : 75,
2694         /**
2695          * The maximum width in pixels of the message box (defaults to 600)
2696          * @type Number
2697          */
2698         maxWidth : 600,
2699         /**
2700          * The minimum width in pixels of the message box (defaults to 100)
2701          * @type Number
2702          */
2703         minWidth : 100,
2704         /**
2705          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
2706          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
2707          * @type Number
2708          */
2709         minProgressWidth : 250,
2710         /**
2711          * An object containing the default button text strings that can be overriden for localized language support.
2712          * Supported properties are: ok, cancel, yes and no.
2713          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
2714          * @type Object
2715          */
2716         buttonText : {
2717             ok : "OK",
2718             cancel : "Cancel",
2719             yes : "Yes",
2720             no : "No"
2721         }
2722     };
2723 }();
2724
2725 /**
2726  * Shorthand for {@link Roo.MessageBox}
2727  */
2728 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox 
2729 Roo.Msg = Roo.Msg || Roo.MessageBox;
2730 /*
2731  * - LGPL
2732  *
2733  * navbar
2734  * 
2735  */
2736
2737 /**
2738  * @class Roo.bootstrap.Navbar
2739  * @extends Roo.bootstrap.Component
2740  * Bootstrap Navbar class
2741
2742  * @constructor
2743  * Create a new Navbar
2744  * @param {Object} config The config object
2745  */
2746
2747
2748 Roo.bootstrap.Navbar = function(config){
2749     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
2750     
2751 };
2752
2753 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
2754     
2755     
2756    
2757     // private
2758     navItems : false,
2759     loadMask : false,
2760     
2761     
2762     getAutoCreate : function(){
2763         
2764         
2765         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
2766         
2767     },
2768     
2769     initEvents :function ()
2770     {
2771         //Roo.log(this.el.select('.navbar-toggle',true));
2772         this.el.select('.navbar-toggle',true).on('click', function() {
2773            // Roo.log('click');
2774             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
2775         }, this);
2776         
2777         var mark = {
2778             tag: "div",
2779             cls:"x-dlg-mask"
2780         }
2781         
2782         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
2783         
2784         var size = this.el.getSize();
2785         this.maskEl.setSize(size.width, size.height);
2786         this.maskEl.enableDisplayMode("block");
2787         this.maskEl.hide();
2788         
2789         if(this.loadMask){
2790             this.maskEl.show();
2791         }
2792     },
2793     
2794     
2795     getChildContainer : function()
2796     {
2797         if (this.el.select('.collapse').getCount()) {
2798             return this.el.select('.collapse',true).first();
2799         }
2800         
2801         return this.el;
2802     },
2803     
2804     mask : function()
2805     {
2806         this.maskEl.show();
2807     },
2808     
2809     unmask : function()
2810     {
2811         this.maskEl.hide();
2812     }
2813     
2814     
2815     
2816 });
2817
2818
2819
2820  
2821
2822  /*
2823  * - LGPL
2824  *
2825  * navbar
2826  * 
2827  */
2828
2829 /**
2830  * @class Roo.bootstrap.NavSimplebar
2831  * @extends Roo.bootstrap.Navbar
2832  * Bootstrap Sidebar class
2833  *
2834  * @cfg {Boolean} inverse is inverted color
2835  * 
2836  * @cfg {String} type (nav | pills | tabs)
2837  * @cfg {Boolean} arrangement stacked | justified
2838  * @cfg {String} align (left | right) alignment
2839  * 
2840  * @cfg {Boolean} main (true|false) main nav bar? default false
2841  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
2842  * 
2843  * @cfg {String} tag (header|footer|nav|div) default is nav 
2844
2845  * 
2846  * 
2847  * 
2848  * @constructor
2849  * Create a new Sidebar
2850  * @param {Object} config The config object
2851  */
2852
2853
2854 Roo.bootstrap.NavSimplebar = function(config){
2855     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
2856 };
2857
2858 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
2859     
2860     inverse: false,
2861     
2862     type: false,
2863     arrangement: '',
2864     align : false,
2865     
2866     
2867     
2868     main : false,
2869     
2870     
2871     tag : false,
2872     
2873     
2874     getAutoCreate : function(){
2875         
2876         
2877         var cfg = {
2878             tag : this.tag || 'div',
2879             cls : 'navbar'
2880         };
2881           
2882         
2883         cfg.cn = [
2884             {
2885                 cls: 'nav',
2886                 tag : 'ul'
2887             }
2888         ];
2889         
2890          
2891         this.type = this.type || 'nav';
2892         if (['tabs','pills'].indexOf(this.type)!==-1) {
2893             cfg.cn[0].cls += ' nav-' + this.type
2894         
2895         
2896         } else {
2897             if (this.type!=='nav') {
2898                 Roo.log('nav type must be nav/tabs/pills')
2899             }
2900             cfg.cn[0].cls += ' navbar-nav'
2901         }
2902         
2903         
2904         
2905         
2906         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
2907             cfg.cn[0].cls += ' nav-' + this.arrangement;
2908         }
2909         
2910         
2911         if (this.align === 'right') {
2912             cfg.cn[0].cls += ' navbar-right';
2913         }
2914         
2915         if (this.inverse) {
2916             cfg.cls += ' navbar-inverse';
2917             
2918         }
2919         
2920         
2921         return cfg;
2922     
2923         
2924     }
2925     
2926     
2927     
2928 });
2929
2930
2931
2932  
2933
2934  
2935        /*
2936  * - LGPL
2937  *
2938  * navbar
2939  * 
2940  */
2941
2942 /**
2943  * @class Roo.bootstrap.NavHeaderbar
2944  * @extends Roo.bootstrap.NavSimplebar
2945  * Bootstrap Sidebar class
2946  *
2947  * @cfg {String} brand what is brand
2948  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
2949  * @cfg {String} brand_href href of the brand
2950  * 
2951  * @constructor
2952  * Create a new Sidebar
2953  * @param {Object} config The config object
2954  */
2955
2956
2957 Roo.bootstrap.NavHeaderbar = function(config){
2958     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
2959 };
2960
2961 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
2962     
2963     position: '',
2964     brand: '',
2965     brand_href: false,
2966     
2967     
2968     getAutoCreate : function(){
2969         
2970         
2971         
2972         var   cfg = {
2973             tag: this.nav || 'nav',
2974             cls: 'navbar',
2975             role: 'navigation',
2976             cn: [
2977                 {
2978                     tag: 'div',
2979                     cls: 'navbar-header',
2980                     cn: [
2981                         {
2982                         tag: 'button',
2983                         type: 'button',
2984                         cls: 'navbar-toggle',
2985                         'data-toggle': 'collapse',
2986                         cn: [
2987                             {
2988                                 tag: 'span',
2989                                 cls: 'sr-only',
2990                                 html: 'Toggle navigation'
2991                             },
2992                             {
2993                                 tag: 'span',
2994                                 cls: 'icon-bar'
2995                             },
2996                             {
2997                                 tag: 'span',
2998                                 cls: 'icon-bar'
2999                             },
3000                             {
3001                                 tag: 'span',
3002                                 cls: 'icon-bar'
3003                             }
3004                         ]
3005                         }
3006                     ]
3007                 },
3008                 {
3009                 tag: 'div',
3010                 cls: 'collapse navbar-collapse'
3011                 }
3012             ]
3013         };
3014         
3015         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3016         
3017         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3018             cfg.cls += ' navbar-' + this.position;
3019             
3020             // tag can override this..
3021             
3022             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3023         }
3024         
3025         if (this.brand !== '') {
3026             cfg.cn[0].cn.push({
3027                 tag: 'a',
3028                 href: this.brand_href ? this.brand_href : '#',
3029                 cls: 'navbar-brand',
3030                 cn: [
3031                 this.brand
3032                 ]
3033             });
3034         }
3035         
3036         if(this.main){
3037             cfg.cls += ' main-nav';
3038         }
3039         
3040         
3041         return cfg;
3042
3043         
3044     }
3045     
3046     
3047     
3048 });
3049
3050
3051
3052  
3053
3054  /*
3055  * - LGPL
3056  *
3057  * navbar
3058  * 
3059  */
3060
3061 /**
3062  * @class Roo.bootstrap.NavSidebar
3063  * @extends Roo.bootstrap.Navbar
3064  * Bootstrap Sidebar class
3065  * 
3066  * @constructor
3067  * Create a new Sidebar
3068  * @param {Object} config The config object
3069  */
3070
3071
3072 Roo.bootstrap.NavSidebar = function(config){
3073     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3074 };
3075
3076 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3077     
3078     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3079     
3080     getAutoCreate : function(){
3081         
3082         
3083         return  {
3084             tag: 'div',
3085             cls: 'sidebar sidebar-nav'
3086         };
3087     
3088         
3089     }
3090     
3091     
3092     
3093 });
3094
3095
3096
3097  
3098
3099  /*
3100  * - LGPL
3101  *
3102  * nav group
3103  * 
3104  */
3105
3106 /**
3107  * @class Roo.bootstrap.NavGroup
3108  * @extends Roo.bootstrap.Component
3109  * Bootstrap NavGroup class
3110  * @cfg {String} align left | right
3111  * @cfg {Boolean} inverse false | true
3112  * @cfg {String} type (nav|pills|tab) default nav
3113  * @cfg {String} navId - reference Id for navbar.
3114
3115  * 
3116  * @constructor
3117  * Create a new nav group
3118  * @param {Object} config The config object
3119  */
3120
3121 Roo.bootstrap.NavGroup = function(config){
3122     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3123     this.navItems = [];
3124     Roo.bootstrap.NavGroup.register(this);
3125      this.addEvents({
3126         /**
3127              * @event changed
3128              * Fires when the active item changes
3129              * @param {Roo.bootstrap.NavGroup} this
3130              * @param {Roo.bootstrap.Navbar.Item} item The item selected
3131              * @param {Roo.bootstrap.Navbar.Item} item The previously selected item 
3132          */
3133         'changed': true
3134      });
3135     
3136 };
3137
3138 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3139     
3140     align: '',
3141     inverse: false,
3142     form: false,
3143     type: 'nav',
3144     navId : '',
3145     // private
3146     
3147     navItems : false,
3148     
3149     getAutoCreate : function()
3150     {
3151         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3152         
3153         cfg = {
3154             tag : 'ul',
3155             cls: 'nav' 
3156         }
3157         
3158         if (['tabs','pills'].indexOf(this.type)!==-1) {
3159             cfg.cls += ' nav-' + this.type
3160         } else {
3161             if (this.type!=='nav') {
3162                 Roo.log('nav type must be nav/tabs/pills')
3163             }
3164             cfg.cls += ' navbar-nav'
3165         }
3166         
3167         if (this.parent().sidebar) {
3168             cfg = {
3169                 tag: 'ul',
3170                 cls: 'dashboard-menu sidebar-menu'
3171             }
3172             
3173             return cfg;
3174         }
3175         
3176         if (this.form === true) {
3177             cfg = {
3178                 tag: 'form',
3179                 cls: 'navbar-form'
3180             }
3181             
3182             if (this.align === 'right') {
3183                 cfg.cls += ' navbar-right';
3184             } else {
3185                 cfg.cls += ' navbar-left';
3186             }
3187         }
3188         
3189         if (this.align === 'right') {
3190             cfg.cls += ' navbar-right';
3191         }
3192         
3193         if (this.inverse) {
3194             cfg.cls += ' navbar-inverse';
3195             
3196         }
3197         
3198         
3199         return cfg;
3200     },
3201     
3202     setActiveItem : function(item)
3203     {
3204         var prev = false;
3205         Roo.each(this.navItems, function(v){
3206             if (v == item) {
3207                 return ;
3208             }
3209             if (v.isActive()) {
3210                 v.setActive(false, true);
3211                 prev = v;
3212                 
3213             }
3214             
3215         });
3216
3217         item.setActive(true, true);
3218         this.fireEvent('changed', this, item, prev);
3219         
3220         
3221     },
3222     
3223     
3224     register : function(item)
3225     {
3226         this.navItems.push( item);
3227         item.navId = this.navId;
3228     
3229     },
3230     getNavItem: function(tabId)
3231     {
3232         var ret = false;
3233         Roo.each(this.navItems, function(e) {
3234             if (e.tabId == tabId) {
3235                ret =  e;
3236                return false;
3237             }
3238             return true;
3239             
3240         });
3241         return ret;
3242     }
3243 });
3244
3245  
3246 Roo.apply(Roo.bootstrap.NavGroup, {
3247     
3248     groups: {},
3249     
3250     register : function(navgrp)
3251     {
3252         this.groups[navgrp.navId] = navgrp;
3253         
3254     },
3255     get: function(navId) {
3256         return this.groups[navId];
3257     }
3258     
3259     
3260     
3261 });
3262
3263  /*
3264  * - LGPL
3265  *
3266  * row
3267  * 
3268  */
3269
3270 /**
3271  * @class Roo.bootstrap.Navbar.Item
3272  * @extends Roo.bootstrap.Component
3273  * Bootstrap Navbar.Button class
3274  * @cfg {String} href  link to
3275  * @cfg {String} html content of button
3276  * @cfg {String} badge text inside badge
3277  * @cfg {String} glyphicon name of glyphicon
3278  * @cfg {String} icon name of font awesome icon
3279  * @cfg {Boolean} active Is item active
3280  * @cfg {Boolean} preventDefault (true | false) default false
3281  * @cfg {String} tabId the tab that this item activates.
3282   
3283  * @constructor
3284  * Create a new Navbar Button
3285  * @param {Object} config The config object
3286  */
3287 Roo.bootstrap.Navbar.Item = function(config){
3288     Roo.bootstrap.Navbar.Item.superclass.constructor.call(this, config);
3289     this.addEvents({
3290         // raw events
3291         /**
3292          * @event click
3293          * The raw click event for the entire grid.
3294          * @param {Roo.EventObject} e
3295          */
3296         "click" : true,
3297          /**
3298             * @event changed
3299             * Fires when the active item active state changes
3300             * @param {Roo.bootstrap.Navbar.Item} this
3301             * @param {boolean} state the new state
3302              
3303          */
3304         'changed': true
3305     });
3306    
3307 };
3308
3309 Roo.extend(Roo.bootstrap.Navbar.Item, Roo.bootstrap.Component,  {
3310     
3311     href: false,
3312     html: '',
3313     badge: '',
3314     icon: false,
3315     glyphicon: false,
3316     active: false,
3317     preventDefault : false,
3318     tabId : false,
3319     
3320     getAutoCreate : function(){
3321         
3322         var cfg = Roo.apply({}, Roo.bootstrap.Navbar.Item.superclass.getAutoCreate.call(this));
3323         
3324         if (this.parent().parent().sidebar === true) {
3325             cfg = {
3326                 tag: 'li',
3327                 cls: '',
3328                 cn: [
3329                     {
3330                     tag: 'p',
3331                     cls: ''
3332                     }
3333                 ]
3334             }
3335             
3336             if (this.html) {
3337                 cfg.cn[0].html = this.html;
3338             }
3339             
3340             if (this.active) {
3341                 this.cls += ' active';
3342             }
3343             
3344             if (this.menu) {
3345                 cfg.cn[0].cls += ' dropdown-toggle';
3346                 cfg.cn[0].html = (cfg.cn[0].html || this.html) + '<span class="glyphicon glyphicon-chevron-down"></span>';
3347             }
3348             
3349             if (this.href) {
3350                 cfg.cn[0].tag = 'a',
3351                 cfg.cn[0].href = this.href;
3352             }
3353             
3354             if (this.glyphicon) {
3355                 cfg.cn[0].html = '<i class="glyphicon glyphicon-'+this.glyphicon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
3356             }
3357                 
3358             if (this.icon) {
3359                 cfg.cn[0].html = '<i class="'+this.icon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
3360             }
3361             
3362             return cfg;
3363         }
3364         
3365         cfg = {
3366             tag: 'li',
3367                 cls: 'nav-item'
3368         }
3369             
3370         if (this.active) {
3371             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
3372         }
3373             
3374         cfg.cn = [
3375             {
3376                 tag: 'p',
3377                 html: 'Text'
3378             }
3379         ];
3380         
3381         if (this.glyphicon) {
3382             if(cfg.html){cfg.html = ' ' + this.html};
3383             cfg.cn=[
3384                 {
3385                     tag: 'span',
3386                     cls: 'glyphicon glyphicon-' + this.glyphicon
3387                 }
3388             ];
3389         }
3390         
3391         cfg.cn[0].html = this.html || cfg.cn[0].html ;
3392         
3393         if (this.menu) {
3394             cfg.cn[0].tag='a';
3395             cfg.cn[0].href='#';
3396             cfg.cn[0].html += " <span class='caret'></span>";
3397         //}else if (!this.href) {
3398         //    cfg.cn[0].tag='p';
3399         //    cfg.cn[0].cls='navbar-text';
3400         } else {
3401             cfg.cn[0].tag='a';
3402             cfg.cn[0].href=this.href||'#';
3403             cfg.cn[0].html=this.html;
3404         }
3405         
3406         if (this.badge !== '') {
3407             
3408             cfg.cn[0].cn=[
3409             cfg.cn[0].html + ' ',
3410             {
3411                 tag: 'span',
3412                 cls: 'badge',
3413                 html: this.badge
3414             }
3415             ];
3416             cfg.cn[0].html=''
3417         }
3418          
3419         if (this.icon) {
3420             cfg.cn[0].html = '<i class="'+this.icon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
3421         }
3422         
3423         return cfg;
3424     },
3425     initEvents: function() {
3426        // Roo.log('init events?');
3427        // Roo.log(this.el.dom);
3428         this.el.select('a',true).on('click', this.onClick, this);
3429         // at this point parent should be available..
3430         this.parent().register(this);
3431     },
3432     
3433     onClick : function(e)
3434     {
3435         if(this.preventDefault){
3436             e.preventDefault();
3437         }
3438         
3439         if(this.fireEvent('click', this, e) === false){
3440             return;
3441         };
3442         
3443         if (['tabs','pills'].indexOf(this.parent().type)!==-1) {
3444              if (typeof(this.parent().setActiveItem) !== 'undefined') {
3445                 this.parent().setActiveItem(this);
3446             }
3447             
3448             
3449             
3450         } 
3451     },
3452     
3453     isActive: function () {
3454         return this.active
3455     },
3456     setActive : function(state, fire)
3457     {
3458         this.active = state;
3459         if (!state ) {
3460             this.el.removeClass('active');
3461         } else if (!this.el.hasClass('active')) {
3462             this.el.addClass('active');
3463         }
3464         if (fire) {
3465             this.fireEvent('changed', this, state);
3466         }
3467         
3468         
3469     }
3470      // this should not be here...
3471  
3472 });
3473  
3474
3475  /*
3476  * - LGPL
3477  *
3478  * row
3479  * 
3480  */
3481
3482 /**
3483  * @class Roo.bootstrap.NavItem
3484  * @extends Roo.bootstrap.Component
3485  * Bootstrap Navbar.NavItem class
3486  * @cfg {String} href  link to
3487  * @cfg {String} html content of button
3488  * @cfg {String} badge text inside badge
3489  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
3490  * @cfg {String} glyphicon name of glyphicon
3491  * @cfg {String} icon name of font awesome icon
3492  * @cfg {Boolean} active Is item active
3493  * @cfg {Boolean} preventDefault (true | false) default false
3494  * @cfg {String} tabId the tab that this item activates.
3495   
3496  * @constructor
3497  * Create a new Navbar Item
3498  * @param {Object} config The config object
3499  */
3500 Roo.bootstrap.NavItem = function(config){
3501     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
3502     this.addEvents({
3503         // raw events
3504         /**
3505          * @event click
3506          * The raw click event for the entire grid.
3507          * @param {Roo.EventObject} e
3508          */
3509         "click" : true,
3510          /**
3511             * @event changed
3512             * Fires when the active item active state changes
3513             * @param {Roo.bootstrap.NavItem} this
3514             * @param {boolean} state the new state
3515              
3516          */
3517         'changed': true
3518     });
3519    
3520 };
3521
3522 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
3523     
3524     href: false,
3525     html: '',
3526     badge: '',
3527     icon: false,
3528     glyphicon: false,
3529     active: false,
3530     preventDefault : false,
3531     tabId : false,
3532     
3533     getAutoCreate : function(){
3534          
3535         var cfg = {
3536             tag: 'li',
3537             cls: 'nav-item',
3538             cn : [
3539                 {
3540                     tag: 'a',
3541                     href : this.href || "#",
3542                     html: this.html || ''
3543                 }
3544             ]
3545         }
3546             
3547         if (this.active) {
3548             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
3549         }
3550             
3551         // glyphicon and icon go before content..
3552         if (this.glyphicon || this.icon) {
3553              if (this.icon) {
3554                 cfg.cn[0].html = '<i class="'+this.icon+'"></i><span>' + cfg.cn[0].html + '</span>'
3555             } else {
3556                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span>'  + cfg.cn[0].html;
3557             }
3558         }
3559         
3560         
3561         
3562         if (this.menu) {
3563             
3564             cfg.cn[0].html += " <span class='caret'></span>";
3565          
3566         }
3567         
3568         if (this.badge !== '') {
3569              
3570             cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
3571         }
3572         
3573         
3574         
3575         return cfg;
3576     },
3577     initEvents: function() {
3578        // Roo.log('init events?');
3579        // Roo.log(this.el.dom);
3580         this.el.select('a',true).on('click', this.onClick, this);
3581         // at this point parent should be available..
3582         this.parent().register(this);
3583     },
3584     
3585     onClick : function(e)
3586     {
3587         if(this.preventDefault){
3588             e.preventDefault();
3589         }
3590         
3591         if(this.fireEvent('click', this, e) === false){
3592             return;
3593         };
3594         
3595         if (['tabs','pills'].indexOf(this.parent().type)!==-1) {
3596              if (typeof(this.parent().setActiveItem) !== 'undefined') {
3597                 this.parent().setActiveItem(this);
3598             }
3599             
3600             
3601             
3602         } 
3603     },
3604     
3605     isActive: function () {
3606         return this.active
3607     },
3608     setActive : function(state, fire)
3609     {
3610         this.active = state;
3611         if (!state ) {
3612             this.el.removeClass('active');
3613         } else if (!this.el.hasClass('active')) {
3614             this.el.addClass('active');
3615         }
3616         if (fire) {
3617             this.fireEvent('changed', this, state);
3618         }
3619         
3620         
3621     }
3622      // this should not be here...
3623  
3624 });
3625  
3626
3627  /*
3628  * - LGPL
3629  *
3630  * sidebar item
3631  *
3632  *  li
3633  *    <span> icon </span>
3634  *    <span> text </span>
3635  *    <span>badge </span>
3636  */
3637
3638 /**
3639  * @class Roo.bootstrap.NavSidebarItem
3640  * @extends Roo.bootstrap.Component
3641  * Bootstrap Navbar.NavSidebarItem class
3642  * @constructor
3643  * Create a new Navbar Button
3644  * @param {Object} config The config object
3645  */
3646 Roo.bootstrap.NavSidebarItem = function(config){
3647     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
3648     this.addEvents({
3649         // raw events
3650         /**
3651          * @event click
3652          * The raw click event for the entire grid.
3653          * @param {Roo.EventObject} e
3654          */
3655         "click" : true,
3656          /**
3657             * @event changed
3658             * Fires when the active item active state changes
3659             * @param {Roo.bootstrap.Navbar.Item} this
3660             * @param {boolean} state the new state
3661              
3662          */
3663         'changed': true
3664     });
3665    
3666 };
3667
3668 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
3669     
3670     
3671     getAutoCreate : function(){
3672         
3673         
3674         var a = {
3675                 tag: 'a',
3676                 href : this.href || '#',
3677                 cls: '',
3678                 html : '',
3679                 cn : []
3680         };
3681         var cfg = {
3682             tag: 'li',
3683             cls: '',
3684             cn: [ a ]
3685         }
3686         var span = {
3687             tag: 'span',
3688             html : this.html || ''
3689         }
3690         
3691         
3692         if (this.active) {
3693             cfg.cls += ' active';
3694         }
3695         var right = false;
3696         // fixme - this should handle glyphicon or ico
3697         if (this.glyphicon || this.icon) {
3698             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
3699             right = { tag : 'i', cls : c };
3700             a.cn.push(right) ;
3701         }
3702         a.cn.push(span);
3703         
3704         if (this.badge !== '') {
3705             a.push({ tag: 'span',  cls : 'badge pull-right ' + (this.badgecls || ''), html: this.badge }); 
3706         }
3707         
3708         if (this.menu) {
3709             right = right || { tag : 'i', cls : '' };
3710             a.cls += ' dropdown-toggle treeview';
3711             right.cls += 'glyphicon glyphicon-chevron-down';
3712         }
3713         
3714         
3715         
3716         return cfg;
3717          
3718            
3719     }
3720    
3721      
3722  
3723 });
3724  
3725
3726  /*
3727  * - LGPL
3728  *
3729  * row
3730  * 
3731  */
3732
3733 /**
3734  * @class Roo.bootstrap.Row
3735  * @extends Roo.bootstrap.Component
3736  * Bootstrap Row class (contains columns...)
3737  * 
3738  * @constructor
3739  * Create a new Row
3740  * @param {Object} config The config object
3741  */
3742
3743 Roo.bootstrap.Row = function(config){
3744     Roo.bootstrap.Row.superclass.constructor.call(this, config);
3745 };
3746
3747 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
3748     
3749     getAutoCreate : function(){
3750        return {
3751             cls: 'row clearfix'
3752        };
3753     }
3754     
3755     
3756 });
3757
3758  
3759
3760  /*
3761  * - LGPL
3762  *
3763  * element
3764  * 
3765  */
3766
3767 /**
3768  * @class Roo.bootstrap.Element
3769  * @extends Roo.bootstrap.Component
3770  * Bootstrap Element class
3771  * @cfg {String} html contents of the element
3772  * @cfg {String} tag tag of the element
3773  * @cfg {String} cls class of the element
3774  * 
3775  * @constructor
3776  * Create a new Element
3777  * @param {Object} config The config object
3778  */
3779
3780 Roo.bootstrap.Element = function(config){
3781     Roo.bootstrap.Element.superclass.constructor.call(this, config);
3782 };
3783
3784 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
3785     
3786     tag: 'div',
3787     cls: '',
3788     html: '',
3789      
3790     
3791     getAutoCreate : function(){
3792         
3793         var cfg = {
3794             tag: this.tag,
3795             cls: this.cls,
3796             html: this.html
3797         }
3798         
3799         
3800         
3801         return cfg;
3802     }
3803    
3804 });
3805
3806  
3807
3808  /*
3809  * - LGPL
3810  *
3811  * pagination
3812  * 
3813  */
3814
3815 /**
3816  * @class Roo.bootstrap.Pagination
3817  * @extends Roo.bootstrap.Component
3818  * Bootstrap Pagination class
3819  * @cfg {String} size xs | sm | md | lg
3820  * @cfg {Boolean} inverse false | true
3821  * 
3822  * @constructor
3823  * Create a new Pagination
3824  * @param {Object} config The config object
3825  */
3826
3827 Roo.bootstrap.Pagination = function(config){
3828     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
3829 };
3830
3831 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
3832     
3833     cls: false,
3834     size: false,
3835     inverse: false,
3836     
3837     getAutoCreate : function(){
3838         var cfg = {
3839             tag: 'ul',
3840                 cls: 'pagination'
3841         };
3842         if (this.inverse) {
3843             cfg.cls += ' inverse';
3844         }
3845         if (this.html) {
3846             cfg.html=this.html;
3847         }
3848         if (this.cls) {
3849             cfg.cls += " " + this.cls;
3850         }
3851         return cfg;
3852     }
3853    
3854 });
3855
3856  
3857
3858  /*
3859  * - LGPL
3860  *
3861  * Pagination item
3862  * 
3863  */
3864
3865
3866 /**
3867  * @class Roo.bootstrap.PaginationItem
3868  * @extends Roo.bootstrap.Component
3869  * Bootstrap PaginationItem class
3870  * @cfg {String} html text
3871  * @cfg {String} href the link
3872  * @cfg {Boolean} preventDefault (true | false) default true
3873  * @cfg {Boolean} active (true | false) default false
3874  * 
3875  * 
3876  * @constructor
3877  * Create a new PaginationItem
3878  * @param {Object} config The config object
3879  */
3880
3881
3882 Roo.bootstrap.PaginationItem = function(config){
3883     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
3884     this.addEvents({
3885         // raw events
3886         /**
3887          * @event click
3888          * The raw click event for the entire grid.
3889          * @param {Roo.EventObject} e
3890          */
3891         "click" : true
3892     });
3893 };
3894
3895 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
3896     
3897     href : false,
3898     html : false,
3899     preventDefault: true,
3900     active : false,
3901     cls : false,
3902     
3903     getAutoCreate : function(){
3904         var cfg= {
3905             tag: 'li',
3906             cn: [
3907                 {
3908                     tag : 'a',
3909                     href : this.href ? this.href : '#',
3910                     html : this.html ? this.html : ''
3911                 }
3912             ]
3913         };
3914         
3915         if(this.cls){
3916             cfg.cls = this.cls;
3917         }
3918         
3919         if(this.active){
3920             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
3921         }
3922         
3923         return cfg;
3924     },
3925     
3926     initEvents: function() {
3927         
3928         this.el.on('click', this.onClick, this);
3929         
3930     },
3931     onClick : function(e)
3932     {
3933         Roo.log('PaginationItem on click ');
3934         if(this.preventDefault){
3935             e.preventDefault();
3936         }
3937         
3938         this.fireEvent('click', this, e);
3939     }
3940    
3941 });
3942
3943  
3944
3945  /*
3946  * - LGPL
3947  *
3948  * slider
3949  * 
3950  */
3951
3952
3953 /**
3954  * @class Roo.bootstrap.Slider
3955  * @extends Roo.bootstrap.Component
3956  * Bootstrap Slider class
3957  *    
3958  * @constructor
3959  * Create a new Slider
3960  * @param {Object} config The config object
3961  */
3962
3963 Roo.bootstrap.Slider = function(config){
3964     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
3965 };
3966
3967 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
3968     
3969     getAutoCreate : function(){
3970         
3971         var cfg = {
3972             tag: 'div',
3973             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
3974             cn: [
3975                 {
3976                     tag: 'a',
3977                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
3978                 }
3979             ]
3980         }
3981         
3982         return cfg;
3983     }
3984    
3985 });
3986
3987  /*
3988  * - LGPL
3989  *
3990  * table
3991  * 
3992  */
3993
3994 /**
3995  * @class Roo.bootstrap.Table
3996  * @extends Roo.bootstrap.Component
3997  * Bootstrap Table class
3998  * @cfg {String} cls table class
3999  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
4000  * @cfg {String} bgcolor Specifies the background color for a table
4001  * @cfg {Number} border Specifies whether the table cells should have borders or not
4002  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
4003  * @cfg {Number} cellspacing Specifies the space between cells
4004  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
4005  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
4006  * @cfg {String} sortable Specifies that the table should be sortable
4007  * @cfg {String} summary Specifies a summary of the content of a table
4008  * @cfg {Number} width Specifies the width of a table
4009  * 
4010  * @cfg {boolean} striped Should the rows be alternative striped
4011  * @cfg {boolean} bordered Add borders to the table
4012  * @cfg {boolean} hover Add hover highlighting
4013  * @cfg {boolean} condensed Format condensed
4014  * @cfg {boolean} responsive Format condensed
4015  *
4016  
4017  
4018  * 
4019  * @constructor
4020  * Create a new Table
4021  * @param {Object} config The config object
4022  */
4023
4024 Roo.bootstrap.Table = function(config){
4025     Roo.bootstrap.Table.superclass.constructor.call(this, config);
4026     
4027     if (this.sm) {
4028         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
4029         this.sm = this.selModel;
4030         this.sm.xmodule = this.xmodule || false;
4031     }
4032     if (this.cm && typeof(this.cm.config) == 'undefined') {
4033         this.colModel = new Roo.bootstrap.Table.ColumnModel(this.cm);
4034         this.cm = this.colModel;
4035         this.cm.xmodule = this.xmodule || false;
4036     }
4037     if (this.store) {
4038         this.store= Roo.factory(this.store, Roo.data);
4039         this.ds = this.store;
4040         this.ds.xmodule = this.xmodule || false;
4041          
4042     }
4043 };
4044
4045 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
4046     
4047     cls: false,
4048     align: false,
4049     bgcolor: false,
4050     border: false,
4051     cellpadding: false,
4052     cellspacing: false,
4053     frame: false,
4054     rules: false,
4055     sortable: false,
4056     summary: false,
4057     width: false,
4058     striped : false,
4059     bordered: false,
4060     hover:  false,
4061     condensed : false,
4062     responsive : false,
4063     sm : false,
4064     cm : false,
4065     store : false,
4066     
4067     getAutoCreate : function(){
4068         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
4069         
4070         cfg = {
4071             tag: 'table',
4072             cls : 'table',
4073             cn : []
4074         }
4075             
4076         if (this.striped) {
4077             cfg.cls += ' table-striped';
4078         }
4079         if (this.hover) {
4080             cfg.cls += ' table-hover';
4081         }
4082         if (this.bordered) {
4083             cfg.cls += ' table-bordered';
4084         }
4085         if (this.condensed) {
4086             cfg.cls += ' table-condensed';
4087         }
4088         if (this.responsive) {
4089             cfg.cls += ' table-responsive';
4090         }
4091         
4092           
4093         
4094         
4095         if (this.cls) {
4096             cfg.cls+=  ' ' +this.cls;
4097         }
4098         
4099         // this lot should be simplifed...
4100         
4101         if (this.align) {
4102             cfg.align=this.align;
4103         }
4104         if (this.bgcolor) {
4105             cfg.bgcolor=this.bgcolor;
4106         }
4107         if (this.border) {
4108             cfg.border=this.border;
4109         }
4110         if (this.cellpadding) {
4111             cfg.cellpadding=this.cellpadding;
4112         }
4113         if (this.cellspacing) {
4114             cfg.cellspacing=this.cellspacing;
4115         }
4116         if (this.frame) {
4117             cfg.frame=this.frame;
4118         }
4119         if (this.rules) {
4120             cfg.rules=this.rules;
4121         }
4122         if (this.sortable) {
4123             cfg.sortable=this.sortable;
4124         }
4125         if (this.summary) {
4126             cfg.summary=this.summary;
4127         }
4128         if (this.width) {
4129             cfg.width=this.width;
4130         }
4131         
4132         if(this.store || this.cm){
4133             cfg.cn.push(this.renderHeader());
4134             cfg.cn.push(this.renderBody());
4135             cfg.cn.push(this.renderFooter());
4136             
4137             cfg.cls+=  ' TableGrid';
4138         }
4139         
4140         return cfg;
4141     },
4142 //    
4143 //    initTableGrid : function()
4144 //    {
4145 //        var cfg = {};
4146 //        
4147 //        var header = {
4148 //            tag: 'thead',
4149 //            cn : []
4150 //        };
4151 //        
4152 //        var cm = this.cm;
4153 //        
4154 //        for(var i = 0, len = cm.getColumnCount(); i < len; i++){
4155 //            header.cn.push({
4156 //                tag: 'th',
4157 //                html: cm.getColumnHeader(i)
4158 //            })
4159 //        }
4160 //        
4161 //        cfg.push(header);
4162 //        
4163 //        return cfg;
4164 //        
4165 //        
4166 //    },
4167     
4168     initEvents : function()
4169     {   
4170         if(!this.store || !this.cm){
4171             return;
4172         }
4173         
4174         Roo.log('initEvents with ds!!!!');
4175         
4176         var _this = this;
4177         
4178         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
4179             e.on('click', _this.sort, _this);
4180         });
4181 //        this.maskEl = Roo.DomHelper.append(this.el.select('.TableGrid', true).first(), {tag: "div", cls:"x-dlg-mask"}, true);
4182 //        this.maskEl.enableDisplayMode("block");
4183 //        this.maskEl.show();
4184         
4185         this.store.on('load', this.onLoad, this);
4186         this.store.on('beforeload', this.onBeforeLoad, this);
4187         
4188         this.store.load();
4189         
4190         
4191         
4192     },
4193     
4194     sort : function(e,el)
4195     {
4196         var col = Roo.get(el)
4197         
4198         if(!col.hasClass('sortable')){
4199             return;
4200         }
4201         
4202         var sort = col.attr('sort');
4203         var dir = 'ASC';
4204         
4205         if(col.hasClass('glyphicon-arrow-up')){
4206             dir = 'DESC';
4207         }
4208         
4209         this.store.sortInfo = {field : sort, direction : dir};
4210         
4211         this.store.load();
4212     },
4213     
4214     renderHeader : function()
4215     {
4216         var header = {
4217             tag: 'thead',
4218             cn : []
4219         };
4220         
4221         var cm = this.cm;
4222         
4223         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
4224             
4225             var config = cm.config[i];
4226             
4227             var c = {
4228                 tag: 'th',
4229                 html: cm.getColumnHeader(i)
4230             };
4231             
4232             if(typeof(config.dataIndex) != 'undefined'){
4233                 c.sort = config.dataIndex;
4234             }
4235             
4236             if(typeof(config.sortable) != 'undefined' && config.sortable){
4237                 c.cls = 'sortable';
4238             }
4239             
4240             if(typeof(config.width) != 'undefined'){
4241                 c.style = 'width:' + config.width + 'px';
4242             }
4243             
4244             header.cn.push(c)
4245         }
4246         
4247         return header;
4248     },
4249     
4250     renderBody : function()
4251     {
4252         var body = {
4253             tag: 'tbody',
4254             cn : []
4255         };
4256         
4257         return body;
4258     },
4259     
4260     renderFooter : function()
4261     {
4262         var footer = {
4263             tag: 'tfoot',
4264             cn : []
4265         };
4266         
4267         return footer;
4268     },
4269     
4270     onLoad : function()
4271     {
4272         Roo.log('ds onload');
4273         
4274         var _this = this;
4275         var cm = this.cm;
4276         
4277         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
4278             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
4279             
4280             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
4281                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
4282             }
4283             
4284             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
4285                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
4286             }
4287         });
4288         
4289         var tbody = this.el.select('tbody', true).first();
4290         
4291         var renders = [];
4292         
4293         if(this.store.getCount() > 0){
4294             this.store.data.each(function(d){
4295                 var row = {
4296                     tag : 'tr',
4297                     cn : []
4298                 };
4299                 
4300                 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
4301                     var renderer = cm.getRenderer(i);
4302                     var config = cm.config[i];
4303                     var value = '';
4304                     var id = Roo.id();
4305                     
4306                     if(typeof(renderer) !== 'undefined'){
4307                         value = renderer(d.data[cm.getDataIndex(i)], false, d);
4308                     }
4309                     
4310                     if(typeof(value) === 'object'){
4311                         renders.push({
4312                             id : id,
4313                             cfg : value 
4314                         })
4315                     }
4316                     
4317                     var td = {
4318                         tag: 'td',
4319                         id: id,
4320                         html: (typeof(value) === 'object') ? '' : value
4321                     };
4322                     
4323                     if(typeof(config.width) != 'undefined'){
4324                         td.style = 'width:' +  config.width + 'px';
4325                     }
4326                     
4327                     row.cn.push(td);
4328                    
4329                 }
4330                 
4331                 tbody.createChild(row);
4332                 
4333             });
4334         }
4335         
4336         
4337         if(renders.length){
4338             var _this = this;
4339             Roo.each(renders, function(r){
4340                 _this.renderColumn(r);
4341             })
4342         }
4343 //        
4344 //        if(this.loadMask){
4345 //            this.maskEl.hide();
4346 //        }
4347     },
4348     
4349     onBeforeLoad : function()
4350     {
4351         Roo.log('ds onBeforeLoad');
4352         
4353         this.clear();
4354         
4355 //        if(this.loadMask){
4356 //            this.maskEl.show();
4357 //        }
4358     },
4359     
4360     clear : function()
4361     {
4362         this.el.select('tbody', true).first().dom.innerHTML = '';
4363     },
4364     
4365     getSelectionModel : function(){
4366         if(!this.selModel){
4367             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
4368         }
4369         return this.selModel;
4370     },
4371     
4372     renderColumn : function(r)
4373     {
4374         var _this = this;
4375         r.cfg.render(Roo.get(r.id));
4376         
4377         if(r.cfg.cn){
4378             Roo.each(r.cfg.cn, function(c){
4379                 var child = {
4380                     id: r.id,
4381                     cfg: c
4382                 }
4383                 _this.renderColumn(child);
4384             })
4385         }
4386     }
4387    
4388 });
4389
4390  
4391
4392  /*
4393  * - LGPL
4394  *
4395  * table cell
4396  * 
4397  */
4398
4399 /**
4400  * @class Roo.bootstrap.TableCell
4401  * @extends Roo.bootstrap.Component
4402  * Bootstrap TableCell class
4403  * @cfg {String} html cell contain text
4404  * @cfg {String} cls cell class
4405  * @cfg {String} tag cell tag (td|th) default td
4406  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
4407  * @cfg {String} align Aligns the content in a cell
4408  * @cfg {String} axis Categorizes cells
4409  * @cfg {String} bgcolor Specifies the background color of a cell
4410  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
4411  * @cfg {Number} colspan Specifies the number of columns a cell should span
4412  * @cfg {String} headers Specifies one or more header cells a cell is related to
4413  * @cfg {Number} height Sets the height of a cell
4414  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
4415  * @cfg {Number} rowspan Sets the number of rows a cell should span
4416  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
4417  * @cfg {String} valign Vertical aligns the content in a cell
4418  * @cfg {Number} width Specifies the width of a cell
4419  * 
4420  * @constructor
4421  * Create a new TableCell
4422  * @param {Object} config The config object
4423  */
4424
4425 Roo.bootstrap.TableCell = function(config){
4426     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
4427 };
4428
4429 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
4430     
4431     html: false,
4432     cls: false,
4433     tag: false,
4434     abbr: false,
4435     align: false,
4436     axis: false,
4437     bgcolor: false,
4438     charoff: false,
4439     colspan: false,
4440     headers: false,
4441     height: false,
4442     nowrap: false,
4443     rowspan: false,
4444     scope: false,
4445     valign: false,
4446     width: false,
4447     
4448     
4449     getAutoCreate : function(){
4450         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
4451         
4452         cfg = {
4453             tag: 'td'
4454         }
4455         
4456         if(this.tag){
4457             cfg.tag = this.tag;
4458         }
4459         
4460         if (this.html) {
4461             cfg.html=this.html
4462         }
4463         if (this.cls) {
4464             cfg.cls=this.cls
4465         }
4466         if (this.abbr) {
4467             cfg.abbr=this.abbr
4468         }
4469         if (this.align) {
4470             cfg.align=this.align
4471         }
4472         if (this.axis) {
4473             cfg.axis=this.axis
4474         }
4475         if (this.bgcolor) {
4476             cfg.bgcolor=this.bgcolor
4477         }
4478         if (this.charoff) {
4479             cfg.charoff=this.charoff
4480         }
4481         if (this.colspan) {
4482             cfg.colspan=this.colspan
4483         }
4484         if (this.headers) {
4485             cfg.headers=this.headers
4486         }
4487         if (this.height) {
4488             cfg.height=this.height
4489         }
4490         if (this.nowrap) {
4491             cfg.nowrap=this.nowrap
4492         }
4493         if (this.rowspan) {
4494             cfg.rowspan=this.rowspan
4495         }
4496         if (this.scope) {
4497             cfg.scope=this.scope
4498         }
4499         if (this.valign) {
4500             cfg.valign=this.valign
4501         }
4502         if (this.width) {
4503             cfg.width=this.width
4504         }
4505         
4506         
4507         return cfg;
4508     }
4509    
4510 });
4511
4512  
4513
4514  /*
4515  * - LGPL
4516  *
4517  * table row
4518  * 
4519  */
4520
4521 /**
4522  * @class Roo.bootstrap.TableRow
4523  * @extends Roo.bootstrap.Component
4524  * Bootstrap TableRow class
4525  * @cfg {String} cls row class
4526  * @cfg {String} align Aligns the content in a table row
4527  * @cfg {String} bgcolor Specifies a background color for a table row
4528  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
4529  * @cfg {String} valign Vertical aligns the content in a table row
4530  * 
4531  * @constructor
4532  * Create a new TableRow
4533  * @param {Object} config The config object
4534  */
4535
4536 Roo.bootstrap.TableRow = function(config){
4537     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
4538 };
4539
4540 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
4541     
4542     cls: false,
4543     align: false,
4544     bgcolor: false,
4545     charoff: false,
4546     valign: false,
4547     
4548     getAutoCreate : function(){
4549         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
4550         
4551         cfg = {
4552             tag: 'tr'
4553         }
4554             
4555         if(this.cls){
4556             cfg.cls = this.cls;
4557         }
4558         if(this.align){
4559             cfg.align = this.align;
4560         }
4561         if(this.bgcolor){
4562             cfg.bgcolor = this.bgcolor;
4563         }
4564         if(this.charoff){
4565             cfg.charoff = this.charoff;
4566         }
4567         if(this.valign){
4568             cfg.valign = this.valign;
4569         }
4570         
4571         return cfg;
4572     }
4573    
4574 });
4575
4576  
4577
4578  /*
4579  * - LGPL
4580  *
4581  * table body
4582  * 
4583  */
4584
4585 /**
4586  * @class Roo.bootstrap.TableBody
4587  * @extends Roo.bootstrap.Component
4588  * Bootstrap TableBody class
4589  * @cfg {String} cls element class
4590  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
4591  * @cfg {String} align Aligns the content inside the element
4592  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
4593  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
4594  * 
4595  * @constructor
4596  * Create a new TableBody
4597  * @param {Object} config The config object
4598  */
4599
4600 Roo.bootstrap.TableBody = function(config){
4601     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
4602 };
4603
4604 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
4605     
4606     cls: false,
4607     tag: false,
4608     align: false,
4609     charoff: false,
4610     valign: false,
4611     
4612     getAutoCreate : function(){
4613         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
4614         
4615         cfg = {
4616             tag: 'tbody'
4617         }
4618             
4619         if (this.cls) {
4620             cfg.cls=this.cls
4621         }
4622         if(this.tag){
4623             cfg.tag = this.tag;
4624         }
4625         
4626         if(this.align){
4627             cfg.align = this.align;
4628         }
4629         if(this.charoff){
4630             cfg.charoff = this.charoff;
4631         }
4632         if(this.valign){
4633             cfg.valign = this.valign;
4634         }
4635         
4636         return cfg;
4637     }
4638     
4639     
4640 //    initEvents : function()
4641 //    {
4642 //        
4643 //        if(!this.store){
4644 //            return;
4645 //        }
4646 //        
4647 //        this.store = Roo.factory(this.store, Roo.data);
4648 //        this.store.on('load', this.onLoad, this);
4649 //        
4650 //        this.store.load();
4651 //        
4652 //    },
4653 //    
4654 //    onLoad: function () 
4655 //    {   
4656 //        this.fireEvent('load', this);
4657 //    }
4658 //    
4659 //   
4660 });
4661
4662  
4663
4664  /*
4665  * Based on:
4666  * Ext JS Library 1.1.1
4667  * Copyright(c) 2006-2007, Ext JS, LLC.
4668  *
4669  * Originally Released Under LGPL - original licence link has changed is not relivant.
4670  *
4671  * Fork - LGPL
4672  * <script type="text/javascript">
4673  */
4674
4675 // as we use this in bootstrap.
4676 Roo.namespace('Roo.form');
4677  /**
4678  * @class Roo.form.Action
4679  * Internal Class used to handle form actions
4680  * @constructor
4681  * @param {Roo.form.BasicForm} el The form element or its id
4682  * @param {Object} config Configuration options
4683  */
4684
4685  
4686  
4687 // define the action interface
4688 Roo.form.Action = function(form, options){
4689     this.form = form;
4690     this.options = options || {};
4691 };
4692 /**
4693  * Client Validation Failed
4694  * @const 
4695  */
4696 Roo.form.Action.CLIENT_INVALID = 'client';
4697 /**
4698  * Server Validation Failed
4699  * @const 
4700  */
4701 Roo.form.Action.SERVER_INVALID = 'server';
4702  /**
4703  * Connect to Server Failed
4704  * @const 
4705  */
4706 Roo.form.Action.CONNECT_FAILURE = 'connect';
4707 /**
4708  * Reading Data from Server Failed
4709  * @const 
4710  */
4711 Roo.form.Action.LOAD_FAILURE = 'load';
4712
4713 Roo.form.Action.prototype = {
4714     type : 'default',
4715     failureType : undefined,
4716     response : undefined,
4717     result : undefined,
4718
4719     // interface method
4720     run : function(options){
4721
4722     },
4723
4724     // interface method
4725     success : function(response){
4726
4727     },
4728
4729     // interface method
4730     handleResponse : function(response){
4731
4732     },
4733
4734     // default connection failure
4735     failure : function(response){
4736         
4737         this.response = response;
4738         this.failureType = Roo.form.Action.CONNECT_FAILURE;
4739         this.form.afterAction(this, false);
4740     },
4741
4742     processResponse : function(response){
4743         this.response = response;
4744         if(!response.responseText){
4745             return true;
4746         }
4747         this.result = this.handleResponse(response);
4748         return this.result;
4749     },
4750
4751     // utility functions used internally
4752     getUrl : function(appendParams){
4753         var url = this.options.url || this.form.url || this.form.el.dom.action;
4754         if(appendParams){
4755             var p = this.getParams();
4756             if(p){
4757                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
4758             }
4759         }
4760         return url;
4761     },
4762
4763     getMethod : function(){
4764         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
4765     },
4766
4767     getParams : function(){
4768         var bp = this.form.baseParams;
4769         var p = this.options.params;
4770         if(p){
4771             if(typeof p == "object"){
4772                 p = Roo.urlEncode(Roo.applyIf(p, bp));
4773             }else if(typeof p == 'string' && bp){
4774                 p += '&' + Roo.urlEncode(bp);
4775             }
4776         }else if(bp){
4777             p = Roo.urlEncode(bp);
4778         }
4779         return p;
4780     },
4781
4782     createCallback : function(){
4783         return {
4784             success: this.success,
4785             failure: this.failure,
4786             scope: this,
4787             timeout: (this.form.timeout*1000),
4788             upload: this.form.fileUpload ? this.success : undefined
4789         };
4790     }
4791 };
4792
4793 Roo.form.Action.Submit = function(form, options){
4794     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
4795 };
4796
4797 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
4798     type : 'submit',
4799
4800     haveProgress : false,
4801     uploadComplete : false,
4802     
4803     // uploadProgress indicator.
4804     uploadProgress : function()
4805     {
4806         if (!this.form.progressUrl) {
4807             return;
4808         }
4809         
4810         if (!this.haveProgress) {
4811             Roo.MessageBox.progress("Uploading", "Uploading");
4812         }
4813         if (this.uploadComplete) {
4814            Roo.MessageBox.hide();
4815            return;
4816         }
4817         
4818         this.haveProgress = true;
4819    
4820         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
4821         
4822         var c = new Roo.data.Connection();
4823         c.request({
4824             url : this.form.progressUrl,
4825             params: {
4826                 id : uid
4827             },
4828             method: 'GET',
4829             success : function(req){
4830                //console.log(data);
4831                 var rdata = false;
4832                 var edata;
4833                 try  {
4834                    rdata = Roo.decode(req.responseText)
4835                 } catch (e) {
4836                     Roo.log("Invalid data from server..");
4837                     Roo.log(edata);
4838                     return;
4839                 }
4840                 if (!rdata || !rdata.success) {
4841                     Roo.log(rdata);
4842                     Roo.MessageBox.alert(Roo.encode(rdata));
4843                     return;
4844                 }
4845                 var data = rdata.data;
4846                 
4847                 if (this.uploadComplete) {
4848                    Roo.MessageBox.hide();
4849                    return;
4850                 }
4851                    
4852                 if (data){
4853                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
4854                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
4855                     );
4856                 }
4857                 this.uploadProgress.defer(2000,this);
4858             },
4859        
4860             failure: function(data) {
4861                 Roo.log('progress url failed ');
4862                 Roo.log(data);
4863             },
4864             scope : this
4865         });
4866            
4867     },
4868     
4869     
4870     run : function()
4871     {
4872         // run get Values on the form, so it syncs any secondary forms.
4873         this.form.getValues();
4874         
4875         var o = this.options;
4876         var method = this.getMethod();
4877         var isPost = method == 'POST';
4878         if(o.clientValidation === false || this.form.isValid()){
4879             
4880             if (this.form.progressUrl) {
4881                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
4882                     (new Date() * 1) + '' + Math.random());
4883                     
4884             } 
4885             
4886             
4887             Roo.Ajax.request(Roo.apply(this.createCallback(), {
4888                 form:this.form.el.dom,
4889                 url:this.getUrl(!isPost),
4890                 method: method,
4891                 params:isPost ? this.getParams() : null,
4892                 isUpload: this.form.fileUpload
4893             }));
4894             
4895             this.uploadProgress();
4896
4897         }else if (o.clientValidation !== false){ // client validation failed
4898             this.failureType = Roo.form.Action.CLIENT_INVALID;
4899             this.form.afterAction(this, false);
4900         }
4901     },
4902
4903     success : function(response)
4904     {
4905         this.uploadComplete= true;
4906         if (this.haveProgress) {
4907             Roo.MessageBox.hide();
4908         }
4909         
4910         
4911         var result = this.processResponse(response);
4912         if(result === true || result.success){
4913             this.form.afterAction(this, true);
4914             return;
4915         }
4916         if(result.errors){
4917             this.form.markInvalid(result.errors);
4918             this.failureType = Roo.form.Action.SERVER_INVALID;
4919         }
4920         this.form.afterAction(this, false);
4921     },
4922     failure : function(response)
4923     {
4924         this.uploadComplete= true;
4925         if (this.haveProgress) {
4926             Roo.MessageBox.hide();
4927         }
4928         
4929         this.response = response;
4930         this.failureType = Roo.form.Action.CONNECT_FAILURE;
4931         this.form.afterAction(this, false);
4932     },
4933     
4934     handleResponse : function(response){
4935         if(this.form.errorReader){
4936             var rs = this.form.errorReader.read(response);
4937             var errors = [];
4938             if(rs.records){
4939                 for(var i = 0, len = rs.records.length; i < len; i++) {
4940                     var r = rs.records[i];
4941                     errors[i] = r.data;
4942                 }
4943             }
4944             if(errors.length < 1){
4945                 errors = null;
4946             }
4947             return {
4948                 success : rs.success,
4949                 errors : errors
4950             };
4951         }
4952         var ret = false;
4953         try {
4954             ret = Roo.decode(response.responseText);
4955         } catch (e) {
4956             ret = {
4957                 success: false,
4958                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
4959                 errors : []
4960             };
4961         }
4962         return ret;
4963         
4964     }
4965 });
4966
4967
4968 Roo.form.Action.Load = function(form, options){
4969     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
4970     this.reader = this.form.reader;
4971 };
4972
4973 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
4974     type : 'load',
4975
4976     run : function(){
4977         
4978         Roo.Ajax.request(Roo.apply(
4979                 this.createCallback(), {
4980                     method:this.getMethod(),
4981                     url:this.getUrl(false),
4982                     params:this.getParams()
4983         }));
4984     },
4985
4986     success : function(response){
4987         
4988         var result = this.processResponse(response);
4989         if(result === true || !result.success || !result.data){
4990             this.failureType = Roo.form.Action.LOAD_FAILURE;
4991             this.form.afterAction(this, false);
4992             return;
4993         }
4994         this.form.clearInvalid();
4995         this.form.setValues(result.data);
4996         this.form.afterAction(this, true);
4997     },
4998
4999     handleResponse : function(response){
5000         if(this.form.reader){
5001             var rs = this.form.reader.read(response);
5002             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
5003             return {
5004                 success : rs.success,
5005                 data : data
5006             };
5007         }
5008         return Roo.decode(response.responseText);
5009     }
5010 });
5011
5012 Roo.form.Action.ACTION_TYPES = {
5013     'load' : Roo.form.Action.Load,
5014     'submit' : Roo.form.Action.Submit
5015 };/*
5016  * - LGPL
5017  *
5018  * form
5019  * 
5020  */
5021
5022 /**
5023  * @class Roo.bootstrap.Form
5024  * @extends Roo.bootstrap.Component
5025  * Bootstrap Form class
5026  * @cfg {String} method  GET | POST (default POST)
5027  * @cfg {String} labelAlign top | left (default top)
5028   * @cfg {String} align left  | right - for navbars
5029
5030  * 
5031  * @constructor
5032  * Create a new Form
5033  * @param {Object} config The config object
5034  */
5035
5036
5037 Roo.bootstrap.Form = function(config){
5038     Roo.bootstrap.Form.superclass.constructor.call(this, config);
5039     this.addEvents({
5040         /**
5041          * @event clientvalidation
5042          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
5043          * @param {Form} this
5044          * @param {Boolean} valid true if the form has passed client-side validation
5045          */
5046         clientvalidation: true,
5047         /**
5048          * @event beforeaction
5049          * Fires before any action is performed. Return false to cancel the action.
5050          * @param {Form} this
5051          * @param {Action} action The action to be performed
5052          */
5053         beforeaction: true,
5054         /**
5055          * @event actionfailed
5056          * Fires when an action fails.
5057          * @param {Form} this
5058          * @param {Action} action The action that failed
5059          */
5060         actionfailed : true,
5061         /**
5062          * @event actioncomplete
5063          * Fires when an action is completed.
5064          * @param {Form} this
5065          * @param {Action} action The action that completed
5066          */
5067         actioncomplete : true
5068     });
5069     
5070 };
5071
5072 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
5073       
5074      /**
5075      * @cfg {String} method
5076      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
5077      */
5078     method : 'POST',
5079     /**
5080      * @cfg {String} url
5081      * The URL to use for form actions if one isn't supplied in the action options.
5082      */
5083     /**
5084      * @cfg {Boolean} fileUpload
5085      * Set to true if this form is a file upload.
5086      */
5087      
5088     /**
5089      * @cfg {Object} baseParams
5090      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
5091      */
5092       
5093     /**
5094      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
5095      */
5096     timeout: 30,
5097     /**
5098      * @cfg {Sting} align (left|right) for navbar forms
5099      */
5100     align : 'left',
5101
5102     // private
5103     activeAction : null,
5104  
5105     /**
5106      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
5107      * element by passing it or its id or mask the form itself by passing in true.
5108      * @type Mixed
5109      */
5110     waitMsgTarget : false,
5111     
5112      
5113     
5114     /**
5115      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
5116      * element by passing it or its id or mask the form itself by passing in true.
5117      * @type Mixed
5118      */
5119     
5120     getAutoCreate : function(){
5121         
5122         var cfg = {
5123             tag: 'form',
5124             method : this.method || 'POST',
5125             id : this.id || Roo.id(),
5126             cls : ''
5127         }
5128         if (this.parent().xtype.match(/^Nav/)) {
5129             cfg.cls = 'navbar-form navbar-' + this.align;
5130             
5131         }
5132         
5133         if (this.labelAlign == 'left' ) {
5134             cfg.cls += ' form-horizontal';
5135         }
5136         
5137         
5138         return cfg;
5139     },
5140     initEvents : function()
5141     {
5142         this.el.on('submit', this.onSubmit, this);
5143         
5144         
5145     },
5146     // private
5147     onSubmit : function(e){
5148         e.stopEvent();
5149     },
5150     
5151      /**
5152      * Returns true if client-side validation on the form is successful.
5153      * @return Boolean
5154      */
5155     isValid : function(){
5156         var items = this.getItems();
5157         var valid = true;
5158         items.each(function(f){
5159            if(!f.validate()){
5160                valid = false;
5161                
5162            }
5163         });
5164         return valid;
5165     },
5166     /**
5167      * Returns true if any fields in this form have changed since their original load.
5168      * @return Boolean
5169      */
5170     isDirty : function(){
5171         var dirty = false;
5172         var items = this.getItems();
5173         items.each(function(f){
5174            if(f.isDirty()){
5175                dirty = true;
5176                return false;
5177            }
5178            return true;
5179         });
5180         return dirty;
5181     },
5182      /**
5183      * Performs a predefined action (submit or load) or custom actions you define on this form.
5184      * @param {String} actionName The name of the action type
5185      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
5186      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
5187      * accept other config options):
5188      * <pre>
5189 Property          Type             Description
5190 ----------------  ---------------  ----------------------------------------------------------------------------------
5191 url               String           The url for the action (defaults to the form's url)
5192 method            String           The form method to use (defaults to the form's method, or POST if not defined)
5193 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
5194 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
5195                                    validate the form on the client (defaults to false)
5196      * </pre>
5197      * @return {BasicForm} this
5198      */
5199     doAction : function(action, options){
5200         if(typeof action == 'string'){
5201             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
5202         }
5203         if(this.fireEvent('beforeaction', this, action) !== false){
5204             this.beforeAction(action);
5205             action.run.defer(100, action);
5206         }
5207         return this;
5208     },
5209     
5210     // private
5211     beforeAction : function(action){
5212         var o = action.options;
5213         
5214         // not really supported yet.. ??
5215         
5216         //if(this.waitMsgTarget === true){
5217             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
5218         //}else if(this.waitMsgTarget){
5219         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
5220         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
5221         //}else {
5222         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
5223        // }
5224          
5225     },
5226
5227     // private
5228     afterAction : function(action, success){
5229         this.activeAction = null;
5230         var o = action.options;
5231         
5232         //if(this.waitMsgTarget === true){
5233             this.el.unmask();
5234         //}else if(this.waitMsgTarget){
5235         //    this.waitMsgTarget.unmask();
5236         //}else{
5237         //    Roo.MessageBox.updateProgress(1);
5238         //    Roo.MessageBox.hide();
5239        // }
5240         // 
5241         if(success){
5242             if(o.reset){
5243                 this.reset();
5244             }
5245             Roo.callback(o.success, o.scope, [this, action]);
5246             this.fireEvent('actioncomplete', this, action);
5247             
5248         }else{
5249             
5250             // failure condition..
5251             // we have a scenario where updates need confirming.
5252             // eg. if a locking scenario exists..
5253             // we look for { errors : { needs_confirm : true }} in the response.
5254             if (
5255                 (typeof(action.result) != 'undefined')  &&
5256                 (typeof(action.result.errors) != 'undefined')  &&
5257                 (typeof(action.result.errors.needs_confirm) != 'undefined')
5258            ){
5259                 var _t = this;
5260                 Roo.log("not supported yet");
5261                  /*
5262                 
5263                 Roo.MessageBox.confirm(
5264                     "Change requires confirmation",
5265                     action.result.errorMsg,
5266                     function(r) {
5267                         if (r != 'yes') {
5268                             return;
5269                         }
5270                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
5271                     }
5272                     
5273                 );
5274                 */
5275                 
5276                 
5277                 return;
5278             }
5279             
5280             Roo.callback(o.failure, o.scope, [this, action]);
5281             // show an error message if no failed handler is set..
5282             if (!this.hasListener('actionfailed')) {
5283                 Roo.log("need to add dialog support");
5284                 /*
5285                 Roo.MessageBox.alert("Error",
5286                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
5287                         action.result.errorMsg :
5288                         "Saving Failed, please check your entries or try again"
5289                 );
5290                 */
5291             }
5292             
5293             this.fireEvent('actionfailed', this, action);
5294         }
5295         
5296     },
5297     /**
5298      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
5299      * @param {String} id The value to search for
5300      * @return Field
5301      */
5302     findField : function(id){
5303         var items = this.getItems();
5304         var field = items.get(id);
5305         if(!field){
5306              items.each(function(f){
5307                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
5308                     field = f;
5309                     return false;
5310                 }
5311                 return true;
5312             });
5313         }
5314         return field || null;
5315     },
5316      /**
5317      * Mark fields in this form invalid in bulk.
5318      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
5319      * @return {BasicForm} this
5320      */
5321     markInvalid : function(errors){
5322         if(errors instanceof Array){
5323             for(var i = 0, len = errors.length; i < len; i++){
5324                 var fieldError = errors[i];
5325                 var f = this.findField(fieldError.id);
5326                 if(f){
5327                     f.markInvalid(fieldError.msg);
5328                 }
5329             }
5330         }else{
5331             var field, id;
5332             for(id in errors){
5333                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
5334                     field.markInvalid(errors[id]);
5335                 }
5336             }
5337         }
5338         //Roo.each(this.childForms || [], function (f) {
5339         //    f.markInvalid(errors);
5340         //});
5341         
5342         return this;
5343     },
5344
5345     /**
5346      * Set values for fields in this form in bulk.
5347      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
5348      * @return {BasicForm} this
5349      */
5350     setValues : function(values){
5351         if(values instanceof Array){ // array of objects
5352             for(var i = 0, len = values.length; i < len; i++){
5353                 var v = values[i];
5354                 var f = this.findField(v.id);
5355                 if(f){
5356                     f.setValue(v.value);
5357                     if(this.trackResetOnLoad){
5358                         f.originalValue = f.getValue();
5359                     }
5360                 }
5361             }
5362         }else{ // object hash
5363             var field, id;
5364             for(id in values){
5365                 if(typeof values[id] != 'function' && (field = this.findField(id))){
5366                     
5367                     if (field.setFromData && 
5368                         field.valueField && 
5369                         field.displayField &&
5370                         // combos' with local stores can 
5371                         // be queried via setValue()
5372                         // to set their value..
5373                         (field.store && !field.store.isLocal)
5374                         ) {
5375                         // it's a combo
5376                         var sd = { };
5377                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
5378                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
5379                         field.setFromData(sd);
5380                         
5381                     } else {
5382                         field.setValue(values[id]);
5383                     }
5384                     
5385                     
5386                     if(this.trackResetOnLoad){
5387                         field.originalValue = field.getValue();
5388                     }
5389                 }
5390             }
5391         }
5392          
5393         //Roo.each(this.childForms || [], function (f) {
5394         //    f.setValues(values);
5395         //});
5396                 
5397         return this;
5398     },
5399
5400     /**
5401      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
5402      * they are returned as an array.
5403      * @param {Boolean} asString
5404      * @return {Object}
5405      */
5406     getValues : function(asString){
5407         //if (this.childForms) {
5408             // copy values from the child forms
5409         //    Roo.each(this.childForms, function (f) {
5410         //        this.setValues(f.getValues());
5411         //    }, this);
5412         //}
5413         
5414         
5415         
5416         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
5417         if(asString === true){
5418             return fs;
5419         }
5420         return Roo.urlDecode(fs);
5421     },
5422     
5423     /**
5424      * Returns the fields in this form as an object with key/value pairs. 
5425      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
5426      * @return {Object}
5427      */
5428     getFieldValues : function(with_hidden)
5429     {
5430         var items = this.getItems();
5431         var ret = {};
5432         items.each(function(f){
5433             if (!f.getName()) {
5434                 return;
5435             }
5436             var v = f.getValue();
5437             if (f.inputType =='radio') {
5438                 if (typeof(ret[f.getName()]) == 'undefined') {
5439                     ret[f.getName()] = ''; // empty..
5440                 }
5441                 
5442                 if (!f.el.dom.checked) {
5443                     return;
5444                     
5445                 }
5446                 v = f.el.dom.value;
5447                 
5448             }
5449             
5450             // not sure if this supported any more..
5451             if ((typeof(v) == 'object') && f.getRawValue) {
5452                 v = f.getRawValue() ; // dates..
5453             }
5454             // combo boxes where name != hiddenName...
5455             if (f.name != f.getName()) {
5456                 ret[f.name] = f.getRawValue();
5457             }
5458             ret[f.getName()] = v;
5459         });
5460         
5461         return ret;
5462     },
5463
5464     /**
5465      * Clears all invalid messages in this form.
5466      * @return {BasicForm} this
5467      */
5468     clearInvalid : function(){
5469         var items = this.getItems();
5470         
5471         items.each(function(f){
5472            f.clearInvalid();
5473         });
5474         
5475         
5476         
5477         return this;
5478     },
5479
5480     /**
5481      * Resets this form.
5482      * @return {BasicForm} this
5483      */
5484     reset : function(){
5485         var items = this.getItems();
5486         items.each(function(f){
5487             f.reset();
5488         });
5489         
5490         Roo.each(this.childForms || [], function (f) {
5491             f.reset();
5492         });
5493        
5494         
5495         return this;
5496     },
5497     getItems : function()
5498     {
5499         var r=new Roo.util.MixedCollection(false, function(o){
5500             return o.id || (o.id = Roo.id());
5501         });
5502         var iter = function(el) {
5503             if (el.inputEl) {
5504                 r.add(el);
5505             }
5506             if (!el.items) {
5507                 return;
5508             }
5509             Roo.each(el.items,function(e) {
5510                 iter(e);
5511             });
5512             
5513             
5514         };
5515         iter(this);
5516         return r;
5517         
5518         
5519         
5520         
5521     }
5522     
5523 });
5524
5525  
5526 /*
5527  * Based on:
5528  * Ext JS Library 1.1.1
5529  * Copyright(c) 2006-2007, Ext JS, LLC.
5530  *
5531  * Originally Released Under LGPL - original licence link has changed is not relivant.
5532  *
5533  * Fork - LGPL
5534  * <script type="text/javascript">
5535  */
5536 /**
5537  * @class Roo.form.VTypes
5538  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
5539  * @singleton
5540  */
5541 Roo.form.VTypes = function(){
5542     // closure these in so they are only created once.
5543     var alpha = /^[a-zA-Z_]+$/;
5544     var alphanum = /^[a-zA-Z0-9_]+$/;
5545     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
5546     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
5547
5548     // All these messages and functions are configurable
5549     return {
5550         /**
5551          * The function used to validate email addresses
5552          * @param {String} value The email address
5553          */
5554         'email' : function(v){
5555             return email.test(v);
5556         },
5557         /**
5558          * The error text to display when the email validation function returns false
5559          * @type String
5560          */
5561         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
5562         /**
5563          * The keystroke filter mask to be applied on email input
5564          * @type RegExp
5565          */
5566         'emailMask' : /[a-z0-9_\.\-@]/i,
5567
5568         /**
5569          * The function used to validate URLs
5570          * @param {String} value The URL
5571          */
5572         'url' : function(v){
5573             return url.test(v);
5574         },
5575         /**
5576          * The error text to display when the url validation function returns false
5577          * @type String
5578          */
5579         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
5580         
5581         /**
5582          * The function used to validate alpha values
5583          * @param {String} value The value
5584          */
5585         'alpha' : function(v){
5586             return alpha.test(v);
5587         },
5588         /**
5589          * The error text to display when the alpha validation function returns false
5590          * @type String
5591          */
5592         'alphaText' : 'This field should only contain letters and _',
5593         /**
5594          * The keystroke filter mask to be applied on alpha input
5595          * @type RegExp
5596          */
5597         'alphaMask' : /[a-z_]/i,
5598
5599         /**
5600          * The function used to validate alphanumeric values
5601          * @param {String} value The value
5602          */
5603         'alphanum' : function(v){
5604             return alphanum.test(v);
5605         },
5606         /**
5607          * The error text to display when the alphanumeric validation function returns false
5608          * @type String
5609          */
5610         'alphanumText' : 'This field should only contain letters, numbers and _',
5611         /**
5612          * The keystroke filter mask to be applied on alphanumeric input
5613          * @type RegExp
5614          */
5615         'alphanumMask' : /[a-z0-9_]/i
5616     };
5617 }();/*
5618  * - LGPL
5619  *
5620  * Input
5621  * 
5622  */
5623
5624 /**
5625  * @class Roo.bootstrap.Input
5626  * @extends Roo.bootstrap.Component
5627  * Bootstrap Input class
5628  * @cfg {Boolean} disabled is it disabled
5629  * @cfg {String} fieldLabel - the label associated
5630  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
5631  * @cfg {String} name name of the input
5632  * @cfg {string} fieldLabel - the label associated
5633  * @cfg {string}  inputType - input / file submit ...
5634  * @cfg {string} placeholder - placeholder to put in text.
5635  * @cfg {string}  before - input group add on before
5636  * @cfg {string} after - input group add on after
5637  * @cfg {string} size - (lg|sm) or leave empty..
5638  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
5639  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
5640  * @cfg {Number} md colspan out of 12 for computer-sized screens
5641  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
5642  * @cfg {string} value default value of the input
5643  * @cfg {Number} labelWidth set the width of label (0-12)
5644  * @cfg {String} labelAlign (top|left)
5645  * @cfg {Boolean} readOnly Specifies that the field should be read-only
5646  * 
5647  * 
5648  * @constructor
5649  * Create a new Input
5650  * @param {Object} config The config object
5651  */
5652
5653 Roo.bootstrap.Input = function(config){
5654     Roo.bootstrap.Input.superclass.constructor.call(this, config);
5655    
5656         this.addEvents({
5657             /**
5658              * @event focus
5659              * Fires when this field receives input focus.
5660              * @param {Roo.form.Field} this
5661              */
5662             focus : true,
5663             /**
5664              * @event blur
5665              * Fires when this field loses input focus.
5666              * @param {Roo.form.Field} this
5667              */
5668             blur : true,
5669             /**
5670              * @event specialkey
5671              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
5672              * {@link Roo.EventObject#getKey} to determine which key was pressed.
5673              * @param {Roo.form.Field} this
5674              * @param {Roo.EventObject} e The event object
5675              */
5676             specialkey : true,
5677             /**
5678              * @event change
5679              * Fires just before the field blurs if the field value has changed.
5680              * @param {Roo.form.Field} this
5681              * @param {Mixed} newValue The new value
5682              * @param {Mixed} oldValue The original value
5683              */
5684             change : true,
5685             /**
5686              * @event invalid
5687              * Fires after the field has been marked as invalid.
5688              * @param {Roo.form.Field} this
5689              * @param {String} msg The validation message
5690              */
5691             invalid : true,
5692             /**
5693              * @event valid
5694              * Fires after the field has been validated with no errors.
5695              * @param {Roo.form.Field} this
5696              */
5697             valid : true,
5698              /**
5699              * @event keyup
5700              * Fires after the key up
5701              * @param {Roo.form.Field} this
5702              * @param {Roo.EventObject}  e The event Object
5703              */
5704             keyup : true
5705         });
5706 };
5707
5708 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
5709      /**
5710      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
5711       automatic validation (defaults to "keyup").
5712      */
5713     validationEvent : "keyup",
5714      /**
5715      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
5716      */
5717     validateOnBlur : true,
5718     /**
5719      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
5720      */
5721     validationDelay : 250,
5722      /**
5723      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
5724      */
5725     focusClass : "x-form-focus",  // not needed???
5726     
5727        
5728     /**
5729      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
5730      */
5731     invalidClass : "has-error",
5732     
5733     /**
5734      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
5735      */
5736     selectOnFocus : false,
5737     
5738      /**
5739      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
5740      */
5741     maskRe : null,
5742        /**
5743      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
5744      */
5745     vtype : null,
5746     
5747       /**
5748      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
5749      */
5750     disableKeyFilter : false,
5751     
5752        /**
5753      * @cfg {Boolean} disabled True to disable the field (defaults to false).
5754      */
5755     disabled : false,
5756      /**
5757      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
5758      */
5759     allowBlank : true,
5760     /**
5761      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
5762      */
5763     blankText : "This field is required",
5764     
5765      /**
5766      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
5767      */
5768     minLength : 0,
5769     /**
5770      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
5771      */
5772     maxLength : Number.MAX_VALUE,
5773     /**
5774      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
5775      */
5776     minLengthText : "The minimum length for this field is {0}",
5777     /**
5778      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
5779      */
5780     maxLengthText : "The maximum length for this field is {0}",
5781   
5782     
5783     /**
5784      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
5785      * If available, this function will be called only after the basic validators all return true, and will be passed the
5786      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
5787      */
5788     validator : null,
5789     /**
5790      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
5791      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
5792      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
5793      */
5794     regex : null,
5795     /**
5796      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
5797      */
5798     regexText : "",
5799     
5800     
5801     
5802     fieldLabel : '',
5803     inputType : 'text',
5804     
5805     name : false,
5806     placeholder: false,
5807     before : false,
5808     after : false,
5809     size : false,
5810     // private
5811     hasFocus : false,
5812     preventMark: false,
5813     isFormField : true,
5814     value : '',
5815     labelWidth : 2,
5816     labelAlign : false,
5817     readOnly : false,
5818     
5819     parentLabelAlign : function()
5820     {
5821         var parent = this;
5822         while (parent.parent()) {
5823             parent = parent.parent();
5824             if (typeof(parent.labelAlign) !='undefined') {
5825                 return parent.labelAlign;
5826             }
5827         }
5828         return 'left';
5829         
5830     },
5831     
5832     getAutoCreate : function(){
5833         
5834         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
5835         
5836         var id = Roo.id();
5837         
5838         var cfg = {};
5839         
5840         if(this.inputType != 'hidden'){
5841             cfg.cls = 'form-group' //input-group
5842         }
5843         
5844         var input =  {
5845             tag: 'input',
5846             id : id,
5847             type : this.inputType,
5848             value : this.value,
5849             cls : 'form-control',
5850             placeholder : this.placeholder || ''
5851             
5852         };
5853         
5854         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
5855             input.maxLength = this.maxLength;
5856         }
5857         
5858         if (this.disabled) {
5859             input.disabled=true;
5860         }
5861         
5862         if (this.readOnly) {
5863             input.readonly=true;
5864         }
5865         
5866         if (this.name) {
5867             input.name = this.name;
5868         }
5869         if (this.size) {
5870             input.cls += ' input-' + this.size;
5871         }
5872         var settings=this;
5873         ['xs','sm','md','lg'].map(function(size){
5874             if (settings[size]) {
5875                 cfg.cls += ' col-' + size + '-' + settings[size];
5876             }
5877         });
5878         
5879         var inputblock = input;
5880         
5881         if (this.before || this.after) {
5882             
5883             inputblock = {
5884                 cls : 'input-group',
5885                 cn :  [] 
5886             };
5887             if (this.before) {
5888                 inputblock.cn.push({
5889                     tag :'span',
5890                     cls : 'input-group-addon',
5891                     html : this.before
5892                 });
5893             }
5894             inputblock.cn.push(input);
5895             if (this.after) {
5896                 inputblock.cn.push({
5897                     tag :'span',
5898                     cls : 'input-group-addon',
5899                     html : this.after
5900                 });
5901             }
5902             
5903         };
5904         
5905         if (align ==='left' && this.fieldLabel.length) {
5906                 Roo.log("left and has label");
5907                 cfg.cn = [
5908                     
5909                     {
5910                         tag: 'label',
5911                         'for' :  id,
5912                         cls : 'control-label col-sm-' + this.labelWidth,
5913                         html : this.fieldLabel
5914                         
5915                     },
5916                     {
5917                         cls : "col-sm-" + (12 - this.labelWidth), 
5918                         cn: [
5919                             inputblock
5920                         ]
5921                     }
5922                     
5923                 ];
5924         } else if ( this.fieldLabel.length) {
5925                 Roo.log(" label");
5926                  cfg.cn = [
5927                    
5928                     {
5929                         tag: 'label',
5930                         //cls : 'input-group-addon',
5931                         html : this.fieldLabel
5932                         
5933                     },
5934                     
5935                     inputblock
5936                     
5937                 ];
5938
5939         } else {
5940             
5941                 Roo.log(" no label && no align");
5942                 cfg.cn = [
5943                     
5944                         inputblock
5945                     
5946                 ];
5947                 
5948                 
5949         };
5950         Roo.log('input-parentType: ' + this.parentType);
5951         
5952         if (this.parentType === 'Navbar' &&  this.parent().bar) {
5953            cfg.cls += ' navbar-form';
5954            Roo.log(cfg);
5955         }
5956         
5957         return cfg;
5958         
5959     },
5960     /**
5961      * return the real input element.
5962      */
5963     inputEl: function ()
5964     {
5965         return this.el.select('input.form-control',true).first();
5966     },
5967     setDisabled : function(v)
5968     {
5969         var i  = this.inputEl().dom;
5970         if (!v) {
5971             i.removeAttribute('disabled');
5972             return;
5973             
5974         }
5975         i.setAttribute('disabled','true');
5976     },
5977     initEvents : function()
5978     {
5979         
5980         this.inputEl().on("keydown" , this.fireKey,  this);
5981         this.inputEl().on("focus", this.onFocus,  this);
5982         this.inputEl().on("blur", this.onBlur,  this);
5983         
5984         this.inputEl().relayEvent('keyup', this);
5985
5986         // reference to original value for reset
5987         this.originalValue = this.getValue();
5988         //Roo.form.TextField.superclass.initEvents.call(this);
5989         if(this.validationEvent == 'keyup'){
5990             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
5991             this.inputEl().on('keyup', this.filterValidation, this);
5992         }
5993         else if(this.validationEvent !== false){
5994             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
5995         }
5996         
5997         if(this.selectOnFocus){
5998             this.on("focus", this.preFocus, this);
5999             
6000         }
6001         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
6002             this.inputEl().on("keypress", this.filterKeys, this);
6003         }
6004        /* if(this.grow){
6005             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
6006             this.el.on("click", this.autoSize,  this);
6007         }
6008         */
6009         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
6010             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
6011         }
6012         
6013     },
6014     filterValidation : function(e){
6015         if(!e.isNavKeyPress()){
6016             this.validationTask.delay(this.validationDelay);
6017         }
6018     },
6019      /**
6020      * Validates the field value
6021      * @return {Boolean} True if the value is valid, else false
6022      */
6023     validate : function(){
6024         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
6025         if(this.disabled || this.validateValue(this.getRawValue())){
6026             this.clearInvalid();
6027             return true;
6028         }
6029         return false;
6030     },
6031     
6032     
6033     /**
6034      * Validates a value according to the field's validation rules and marks the field as invalid
6035      * if the validation fails
6036      * @param {Mixed} value The value to validate
6037      * @return {Boolean} True if the value is valid, else false
6038      */
6039     validateValue : function(value){
6040         if(value.length < 1)  { // if it's blank
6041              if(this.allowBlank){
6042                 this.clearInvalid();
6043                 return true;
6044              }else{
6045                 this.markInvalid(this.blankText);
6046                 return false;
6047              }
6048         }
6049         if(value.length < this.minLength){
6050             this.markInvalid(String.format(this.minLengthText, this.minLength));
6051             return false;
6052         }
6053         if(value.length > this.maxLength){
6054             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
6055             return false;
6056         }
6057         if(this.vtype){
6058             var vt = Roo.form.VTypes;
6059             if(!vt[this.vtype](value, this)){
6060                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
6061                 return false;
6062             }
6063         }
6064         if(typeof this.validator == "function"){
6065             var msg = this.validator(value);
6066             if(msg !== true){
6067                 this.markInvalid(msg);
6068                 return false;
6069             }
6070         }
6071         if(this.regex && !this.regex.test(value)){
6072             this.markInvalid(this.regexText);
6073             return false;
6074         }
6075         return true;
6076     },
6077
6078     
6079     
6080      // private
6081     fireKey : function(e){
6082         //Roo.log('field ' + e.getKey());
6083         if(e.isNavKeyPress()){
6084             this.fireEvent("specialkey", this, e);
6085         }
6086     },
6087     focus : function (selectText){
6088         if(this.rendered){
6089             this.inputEl().focus();
6090             if(selectText === true){
6091                 this.inputEl().dom.select();
6092             }
6093         }
6094         return this;
6095     } ,
6096     
6097     onFocus : function(){
6098         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
6099            // this.el.addClass(this.focusClass);
6100         }
6101         if(!this.hasFocus){
6102             this.hasFocus = true;
6103             this.startValue = this.getValue();
6104             this.fireEvent("focus", this);
6105         }
6106     },
6107     
6108     beforeBlur : Roo.emptyFn,
6109
6110     
6111     // private
6112     onBlur : function(){
6113         this.beforeBlur();
6114         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
6115             //this.el.removeClass(this.focusClass);
6116         }
6117         this.hasFocus = false;
6118         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
6119             this.validate();
6120         }
6121         var v = this.getValue();
6122         if(String(v) !== String(this.startValue)){
6123             this.fireEvent('change', this, v, this.startValue);
6124         }
6125         this.fireEvent("blur", this);
6126     },
6127     
6128     /**
6129      * Resets the current field value to the originally loaded value and clears any validation messages
6130      */
6131     reset : function(){
6132         this.setValue(this.originalValue);
6133         this.clearInvalid();
6134     },
6135      /**
6136      * Returns the name of the field
6137      * @return {Mixed} name The name field
6138      */
6139     getName: function(){
6140         return this.name;
6141     },
6142      /**
6143      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
6144      * @return {Mixed} value The field value
6145      */
6146     getValue : function(){
6147         return this.inputEl().getValue();
6148     },
6149     /**
6150      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
6151      * @return {Mixed} value The field value
6152      */
6153     getRawValue : function(){
6154         var v = this.inputEl().getValue();
6155         
6156         return v;
6157     },
6158     
6159     /**
6160      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
6161      * @param {Mixed} value The value to set
6162      */
6163     setRawValue : function(v){
6164         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
6165     },
6166     
6167     selectText : function(start, end){
6168         var v = this.getRawValue();
6169         if(v.length > 0){
6170             start = start === undefined ? 0 : start;
6171             end = end === undefined ? v.length : end;
6172             var d = this.inputEl().dom;
6173             if(d.setSelectionRange){
6174                 d.setSelectionRange(start, end);
6175             }else if(d.createTextRange){
6176                 var range = d.createTextRange();
6177                 range.moveStart("character", start);
6178                 range.moveEnd("character", v.length-end);
6179                 range.select();
6180             }
6181         }
6182     },
6183     
6184     /**
6185      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
6186      * @param {Mixed} value The value to set
6187      */
6188     setValue : function(v){
6189         this.value = v;
6190         if(this.rendered){
6191             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
6192             this.validate();
6193         }
6194     },
6195     
6196     /*
6197     processValue : function(value){
6198         if(this.stripCharsRe){
6199             var newValue = value.replace(this.stripCharsRe, '');
6200             if(newValue !== value){
6201                 this.setRawValue(newValue);
6202                 return newValue;
6203             }
6204         }
6205         return value;
6206     },
6207   */
6208     preFocus : function(){
6209         
6210         if(this.selectOnFocus){
6211             this.inputEl().dom.select();
6212         }
6213     },
6214     filterKeys : function(e){
6215         var k = e.getKey();
6216         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
6217             return;
6218         }
6219         var c = e.getCharCode(), cc = String.fromCharCode(c);
6220         if(Roo.isIE && (e.isSpecialKey() || !cc)){
6221             return;
6222         }
6223         if(!this.maskRe.test(cc)){
6224             e.stopEvent();
6225         }
6226     },
6227      /**
6228      * Clear any invalid styles/messages for this field
6229      */
6230     clearInvalid : function(){
6231         
6232         if(!this.el || this.preventMark){ // not rendered
6233             return;
6234         }
6235         this.el.removeClass(this.invalidClass);
6236         /*
6237         switch(this.msgTarget){
6238             case 'qtip':
6239                 this.el.dom.qtip = '';
6240                 break;
6241             case 'title':
6242                 this.el.dom.title = '';
6243                 break;
6244             case 'under':
6245                 if(this.errorEl){
6246                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
6247                 }
6248                 break;
6249             case 'side':
6250                 if(this.errorIcon){
6251                     this.errorIcon.dom.qtip = '';
6252                     this.errorIcon.hide();
6253                     this.un('resize', this.alignErrorIcon, this);
6254                 }
6255                 break;
6256             default:
6257                 var t = Roo.getDom(this.msgTarget);
6258                 t.innerHTML = '';
6259                 t.style.display = 'none';
6260                 break;
6261         }
6262         */
6263         this.fireEvent('valid', this);
6264     },
6265      /**
6266      * Mark this field as invalid
6267      * @param {String} msg The validation message
6268      */
6269     markInvalid : function(msg){
6270         if(!this.el  || this.preventMark){ // not rendered
6271             return;
6272         }
6273         this.el.addClass(this.invalidClass);
6274         /*
6275         msg = msg || this.invalidText;
6276         switch(this.msgTarget){
6277             case 'qtip':
6278                 this.el.dom.qtip = msg;
6279                 this.el.dom.qclass = 'x-form-invalid-tip';
6280                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
6281                     Roo.QuickTips.enable();
6282                 }
6283                 break;
6284             case 'title':
6285                 this.el.dom.title = msg;
6286                 break;
6287             case 'under':
6288                 if(!this.errorEl){
6289                     var elp = this.el.findParent('.x-form-element', 5, true);
6290                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
6291                     this.errorEl.setWidth(elp.getWidth(true)-20);
6292                 }
6293                 this.errorEl.update(msg);
6294                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
6295                 break;
6296             case 'side':
6297                 if(!this.errorIcon){
6298                     var elp = this.el.findParent('.x-form-element', 5, true);
6299                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
6300                 }
6301                 this.alignErrorIcon();
6302                 this.errorIcon.dom.qtip = msg;
6303                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
6304                 this.errorIcon.show();
6305                 this.on('resize', this.alignErrorIcon, this);
6306                 break;
6307             default:
6308                 var t = Roo.getDom(this.msgTarget);
6309                 t.innerHTML = msg;
6310                 t.style.display = this.msgDisplay;
6311                 break;
6312         }
6313         */
6314         this.fireEvent('invalid', this, msg);
6315     },
6316     // private
6317     SafariOnKeyDown : function(event)
6318     {
6319         // this is a workaround for a password hang bug on chrome/ webkit.
6320         
6321         var isSelectAll = false;
6322         
6323         if(this.inputEl().dom.selectionEnd > 0){
6324             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
6325         }
6326         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
6327             event.preventDefault();
6328             this.setValue('');
6329             return;
6330         }
6331         
6332         if(isSelectAll){ // backspace and delete key
6333             
6334             event.preventDefault();
6335             // this is very hacky as keydown always get's upper case.
6336             //
6337             var cc = String.fromCharCode(event.getCharCode());
6338             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
6339             
6340         }
6341     },
6342     adjustWidth : function(tag, w){
6343         tag = tag.toLowerCase();
6344         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
6345             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
6346                 if(tag == 'input'){
6347                     return w + 2;
6348                 }
6349                 if(tag == 'textarea'){
6350                     return w-2;
6351                 }
6352             }else if(Roo.isOpera){
6353                 if(tag == 'input'){
6354                     return w + 2;
6355                 }
6356                 if(tag == 'textarea'){
6357                     return w-2;
6358                 }
6359             }
6360         }
6361         return w;
6362     }
6363     
6364 });
6365
6366  
6367 /*
6368  * - LGPL
6369  *
6370  * Input
6371  * 
6372  */
6373
6374 /**
6375  * @class Roo.bootstrap.TextArea
6376  * @extends Roo.bootstrap.Input
6377  * Bootstrap TextArea class
6378  * @cfg {Number} cols Specifies the visible width of a text area
6379  * @cfg {Number} rows Specifies the visible number of lines in a text area
6380  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
6381  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
6382  * @cfg {string} html text
6383  * 
6384  * @constructor
6385  * Create a new TextArea
6386  * @param {Object} config The config object
6387  */
6388
6389 Roo.bootstrap.TextArea = function(config){
6390     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
6391    
6392 };
6393
6394 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
6395      
6396     cols : false,
6397     rows : 5,
6398     readOnly : false,
6399     warp : 'soft',
6400     resize : false,
6401     value: false,
6402     html: false,
6403     
6404     getAutoCreate : function(){
6405         
6406         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
6407         
6408         var id = Roo.id();
6409         
6410         var cfg = {};
6411         
6412         var input =  {
6413             tag: 'textarea',
6414             id : id,
6415             warp : this.warp,
6416             rows : this.rows,
6417             value : this.value || '',
6418             html: this.html || '',
6419             cls : 'form-control',
6420             placeholder : this.placeholder || '' 
6421             
6422         };
6423         
6424         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
6425             input.maxLength = this.maxLength;
6426         }
6427         
6428         if(this.resize){
6429             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
6430         }
6431         
6432         if(this.cols){
6433             input.cols = this.cols;
6434         }
6435         
6436         if (this.readOnly) {
6437             input.readonly = true;
6438         }
6439         
6440         if (this.name) {
6441             input.name = this.name;
6442         }
6443         
6444         if (this.size) {
6445             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
6446         }
6447         
6448         var settings=this;
6449         ['xs','sm','md','lg'].map(function(size){
6450             if (settings[size]) {
6451                 cfg.cls += ' col-' + size + '-' + settings[size];
6452             }
6453         });
6454         
6455         var inputblock = input;
6456         
6457         if (this.before || this.after) {
6458             
6459             inputblock = {
6460                 cls : 'input-group',
6461                 cn :  [] 
6462             };
6463             if (this.before) {
6464                 inputblock.cn.push({
6465                     tag :'span',
6466                     cls : 'input-group-addon',
6467                     html : this.before
6468                 });
6469             }
6470             inputblock.cn.push(input);
6471             if (this.after) {
6472                 inputblock.cn.push({
6473                     tag :'span',
6474                     cls : 'input-group-addon',
6475                     html : this.after
6476                 });
6477             }
6478             
6479         }
6480         
6481         if (align ==='left' && this.fieldLabel.length) {
6482                 Roo.log("left and has label");
6483                 cfg.cn = [
6484                     
6485                     {
6486                         tag: 'label',
6487                         'for' :  id,
6488                         cls : 'control-label col-sm-' + this.labelWidth,
6489                         html : this.fieldLabel
6490                         
6491                     },
6492                     {
6493                         cls : "col-sm-" + (12 - this.labelWidth), 
6494                         cn: [
6495                             inputblock
6496                         ]
6497                     }
6498                     
6499                 ];
6500         } else if ( this.fieldLabel.length) {
6501                 Roo.log(" label");
6502                  cfg.cn = [
6503                    
6504                     {
6505                         tag: 'label',
6506                         //cls : 'input-group-addon',
6507                         html : this.fieldLabel
6508                         
6509                     },
6510                     
6511                     inputblock
6512                     
6513                 ];
6514
6515         } else {
6516             
6517                    Roo.log(" no label && no align");
6518                 cfg.cn = [
6519                     
6520                         inputblock
6521                     
6522                 ];
6523                 
6524                 
6525         }
6526         
6527         if (this.disabled) {
6528             input.disabled=true;
6529         }
6530         
6531         return cfg;
6532         
6533     },
6534     /**
6535      * return the real textarea element.
6536      */
6537     inputEl: function ()
6538     {
6539         return this.el.select('textarea.form-control',true).first();
6540     }
6541 });
6542
6543  
6544 /*
6545  * - LGPL
6546  *
6547  * trigger field - base class for combo..
6548  * 
6549  */
6550  
6551 /**
6552  * @class Roo.bootstrap.TriggerField
6553  * @extends Roo.bootstrap.Input
6554  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
6555  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
6556  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
6557  * for which you can provide a custom implementation.  For example:
6558  * <pre><code>
6559 var trigger = new Roo.bootstrap.TriggerField();
6560 trigger.onTriggerClick = myTriggerFn;
6561 trigger.applyTo('my-field');
6562 </code></pre>
6563  *
6564  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
6565  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
6566  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
6567  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
6568  * @constructor
6569  * Create a new TriggerField.
6570  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
6571  * to the base TextField)
6572  */
6573 Roo.bootstrap.TriggerField = function(config){
6574     this.mimicing = false;
6575     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
6576 };
6577
6578 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
6579     /**
6580      * @cfg {String} triggerClass A CSS class to apply to the trigger
6581      */
6582      /**
6583      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
6584      */
6585     hideTrigger:false,
6586
6587     /** @cfg {Boolean} grow @hide */
6588     /** @cfg {Number} growMin @hide */
6589     /** @cfg {Number} growMax @hide */
6590
6591     /**
6592      * @hide 
6593      * @method
6594      */
6595     autoSize: Roo.emptyFn,
6596     // private
6597     monitorTab : true,
6598     // private
6599     deferHeight : true,
6600
6601     
6602     actionMode : 'wrap',
6603     
6604     
6605     
6606     getAutoCreate : function(){
6607        
6608         var parent = this.parent();
6609         
6610         var align = this.parentLabelAlign();
6611         
6612         var id = Roo.id();
6613         
6614         var cfg = {
6615             cls: 'form-group' //input-group
6616         };
6617         
6618         
6619         var input =  {
6620             tag: 'input',
6621             id : id,
6622             type : this.inputType,
6623             cls : 'form-control',
6624             autocomplete: 'off',
6625             placeholder : this.placeholder || '' 
6626             
6627         };
6628         if (this.name) {
6629             input.name = this.name;
6630         }
6631         if (this.size) {
6632             input.cls += ' input-' + this.size;
6633         }
6634         
6635         if (this.disabled) {
6636             input.disabled=true;
6637         }
6638         
6639         var inputblock = input;
6640         
6641         if (this.before || this.after) {
6642             
6643             inputblock = {
6644                 cls : 'input-group',
6645                 cn :  [] 
6646             };
6647             if (this.before) {
6648                 inputblock.cn.push({
6649                     tag :'span',
6650                     cls : 'input-group-addon',
6651                     html : this.before
6652                 });
6653             }
6654             inputblock.cn.push(input);
6655             if (this.after) {
6656                 inputblock.cn.push({
6657                     tag :'span',
6658                     cls : 'input-group-addon',
6659                     html : this.after
6660                 });
6661             }
6662             
6663         };
6664         
6665         var box = {
6666             tag: 'div',
6667             cn: [
6668                 {
6669                     tag: 'input',
6670                     type : 'hidden',
6671                     cls: 'form-hidden-field'
6672                 },
6673                 inputblock
6674             ]
6675             
6676         };
6677         
6678         if(this.multiple){
6679             Roo.log('multiple');
6680             
6681             box = {
6682                 tag: 'div',
6683                 cn: [
6684                     {
6685                         tag: 'input',
6686                         type : 'hidden',
6687                         cls: 'form-hidden-field'
6688                     },
6689                     {
6690                         tag: 'ul',
6691                         cls: 'select2-choices',
6692                         cn:[
6693                             {
6694                                 tag: 'li',
6695                                 cls: 'select2-search-field',
6696                                 cn: [
6697
6698                                     inputblock
6699                                 ]
6700                             }
6701                         ]
6702                     }
6703                 ]
6704             }
6705         };
6706         
6707         var combobox = {
6708             cls: 'select2-container input-group',
6709             cn: [
6710                 box,
6711                 {
6712                     tag: 'ul',
6713                     cls: 'typeahead typeahead-long dropdown-menu',
6714                     style: 'display:none'
6715                 }
6716             ]
6717         };
6718         
6719         if(!this.multiple){
6720             combobox.cn.push({
6721                 tag :'span',
6722                 cls : 'input-group-addon btn dropdown-toggle',
6723                 cn : [
6724                     {
6725                         tag: 'span',
6726                         cls: 'caret'
6727                     },
6728                     {
6729                         tag: 'span',
6730                         cls: 'combobox-clear',
6731                         cn  : [
6732                             {
6733                                 tag : 'i',
6734                                 cls: 'icon-remove'
6735                             }
6736                         ]
6737                     }
6738                 ]
6739
6740             })
6741         }
6742         
6743         if(this.multiple){
6744             combobox.cls += ' select2-container-multi';
6745         }
6746         
6747         if (align ==='left' && this.fieldLabel.length) {
6748             
6749                 Roo.log("left and has label");
6750                 cfg.cn = [
6751                     
6752                     {
6753                         tag: 'label',
6754                         'for' :  id,
6755                         cls : 'control-label col-sm-' + this.labelWidth,
6756                         html : this.fieldLabel
6757                         
6758                     },
6759                     {
6760                         cls : "col-sm-" + (12 - this.labelWidth), 
6761                         cn: [
6762                             combobox
6763                         ]
6764                     }
6765                     
6766                 ];
6767         } else if ( this.fieldLabel.length) {
6768                 Roo.log(" label");
6769                  cfg.cn = [
6770                    
6771                     {
6772                         tag: 'label',
6773                         //cls : 'input-group-addon',
6774                         html : this.fieldLabel
6775                         
6776                     },
6777                     
6778                     combobox
6779                     
6780                 ];
6781
6782         } else {
6783             
6784                 Roo.log(" no label && no align");
6785                 cfg = combobox
6786                      
6787                 
6788         }
6789          
6790         var settings=this;
6791         ['xs','sm','md','lg'].map(function(size){
6792             if (settings[size]) {
6793                 cfg.cls += ' col-' + size + '-' + settings[size];
6794             }
6795         });
6796         
6797         return cfg;
6798         
6799     },
6800     
6801     
6802     
6803     // private
6804     onResize : function(w, h){
6805 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
6806 //        if(typeof w == 'number'){
6807 //            var x = w - this.trigger.getWidth();
6808 //            this.inputEl().setWidth(this.adjustWidth('input', x));
6809 //            this.trigger.setStyle('left', x+'px');
6810 //        }
6811     },
6812
6813     // private
6814     adjustSize : Roo.BoxComponent.prototype.adjustSize,
6815
6816     // private
6817     getResizeEl : function(){
6818         return this.inputEl();
6819     },
6820
6821     // private
6822     getPositionEl : function(){
6823         return this.inputEl();
6824     },
6825
6826     // private
6827     alignErrorIcon : function(){
6828         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
6829     },
6830
6831     // private
6832     initEvents : function(){
6833         
6834         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
6835         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
6836         if(!this.multiple){
6837             this.trigger = this.el.select('span.dropdown-toggle',true).first();
6838             if(this.hideTrigger){
6839                 this.trigger.setDisplayed(false);
6840             }
6841             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
6842         }
6843         
6844         if(this.multiple){
6845             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
6846         }
6847         
6848         //this.trigger.addClassOnOver('x-form-trigger-over');
6849         //this.trigger.addClassOnClick('x-form-trigger-click');
6850         
6851         //if(!this.width){
6852         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
6853         //}
6854     },
6855
6856     // private
6857     initTrigger : function(){
6858        
6859     },
6860
6861     // private
6862     onDestroy : function(){
6863         if(this.trigger){
6864             this.trigger.removeAllListeners();
6865           //  this.trigger.remove();
6866         }
6867         //if(this.wrap){
6868         //    this.wrap.remove();
6869         //}
6870         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
6871     },
6872
6873     // private
6874     onFocus : function(){
6875         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
6876         /*
6877         if(!this.mimicing){
6878             this.wrap.addClass('x-trigger-wrap-focus');
6879             this.mimicing = true;
6880             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
6881             if(this.monitorTab){
6882                 this.el.on("keydown", this.checkTab, this);
6883             }
6884         }
6885         */
6886     },
6887
6888     // private
6889     checkTab : function(e){
6890         if(e.getKey() == e.TAB){
6891             this.triggerBlur();
6892         }
6893     },
6894
6895     // private
6896     onBlur : function(){
6897         // do nothing
6898     },
6899
6900     // private
6901     mimicBlur : function(e, t){
6902         /*
6903         if(!this.wrap.contains(t) && this.validateBlur()){
6904             this.triggerBlur();
6905         }
6906         */
6907     },
6908
6909     // private
6910     triggerBlur : function(){
6911         this.mimicing = false;
6912         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
6913         if(this.monitorTab){
6914             this.el.un("keydown", this.checkTab, this);
6915         }
6916         //this.wrap.removeClass('x-trigger-wrap-focus');
6917         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
6918     },
6919
6920     // private
6921     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
6922     validateBlur : function(e, t){
6923         return true;
6924     },
6925
6926     // private
6927     onDisable : function(){
6928         Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
6929         //if(this.wrap){
6930         //    this.wrap.addClass('x-item-disabled');
6931         //}
6932     },
6933
6934     // private
6935     onEnable : function(){
6936         Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
6937         //if(this.wrap){
6938         //    this.el.removeClass('x-item-disabled');
6939         //}
6940     },
6941
6942     // private
6943     onShow : function(){
6944         var ae = this.getActionEl();
6945         
6946         if(ae){
6947             ae.dom.style.display = '';
6948             ae.dom.style.visibility = 'visible';
6949         }
6950     },
6951
6952     // private
6953     
6954     onHide : function(){
6955         var ae = this.getActionEl();
6956         ae.dom.style.display = 'none';
6957     },
6958
6959     /**
6960      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
6961      * by an implementing function.
6962      * @method
6963      * @param {EventObject} e
6964      */
6965     onTriggerClick : Roo.emptyFn
6966 });
6967  /*
6968  * Based on:
6969  * Ext JS Library 1.1.1
6970  * Copyright(c) 2006-2007, Ext JS, LLC.
6971  *
6972  * Originally Released Under LGPL - original licence link has changed is not relivant.
6973  *
6974  * Fork - LGPL
6975  * <script type="text/javascript">
6976  */
6977
6978
6979 /**
6980  * @class Roo.data.SortTypes
6981  * @singleton
6982  * Defines the default sorting (casting?) comparison functions used when sorting data.
6983  */
6984 Roo.data.SortTypes = {
6985     /**
6986      * Default sort that does nothing
6987      * @param {Mixed} s The value being converted
6988      * @return {Mixed} The comparison value
6989      */
6990     none : function(s){
6991         return s;
6992     },
6993     
6994     /**
6995      * The regular expression used to strip tags
6996      * @type {RegExp}
6997      * @property
6998      */
6999     stripTagsRE : /<\/?[^>]+>/gi,
7000     
7001     /**
7002      * Strips all HTML tags to sort on text only
7003      * @param {Mixed} s The value being converted
7004      * @return {String} The comparison value
7005      */
7006     asText : function(s){
7007         return String(s).replace(this.stripTagsRE, "");
7008     },
7009     
7010     /**
7011      * Strips all HTML tags to sort on text only - Case insensitive
7012      * @param {Mixed} s The value being converted
7013      * @return {String} The comparison value
7014      */
7015     asUCText : function(s){
7016         return String(s).toUpperCase().replace(this.stripTagsRE, "");
7017     },
7018     
7019     /**
7020      * Case insensitive string
7021      * @param {Mixed} s The value being converted
7022      * @return {String} The comparison value
7023      */
7024     asUCString : function(s) {
7025         return String(s).toUpperCase();
7026     },
7027     
7028     /**
7029      * Date sorting
7030      * @param {Mixed} s The value being converted
7031      * @return {Number} The comparison value
7032      */
7033     asDate : function(s) {
7034         if(!s){
7035             return 0;
7036         }
7037         if(s instanceof Date){
7038             return s.getTime();
7039         }
7040         return Date.parse(String(s));
7041     },
7042     
7043     /**
7044      * Float sorting
7045      * @param {Mixed} s The value being converted
7046      * @return {Float} The comparison value
7047      */
7048     asFloat : function(s) {
7049         var val = parseFloat(String(s).replace(/,/g, ""));
7050         if(isNaN(val)) val = 0;
7051         return val;
7052     },
7053     
7054     /**
7055      * Integer sorting
7056      * @param {Mixed} s The value being converted
7057      * @return {Number} The comparison value
7058      */
7059     asInt : function(s) {
7060         var val = parseInt(String(s).replace(/,/g, ""));
7061         if(isNaN(val)) val = 0;
7062         return val;
7063     }
7064 };/*
7065  * Based on:
7066  * Ext JS Library 1.1.1
7067  * Copyright(c) 2006-2007, Ext JS, LLC.
7068  *
7069  * Originally Released Under LGPL - original licence link has changed is not relivant.
7070  *
7071  * Fork - LGPL
7072  * <script type="text/javascript">
7073  */
7074
7075 /**
7076 * @class Roo.data.Record
7077  * Instances of this class encapsulate both record <em>definition</em> information, and record
7078  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
7079  * to access Records cached in an {@link Roo.data.Store} object.<br>
7080  * <p>
7081  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
7082  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
7083  * objects.<br>
7084  * <p>
7085  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
7086  * @constructor
7087  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
7088  * {@link #create}. The parameters are the same.
7089  * @param {Array} data An associative Array of data values keyed by the field name.
7090  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
7091  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
7092  * not specified an integer id is generated.
7093  */
7094 Roo.data.Record = function(data, id){
7095     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
7096     this.data = data;
7097 };
7098
7099 /**
7100  * Generate a constructor for a specific record layout.
7101  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
7102  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
7103  * Each field definition object may contain the following properties: <ul>
7104  * <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,
7105  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
7106  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
7107  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
7108  * is being used, then this is a string containing the javascript expression to reference the data relative to 
7109  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
7110  * to the data item relative to the record element. If the mapping expression is the same as the field name,
7111  * this may be omitted.</p></li>
7112  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
7113  * <ul><li>auto (Default, implies no conversion)</li>
7114  * <li>string</li>
7115  * <li>int</li>
7116  * <li>float</li>
7117  * <li>boolean</li>
7118  * <li>date</li></ul></p></li>
7119  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
7120  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
7121  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
7122  * by the Reader into an object that will be stored in the Record. It is passed the
7123  * following parameters:<ul>
7124  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
7125  * </ul></p></li>
7126  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
7127  * </ul>
7128  * <br>usage:<br><pre><code>
7129 var TopicRecord = Roo.data.Record.create(
7130     {name: 'title', mapping: 'topic_title'},
7131     {name: 'author', mapping: 'username'},
7132     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
7133     {name: 'lastPost', mapping: 'post_time', type: 'date'},
7134     {name: 'lastPoster', mapping: 'user2'},
7135     {name: 'excerpt', mapping: 'post_text'}
7136 );
7137
7138 var myNewRecord = new TopicRecord({
7139     title: 'Do my job please',
7140     author: 'noobie',
7141     totalPosts: 1,
7142     lastPost: new Date(),
7143     lastPoster: 'Animal',
7144     excerpt: 'No way dude!'
7145 });
7146 myStore.add(myNewRecord);
7147 </code></pre>
7148  * @method create
7149  * @static
7150  */
7151 Roo.data.Record.create = function(o){
7152     var f = function(){
7153         f.superclass.constructor.apply(this, arguments);
7154     };
7155     Roo.extend(f, Roo.data.Record);
7156     var p = f.prototype;
7157     p.fields = new Roo.util.MixedCollection(false, function(field){
7158         return field.name;
7159     });
7160     for(var i = 0, len = o.length; i < len; i++){
7161         p.fields.add(new Roo.data.Field(o[i]));
7162     }
7163     f.getField = function(name){
7164         return p.fields.get(name);  
7165     };
7166     return f;
7167 };
7168
7169 Roo.data.Record.AUTO_ID = 1000;
7170 Roo.data.Record.EDIT = 'edit';
7171 Roo.data.Record.REJECT = 'reject';
7172 Roo.data.Record.COMMIT = 'commit';
7173
7174 Roo.data.Record.prototype = {
7175     /**
7176      * Readonly flag - true if this record has been modified.
7177      * @type Boolean
7178      */
7179     dirty : false,
7180     editing : false,
7181     error: null,
7182     modified: null,
7183
7184     // private
7185     join : function(store){
7186         this.store = store;
7187     },
7188
7189     /**
7190      * Set the named field to the specified value.
7191      * @param {String} name The name of the field to set.
7192      * @param {Object} value The value to set the field to.
7193      */
7194     set : function(name, value){
7195         if(this.data[name] == value){
7196             return;
7197         }
7198         this.dirty = true;
7199         if(!this.modified){
7200             this.modified = {};
7201         }
7202         if(typeof this.modified[name] == 'undefined'){
7203             this.modified[name] = this.data[name];
7204         }
7205         this.data[name] = value;
7206         if(!this.editing && this.store){
7207             this.store.afterEdit(this);
7208         }       
7209     },
7210
7211     /**
7212      * Get the value of the named field.
7213      * @param {String} name The name of the field to get the value of.
7214      * @return {Object} The value of the field.
7215      */
7216     get : function(name){
7217         return this.data[name]; 
7218     },
7219
7220     // private
7221     beginEdit : function(){
7222         this.editing = true;
7223         this.modified = {}; 
7224     },
7225
7226     // private
7227     cancelEdit : function(){
7228         this.editing = false;
7229         delete this.modified;
7230     },
7231
7232     // private
7233     endEdit : function(){
7234         this.editing = false;
7235         if(this.dirty && this.store){
7236             this.store.afterEdit(this);
7237         }
7238     },
7239
7240     /**
7241      * Usually called by the {@link Roo.data.Store} which owns the Record.
7242      * Rejects all changes made to the Record since either creation, or the last commit operation.
7243      * Modified fields are reverted to their original values.
7244      * <p>
7245      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
7246      * of reject operations.
7247      */
7248     reject : function(){
7249         var m = this.modified;
7250         for(var n in m){
7251             if(typeof m[n] != "function"){
7252                 this.data[n] = m[n];
7253             }
7254         }
7255         this.dirty = false;
7256         delete this.modified;
7257         this.editing = false;
7258         if(this.store){
7259             this.store.afterReject(this);
7260         }
7261     },
7262
7263     /**
7264      * Usually called by the {@link Roo.data.Store} which owns the Record.
7265      * Commits all changes made to the Record since either creation, or the last commit operation.
7266      * <p>
7267      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
7268      * of commit operations.
7269      */
7270     commit : function(){
7271         this.dirty = false;
7272         delete this.modified;
7273         this.editing = false;
7274         if(this.store){
7275             this.store.afterCommit(this);
7276         }
7277     },
7278
7279     // private
7280     hasError : function(){
7281         return this.error != null;
7282     },
7283
7284     // private
7285     clearError : function(){
7286         this.error = null;
7287     },
7288
7289     /**
7290      * Creates a copy of this record.
7291      * @param {String} id (optional) A new record id if you don't want to use this record's id
7292      * @return {Record}
7293      */
7294     copy : function(newId) {
7295         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
7296     }
7297 };/*
7298  * Based on:
7299  * Ext JS Library 1.1.1
7300  * Copyright(c) 2006-2007, Ext JS, LLC.
7301  *
7302  * Originally Released Under LGPL - original licence link has changed is not relivant.
7303  *
7304  * Fork - LGPL
7305  * <script type="text/javascript">
7306  */
7307
7308
7309
7310 /**
7311  * @class Roo.data.Store
7312  * @extends Roo.util.Observable
7313  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
7314  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
7315  * <p>
7316  * 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
7317  * has no knowledge of the format of the data returned by the Proxy.<br>
7318  * <p>
7319  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
7320  * instances from the data object. These records are cached and made available through accessor functions.
7321  * @constructor
7322  * Creates a new Store.
7323  * @param {Object} config A config object containing the objects needed for the Store to access data,
7324  * and read the data into Records.
7325  */
7326 Roo.data.Store = function(config){
7327     this.data = new Roo.util.MixedCollection(false);
7328     this.data.getKey = function(o){
7329         return o.id;
7330     };
7331     this.baseParams = {};
7332     // private
7333     this.paramNames = {
7334         "start" : "start",
7335         "limit" : "limit",
7336         "sort" : "sort",
7337         "dir" : "dir",
7338         "multisort" : "_multisort"
7339     };
7340
7341     if(config && config.data){
7342         this.inlineData = config.data;
7343         delete config.data;
7344     }
7345
7346     Roo.apply(this, config);
7347     
7348     if(this.reader){ // reader passed
7349         this.reader = Roo.factory(this.reader, Roo.data);
7350         this.reader.xmodule = this.xmodule || false;
7351         if(!this.recordType){
7352             this.recordType = this.reader.recordType;
7353         }
7354         if(this.reader.onMetaChange){
7355             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
7356         }
7357     }
7358
7359     if(this.recordType){
7360         this.fields = this.recordType.prototype.fields;
7361     }
7362     this.modified = [];
7363
7364     this.addEvents({
7365         /**
7366          * @event datachanged
7367          * Fires when the data cache has changed, and a widget which is using this Store
7368          * as a Record cache should refresh its view.
7369          * @param {Store} this
7370          */
7371         datachanged : true,
7372         /**
7373          * @event metachange
7374          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
7375          * @param {Store} this
7376          * @param {Object} meta The JSON metadata
7377          */
7378         metachange : true,
7379         /**
7380          * @event add
7381          * Fires when Records have been added to the Store
7382          * @param {Store} this
7383          * @param {Roo.data.Record[]} records The array of Records added
7384          * @param {Number} index The index at which the record(s) were added
7385          */
7386         add : true,
7387         /**
7388          * @event remove
7389          * Fires when a Record has been removed from the Store
7390          * @param {Store} this
7391          * @param {Roo.data.Record} record The Record that was removed
7392          * @param {Number} index The index at which the record was removed
7393          */
7394         remove : true,
7395         /**
7396          * @event update
7397          * Fires when a Record has been updated
7398          * @param {Store} this
7399          * @param {Roo.data.Record} record The Record that was updated
7400          * @param {String} operation The update operation being performed.  Value may be one of:
7401          * <pre><code>
7402  Roo.data.Record.EDIT
7403  Roo.data.Record.REJECT
7404  Roo.data.Record.COMMIT
7405          * </code></pre>
7406          */
7407         update : true,
7408         /**
7409          * @event clear
7410          * Fires when the data cache has been cleared.
7411          * @param {Store} this
7412          */
7413         clear : true,
7414         /**
7415          * @event beforeload
7416          * Fires before a request is made for a new data object.  If the beforeload handler returns false
7417          * the load action will be canceled.
7418          * @param {Store} this
7419          * @param {Object} options The loading options that were specified (see {@link #load} for details)
7420          */
7421         beforeload : true,
7422         /**
7423          * @event beforeloadadd
7424          * Fires after a new set of Records has been loaded.
7425          * @param {Store} this
7426          * @param {Roo.data.Record[]} records The Records that were loaded
7427          * @param {Object} options The loading options that were specified (see {@link #load} for details)
7428          */
7429         beforeloadadd : true,
7430         /**
7431          * @event load
7432          * Fires after a new set of Records has been loaded, before they are added to the store.
7433          * @param {Store} this
7434          * @param {Roo.data.Record[]} records The Records that were loaded
7435          * @param {Object} options The loading options that were specified (see {@link #load} for details)
7436          * @params {Object} return from reader
7437          */
7438         load : true,
7439         /**
7440          * @event loadexception
7441          * Fires if an exception occurs in the Proxy during loading.
7442          * Called with the signature of the Proxy's "loadexception" event.
7443          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
7444          * 
7445          * @param {Proxy} 
7446          * @param {Object} return from JsonData.reader() - success, totalRecords, records
7447          * @param {Object} load options 
7448          * @param {Object} jsonData from your request (normally this contains the Exception)
7449          */
7450         loadexception : true
7451     });
7452     
7453     if(this.proxy){
7454         this.proxy = Roo.factory(this.proxy, Roo.data);
7455         this.proxy.xmodule = this.xmodule || false;
7456         this.relayEvents(this.proxy,  ["loadexception"]);
7457     }
7458     this.sortToggle = {};
7459     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
7460
7461     Roo.data.Store.superclass.constructor.call(this);
7462
7463     if(this.inlineData){
7464         this.loadData(this.inlineData);
7465         delete this.inlineData;
7466     }
7467 };
7468
7469 Roo.extend(Roo.data.Store, Roo.util.Observable, {
7470      /**
7471     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
7472     * without a remote query - used by combo/forms at present.
7473     */
7474     
7475     /**
7476     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
7477     */
7478     /**
7479     * @cfg {Array} data Inline data to be loaded when the store is initialized.
7480     */
7481     /**
7482     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
7483     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
7484     */
7485     /**
7486     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
7487     * on any HTTP request
7488     */
7489     /**
7490     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
7491     */
7492     /**
7493     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
7494     */
7495     multiSort: false,
7496     /**
7497     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
7498     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
7499     */
7500     remoteSort : false,
7501
7502     /**
7503     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
7504      * loaded or when a record is removed. (defaults to false).
7505     */
7506     pruneModifiedRecords : false,
7507
7508     // private
7509     lastOptions : null,
7510
7511     /**
7512      * Add Records to the Store and fires the add event.
7513      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
7514      */
7515     add : function(records){
7516         records = [].concat(records);
7517         for(var i = 0, len = records.length; i < len; i++){
7518             records[i].join(this);
7519         }
7520         var index = this.data.length;
7521         this.data.addAll(records);
7522         this.fireEvent("add", this, records, index);
7523     },
7524
7525     /**
7526      * Remove a Record from the Store and fires the remove event.
7527      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
7528      */
7529     remove : function(record){
7530         var index = this.data.indexOf(record);
7531         this.data.removeAt(index);
7532         if(this.pruneModifiedRecords){
7533             this.modified.remove(record);
7534         }
7535         this.fireEvent("remove", this, record, index);
7536     },
7537
7538     /**
7539      * Remove all Records from the Store and fires the clear event.
7540      */
7541     removeAll : function(){
7542         this.data.clear();
7543         if(this.pruneModifiedRecords){
7544             this.modified = [];
7545         }
7546         this.fireEvent("clear", this);
7547     },
7548
7549     /**
7550      * Inserts Records to the Store at the given index and fires the add event.
7551      * @param {Number} index The start index at which to insert the passed Records.
7552      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
7553      */
7554     insert : function(index, records){
7555         records = [].concat(records);
7556         for(var i = 0, len = records.length; i < len; i++){
7557             this.data.insert(index, records[i]);
7558             records[i].join(this);
7559         }
7560         this.fireEvent("add", this, records, index);
7561     },
7562
7563     /**
7564      * Get the index within the cache of the passed Record.
7565      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
7566      * @return {Number} The index of the passed Record. Returns -1 if not found.
7567      */
7568     indexOf : function(record){
7569         return this.data.indexOf(record);
7570     },
7571
7572     /**
7573      * Get the index within the cache of the Record with the passed id.
7574      * @param {String} id The id of the Record to find.
7575      * @return {Number} The index of the Record. Returns -1 if not found.
7576      */
7577     indexOfId : function(id){
7578         return this.data.indexOfKey(id);
7579     },
7580
7581     /**
7582      * Get the Record with the specified id.
7583      * @param {String} id The id of the Record to find.
7584      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
7585      */
7586     getById : function(id){
7587         return this.data.key(id);
7588     },
7589
7590     /**
7591      * Get the Record at the specified index.
7592      * @param {Number} index The index of the Record to find.
7593      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
7594      */
7595     getAt : function(index){
7596         return this.data.itemAt(index);
7597     },
7598
7599     /**
7600      * Returns a range of Records between specified indices.
7601      * @param {Number} startIndex (optional) The starting index (defaults to 0)
7602      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
7603      * @return {Roo.data.Record[]} An array of Records
7604      */
7605     getRange : function(start, end){
7606         return this.data.getRange(start, end);
7607     },
7608
7609     // private
7610     storeOptions : function(o){
7611         o = Roo.apply({}, o);
7612         delete o.callback;
7613         delete o.scope;
7614         this.lastOptions = o;
7615     },
7616
7617     /**
7618      * Loads the Record cache from the configured Proxy using the configured Reader.
7619      * <p>
7620      * If using remote paging, then the first load call must specify the <em>start</em>
7621      * and <em>limit</em> properties in the options.params property to establish the initial
7622      * position within the dataset, and the number of Records to cache on each read from the Proxy.
7623      * <p>
7624      * <strong>It is important to note that for remote data sources, loading is asynchronous,
7625      * and this call will return before the new data has been loaded. Perform any post-processing
7626      * in a callback function, or in a "load" event handler.</strong>
7627      * <p>
7628      * @param {Object} options An object containing properties which control loading options:<ul>
7629      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
7630      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
7631      * passed the following arguments:<ul>
7632      * <li>r : Roo.data.Record[]</li>
7633      * <li>options: Options object from the load call</li>
7634      * <li>success: Boolean success indicator</li></ul></li>
7635      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
7636      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
7637      * </ul>
7638      */
7639     load : function(options){
7640         options = options || {};
7641         if(this.fireEvent("beforeload", this, options) !== false){
7642             this.storeOptions(options);
7643             var p = Roo.apply(options.params || {}, this.baseParams);
7644             // if meta was not loaded from remote source.. try requesting it.
7645             if (!this.reader.metaFromRemote) {
7646                 p._requestMeta = 1;
7647             }
7648             if(this.sortInfo && this.remoteSort){
7649                 var pn = this.paramNames;
7650                 p[pn["sort"]] = this.sortInfo.field;
7651                 p[pn["dir"]] = this.sortInfo.direction;
7652             }
7653             if (this.multiSort) {
7654                 var pn = this.paramNames;
7655                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
7656             }
7657             
7658             this.proxy.load(p, this.reader, this.loadRecords, this, options);
7659         }
7660     },
7661
7662     /**
7663      * Reloads the Record cache from the configured Proxy using the configured Reader and
7664      * the options from the last load operation performed.
7665      * @param {Object} options (optional) An object containing properties which may override the options
7666      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
7667      * the most recently used options are reused).
7668      */
7669     reload : function(options){
7670         this.load(Roo.applyIf(options||{}, this.lastOptions));
7671     },
7672
7673     // private
7674     // Called as a callback by the Reader during a load operation.
7675     loadRecords : function(o, options, success){
7676         if(!o || success === false){
7677             if(success !== false){
7678                 this.fireEvent("load", this, [], options, o);
7679             }
7680             if(options.callback){
7681                 options.callback.call(options.scope || this, [], options, false);
7682             }
7683             return;
7684         }
7685         // if data returned failure - throw an exception.
7686         if (o.success === false) {
7687             // show a message if no listener is registered.
7688             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
7689                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
7690             }
7691             // loadmask wil be hooked into this..
7692             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
7693             return;
7694         }
7695         var r = o.records, t = o.totalRecords || r.length;
7696         
7697         this.fireEvent("beforeloadadd", this, r, options, o);
7698         
7699         if(!options || options.add !== true){
7700             if(this.pruneModifiedRecords){
7701                 this.modified = [];
7702             }
7703             for(var i = 0, len = r.length; i < len; i++){
7704                 r[i].join(this);
7705             }
7706             if(this.snapshot){
7707                 this.data = this.snapshot;
7708                 delete this.snapshot;
7709             }
7710             this.data.clear();
7711             this.data.addAll(r);
7712             this.totalLength = t;
7713             this.applySort();
7714             this.fireEvent("datachanged", this);
7715         }else{
7716             this.totalLength = Math.max(t, this.data.length+r.length);
7717             this.add(r);
7718         }
7719         this.fireEvent("load", this, r, options, o);
7720         if(options.callback){
7721             options.callback.call(options.scope || this, r, options, true);
7722         }
7723     },
7724
7725
7726     /**
7727      * Loads data from a passed data block. A Reader which understands the format of the data
7728      * must have been configured in the constructor.
7729      * @param {Object} data The data block from which to read the Records.  The format of the data expected
7730      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
7731      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
7732      */
7733     loadData : function(o, append){
7734         var r = this.reader.readRecords(o);
7735         this.loadRecords(r, {add: append}, true);
7736     },
7737
7738     /**
7739      * Gets the number of cached records.
7740      * <p>
7741      * <em>If using paging, this may not be the total size of the dataset. If the data object
7742      * used by the Reader contains the dataset size, then the getTotalCount() function returns
7743      * the data set size</em>
7744      */
7745     getCount : function(){
7746         return this.data.length || 0;
7747     },
7748
7749     /**
7750      * Gets the total number of records in the dataset as returned by the server.
7751      * <p>
7752      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
7753      * the dataset size</em>
7754      */
7755     getTotalCount : function(){
7756         return this.totalLength || 0;
7757     },
7758
7759     /**
7760      * Returns the sort state of the Store as an object with two properties:
7761      * <pre><code>
7762  field {String} The name of the field by which the Records are sorted
7763  direction {String} The sort order, "ASC" or "DESC"
7764      * </code></pre>
7765      */
7766     getSortState : function(){
7767         return this.sortInfo;
7768     },
7769
7770     // private
7771     applySort : function(){
7772         if(this.sortInfo && !this.remoteSort){
7773             var s = this.sortInfo, f = s.field;
7774             var st = this.fields.get(f).sortType;
7775             var fn = function(r1, r2){
7776                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
7777                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
7778             };
7779             this.data.sort(s.direction, fn);
7780             if(this.snapshot && this.snapshot != this.data){
7781                 this.snapshot.sort(s.direction, fn);
7782             }
7783         }
7784     },
7785
7786     /**
7787      * Sets the default sort column and order to be used by the next load operation.
7788      * @param {String} fieldName The name of the field to sort by.
7789      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
7790      */
7791     setDefaultSort : function(field, dir){
7792         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
7793     },
7794
7795     /**
7796      * Sort the Records.
7797      * If remote sorting is used, the sort is performed on the server, and the cache is
7798      * reloaded. If local sorting is used, the cache is sorted internally.
7799      * @param {String} fieldName The name of the field to sort by.
7800      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
7801      */
7802     sort : function(fieldName, dir){
7803         var f = this.fields.get(fieldName);
7804         if(!dir){
7805             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
7806             
7807             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
7808                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
7809             }else{
7810                 dir = f.sortDir;
7811             }
7812         }
7813         this.sortToggle[f.name] = dir;
7814         this.sortInfo = {field: f.name, direction: dir};
7815         if(!this.remoteSort){
7816             this.applySort();
7817             this.fireEvent("datachanged", this);
7818         }else{
7819             this.load(this.lastOptions);
7820         }
7821     },
7822
7823     /**
7824      * Calls the specified function for each of the Records in the cache.
7825      * @param {Function} fn The function to call. The Record is passed as the first parameter.
7826      * Returning <em>false</em> aborts and exits the iteration.
7827      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
7828      */
7829     each : function(fn, scope){
7830         this.data.each(fn, scope);
7831     },
7832
7833     /**
7834      * Gets all records modified since the last commit.  Modified records are persisted across load operations
7835      * (e.g., during paging).
7836      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
7837      */
7838     getModifiedRecords : function(){
7839         return this.modified;
7840     },
7841
7842     // private
7843     createFilterFn : function(property, value, anyMatch){
7844         if(!value.exec){ // not a regex
7845             value = String(value);
7846             if(value.length == 0){
7847                 return false;
7848             }
7849             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
7850         }
7851         return function(r){
7852             return value.test(r.data[property]);
7853         };
7854     },
7855
7856     /**
7857      * Sums the value of <i>property</i> for each record between start and end and returns the result.
7858      * @param {String} property A field on your records
7859      * @param {Number} start The record index to start at (defaults to 0)
7860      * @param {Number} end The last record index to include (defaults to length - 1)
7861      * @return {Number} The sum
7862      */
7863     sum : function(property, start, end){
7864         var rs = this.data.items, v = 0;
7865         start = start || 0;
7866         end = (end || end === 0) ? end : rs.length-1;
7867
7868         for(var i = start; i <= end; i++){
7869             v += (rs[i].data[property] || 0);
7870         }
7871         return v;
7872     },
7873
7874     /**
7875      * Filter the records by a specified property.
7876      * @param {String} field A field on your records
7877      * @param {String/RegExp} value Either a string that the field
7878      * should start with or a RegExp to test against the field
7879      * @param {Boolean} anyMatch True to match any part not just the beginning
7880      */
7881     filter : function(property, value, anyMatch){
7882         var fn = this.createFilterFn(property, value, anyMatch);
7883         return fn ? this.filterBy(fn) : this.clearFilter();
7884     },
7885
7886     /**
7887      * Filter by a function. The specified function will be called with each
7888      * record in this data source. If the function returns true the record is included,
7889      * otherwise it is filtered.
7890      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
7891      * @param {Object} scope (optional) The scope of the function (defaults to this)
7892      */
7893     filterBy : function(fn, scope){
7894         this.snapshot = this.snapshot || this.data;
7895         this.data = this.queryBy(fn, scope||this);
7896         this.fireEvent("datachanged", this);
7897     },
7898
7899     /**
7900      * Query the records by a specified property.
7901      * @param {String} field A field on your records
7902      * @param {String/RegExp} value Either a string that the field
7903      * should start with or a RegExp to test against the field
7904      * @param {Boolean} anyMatch True to match any part not just the beginning
7905      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
7906      */
7907     query : function(property, value, anyMatch){
7908         var fn = this.createFilterFn(property, value, anyMatch);
7909         return fn ? this.queryBy(fn) : this.data.clone();
7910     },
7911
7912     /**
7913      * Query by a function. The specified function will be called with each
7914      * record in this data source. If the function returns true the record is included
7915      * in the results.
7916      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
7917      * @param {Object} scope (optional) The scope of the function (defaults to this)
7918       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
7919      **/
7920     queryBy : function(fn, scope){
7921         var data = this.snapshot || this.data;
7922         return data.filterBy(fn, scope||this);
7923     },
7924
7925     /**
7926      * Collects unique values for a particular dataIndex from this store.
7927      * @param {String} dataIndex The property to collect
7928      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
7929      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
7930      * @return {Array} An array of the unique values
7931      **/
7932     collect : function(dataIndex, allowNull, bypassFilter){
7933         var d = (bypassFilter === true && this.snapshot) ?
7934                 this.snapshot.items : this.data.items;
7935         var v, sv, r = [], l = {};
7936         for(var i = 0, len = d.length; i < len; i++){
7937             v = d[i].data[dataIndex];
7938             sv = String(v);
7939             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
7940                 l[sv] = true;
7941                 r[r.length] = v;
7942             }
7943         }
7944         return r;
7945     },
7946
7947     /**
7948      * Revert to a view of the Record cache with no filtering applied.
7949      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
7950      */
7951     clearFilter : function(suppressEvent){
7952         if(this.snapshot && this.snapshot != this.data){
7953             this.data = this.snapshot;
7954             delete this.snapshot;
7955             if(suppressEvent !== true){
7956                 this.fireEvent("datachanged", this);
7957             }
7958         }
7959     },
7960
7961     // private
7962     afterEdit : function(record){
7963         if(this.modified.indexOf(record) == -1){
7964             this.modified.push(record);
7965         }
7966         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
7967     },
7968     
7969     // private
7970     afterReject : function(record){
7971         this.modified.remove(record);
7972         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
7973     },
7974
7975     // private
7976     afterCommit : function(record){
7977         this.modified.remove(record);
7978         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
7979     },
7980
7981     /**
7982      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
7983      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
7984      */
7985     commitChanges : function(){
7986         var m = this.modified.slice(0);
7987         this.modified = [];
7988         for(var i = 0, len = m.length; i < len; i++){
7989             m[i].commit();
7990         }
7991     },
7992
7993     /**
7994      * Cancel outstanding changes on all changed records.
7995      */
7996     rejectChanges : function(){
7997         var m = this.modified.slice(0);
7998         this.modified = [];
7999         for(var i = 0, len = m.length; i < len; i++){
8000             m[i].reject();
8001         }
8002     },
8003
8004     onMetaChange : function(meta, rtype, o){
8005         this.recordType = rtype;
8006         this.fields = rtype.prototype.fields;
8007         delete this.snapshot;
8008         this.sortInfo = meta.sortInfo || this.sortInfo;
8009         this.modified = [];
8010         this.fireEvent('metachange', this, this.reader.meta);
8011     },
8012     
8013     moveIndex : function(data, type)
8014     {
8015         var index = this.indexOf(data);
8016         
8017         var newIndex = index + type;
8018         
8019         this.remove(data);
8020         
8021         this.insert(newIndex, data);
8022         
8023     }
8024 });/*
8025  * Based on:
8026  * Ext JS Library 1.1.1
8027  * Copyright(c) 2006-2007, Ext JS, LLC.
8028  *
8029  * Originally Released Under LGPL - original licence link has changed is not relivant.
8030  *
8031  * Fork - LGPL
8032  * <script type="text/javascript">
8033  */
8034
8035 /**
8036  * @class Roo.data.SimpleStore
8037  * @extends Roo.data.Store
8038  * Small helper class to make creating Stores from Array data easier.
8039  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
8040  * @cfg {Array} fields An array of field definition objects, or field name strings.
8041  * @cfg {Array} data The multi-dimensional array of data
8042  * @constructor
8043  * @param {Object} config
8044  */
8045 Roo.data.SimpleStore = function(config){
8046     Roo.data.SimpleStore.superclass.constructor.call(this, {
8047         isLocal : true,
8048         reader: new Roo.data.ArrayReader({
8049                 id: config.id
8050             },
8051             Roo.data.Record.create(config.fields)
8052         ),
8053         proxy : new Roo.data.MemoryProxy(config.data)
8054     });
8055     this.load();
8056 };
8057 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
8058  * Based on:
8059  * Ext JS Library 1.1.1
8060  * Copyright(c) 2006-2007, Ext JS, LLC.
8061  *
8062  * Originally Released Under LGPL - original licence link has changed is not relivant.
8063  *
8064  * Fork - LGPL
8065  * <script type="text/javascript">
8066  */
8067
8068 /**
8069 /**
8070  * @extends Roo.data.Store
8071  * @class Roo.data.JsonStore
8072  * Small helper class to make creating Stores for JSON data easier. <br/>
8073 <pre><code>
8074 var store = new Roo.data.JsonStore({
8075     url: 'get-images.php',
8076     root: 'images',
8077     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
8078 });
8079 </code></pre>
8080  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
8081  * JsonReader and HttpProxy (unless inline data is provided).</b>
8082  * @cfg {Array} fields An array of field definition objects, or field name strings.
8083  * @constructor
8084  * @param {Object} config
8085  */
8086 Roo.data.JsonStore = function(c){
8087     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
8088         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
8089         reader: new Roo.data.JsonReader(c, c.fields)
8090     }));
8091 };
8092 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
8093  * Based on:
8094  * Ext JS Library 1.1.1
8095  * Copyright(c) 2006-2007, Ext JS, LLC.
8096  *
8097  * Originally Released Under LGPL - original licence link has changed is not relivant.
8098  *
8099  * Fork - LGPL
8100  * <script type="text/javascript">
8101  */
8102
8103  
8104 Roo.data.Field = function(config){
8105     if(typeof config == "string"){
8106         config = {name: config};
8107     }
8108     Roo.apply(this, config);
8109     
8110     if(!this.type){
8111         this.type = "auto";
8112     }
8113     
8114     var st = Roo.data.SortTypes;
8115     // named sortTypes are supported, here we look them up
8116     if(typeof this.sortType == "string"){
8117         this.sortType = st[this.sortType];
8118     }
8119     
8120     // set default sortType for strings and dates
8121     if(!this.sortType){
8122         switch(this.type){
8123             case "string":
8124                 this.sortType = st.asUCString;
8125                 break;
8126             case "date":
8127                 this.sortType = st.asDate;
8128                 break;
8129             default:
8130                 this.sortType = st.none;
8131         }
8132     }
8133
8134     // define once
8135     var stripRe = /[\$,%]/g;
8136
8137     // prebuilt conversion function for this field, instead of
8138     // switching every time we're reading a value
8139     if(!this.convert){
8140         var cv, dateFormat = this.dateFormat;
8141         switch(this.type){
8142             case "":
8143             case "auto":
8144             case undefined:
8145                 cv = function(v){ return v; };
8146                 break;
8147             case "string":
8148                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
8149                 break;
8150             case "int":
8151                 cv = function(v){
8152                     return v !== undefined && v !== null && v !== '' ?
8153                            parseInt(String(v).replace(stripRe, ""), 10) : '';
8154                     };
8155                 break;
8156             case "float":
8157                 cv = function(v){
8158                     return v !== undefined && v !== null && v !== '' ?
8159                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
8160                     };
8161                 break;
8162             case "bool":
8163             case "boolean":
8164                 cv = function(v){ return v === true || v === "true" || v == 1; };
8165                 break;
8166             case "date":
8167                 cv = function(v){
8168                     if(!v){
8169                         return '';
8170                     }
8171                     if(v instanceof Date){
8172                         return v;
8173                     }
8174                     if(dateFormat){
8175                         if(dateFormat == "timestamp"){
8176                             return new Date(v*1000);
8177                         }
8178                         return Date.parseDate(v, dateFormat);
8179                     }
8180                     var parsed = Date.parse(v);
8181                     return parsed ? new Date(parsed) : null;
8182                 };
8183              break;
8184             
8185         }
8186         this.convert = cv;
8187     }
8188 };
8189
8190 Roo.data.Field.prototype = {
8191     dateFormat: null,
8192     defaultValue: "",
8193     mapping: null,
8194     sortType : null,
8195     sortDir : "ASC"
8196 };/*
8197  * Based on:
8198  * Ext JS Library 1.1.1
8199  * Copyright(c) 2006-2007, Ext JS, LLC.
8200  *
8201  * Originally Released Under LGPL - original licence link has changed is not relivant.
8202  *
8203  * Fork - LGPL
8204  * <script type="text/javascript">
8205  */
8206  
8207 // Base class for reading structured data from a data source.  This class is intended to be
8208 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
8209
8210 /**
8211  * @class Roo.data.DataReader
8212  * Base class for reading structured data from a data source.  This class is intended to be
8213  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
8214  */
8215
8216 Roo.data.DataReader = function(meta, recordType){
8217     
8218     this.meta = meta;
8219     
8220     this.recordType = recordType instanceof Array ? 
8221         Roo.data.Record.create(recordType) : recordType;
8222 };
8223
8224 Roo.data.DataReader.prototype = {
8225      /**
8226      * Create an empty record
8227      * @param {Object} data (optional) - overlay some values
8228      * @return {Roo.data.Record} record created.
8229      */
8230     newRow :  function(d) {
8231         var da =  {};
8232         this.recordType.prototype.fields.each(function(c) {
8233             switch( c.type) {
8234                 case 'int' : da[c.name] = 0; break;
8235                 case 'date' : da[c.name] = new Date(); break;
8236                 case 'float' : da[c.name] = 0.0; break;
8237                 case 'boolean' : da[c.name] = false; break;
8238                 default : da[c.name] = ""; break;
8239             }
8240             
8241         });
8242         return new this.recordType(Roo.apply(da, d));
8243     }
8244     
8245 };/*
8246  * Based on:
8247  * Ext JS Library 1.1.1
8248  * Copyright(c) 2006-2007, Ext JS, LLC.
8249  *
8250  * Originally Released Under LGPL - original licence link has changed is not relivant.
8251  *
8252  * Fork - LGPL
8253  * <script type="text/javascript">
8254  */
8255
8256 /**
8257  * @class Roo.data.DataProxy
8258  * @extends Roo.data.Observable
8259  * This class is an abstract base class for implementations which provide retrieval of
8260  * unformatted data objects.<br>
8261  * <p>
8262  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
8263  * (of the appropriate type which knows how to parse the data object) to provide a block of
8264  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
8265  * <p>
8266  * Custom implementations must implement the load method as described in
8267  * {@link Roo.data.HttpProxy#load}.
8268  */
8269 Roo.data.DataProxy = function(){
8270     this.addEvents({
8271         /**
8272          * @event beforeload
8273          * Fires before a network request is made to retrieve a data object.
8274          * @param {Object} This DataProxy object.
8275          * @param {Object} params The params parameter to the load function.
8276          */
8277         beforeload : true,
8278         /**
8279          * @event load
8280          * Fires before the load method's callback is called.
8281          * @param {Object} This DataProxy object.
8282          * @param {Object} o The data object.
8283          * @param {Object} arg The callback argument object passed to the load function.
8284          */
8285         load : true,
8286         /**
8287          * @event loadexception
8288          * Fires if an Exception occurs during data retrieval.
8289          * @param {Object} This DataProxy object.
8290          * @param {Object} o The data object.
8291          * @param {Object} arg The callback argument object passed to the load function.
8292          * @param {Object} e The Exception.
8293          */
8294         loadexception : true
8295     });
8296     Roo.data.DataProxy.superclass.constructor.call(this);
8297 };
8298
8299 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
8300
8301     /**
8302      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
8303      */
8304 /*
8305  * Based on:
8306  * Ext JS Library 1.1.1
8307  * Copyright(c) 2006-2007, Ext JS, LLC.
8308  *
8309  * Originally Released Under LGPL - original licence link has changed is not relivant.
8310  *
8311  * Fork - LGPL
8312  * <script type="text/javascript">
8313  */
8314 /**
8315  * @class Roo.data.MemoryProxy
8316  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
8317  * to the Reader when its load method is called.
8318  * @constructor
8319  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
8320  */
8321 Roo.data.MemoryProxy = function(data){
8322     if (data.data) {
8323         data = data.data;
8324     }
8325     Roo.data.MemoryProxy.superclass.constructor.call(this);
8326     this.data = data;
8327 };
8328
8329 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
8330     /**
8331      * Load data from the requested source (in this case an in-memory
8332      * data object passed to the constructor), read the data object into
8333      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
8334      * process that block using the passed callback.
8335      * @param {Object} params This parameter is not used by the MemoryProxy class.
8336      * @param {Roo.data.DataReader} reader The Reader object which converts the data
8337      * object into a block of Roo.data.Records.
8338      * @param {Function} callback The function into which to pass the block of Roo.data.records.
8339      * The function must be passed <ul>
8340      * <li>The Record block object</li>
8341      * <li>The "arg" argument from the load function</li>
8342      * <li>A boolean success indicator</li>
8343      * </ul>
8344      * @param {Object} scope The scope in which to call the callback
8345      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
8346      */
8347     load : function(params, reader, callback, scope, arg){
8348         params = params || {};
8349         var result;
8350         try {
8351             result = reader.readRecords(this.data);
8352         }catch(e){
8353             this.fireEvent("loadexception", this, arg, null, e);
8354             callback.call(scope, null, arg, false);
8355             return;
8356         }
8357         callback.call(scope, result, arg, true);
8358     },
8359     
8360     // private
8361     update : function(params, records){
8362         
8363     }
8364 });/*
8365  * Based on:
8366  * Ext JS Library 1.1.1
8367  * Copyright(c) 2006-2007, Ext JS, LLC.
8368  *
8369  * Originally Released Under LGPL - original licence link has changed is not relivant.
8370  *
8371  * Fork - LGPL
8372  * <script type="text/javascript">
8373  */
8374 /**
8375  * @class Roo.data.HttpProxy
8376  * @extends Roo.data.DataProxy
8377  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
8378  * configured to reference a certain URL.<br><br>
8379  * <p>
8380  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
8381  * from which the running page was served.<br><br>
8382  * <p>
8383  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
8384  * <p>
8385  * Be aware that to enable the browser to parse an XML document, the server must set
8386  * the Content-Type header in the HTTP response to "text/xml".
8387  * @constructor
8388  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
8389  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
8390  * will be used to make the request.
8391  */
8392 Roo.data.HttpProxy = function(conn){
8393     Roo.data.HttpProxy.superclass.constructor.call(this);
8394     // is conn a conn config or a real conn?
8395     this.conn = conn;
8396     this.useAjax = !conn || !conn.events;
8397   
8398 };
8399
8400 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
8401     // thse are take from connection...
8402     
8403     /**
8404      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
8405      */
8406     /**
8407      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
8408      * extra parameters to each request made by this object. (defaults to undefined)
8409      */
8410     /**
8411      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
8412      *  to each request made by this object. (defaults to undefined)
8413      */
8414     /**
8415      * @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)
8416      */
8417     /**
8418      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
8419      */
8420      /**
8421      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
8422      * @type Boolean
8423      */
8424   
8425
8426     /**
8427      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
8428      * @type Boolean
8429      */
8430     /**
8431      * Return the {@link Roo.data.Connection} object being used by this Proxy.
8432      * @return {Connection} The Connection object. This object may be used to subscribe to events on
8433      * a finer-grained basis than the DataProxy events.
8434      */
8435     getConnection : function(){
8436         return this.useAjax ? Roo.Ajax : this.conn;
8437     },
8438
8439     /**
8440      * Load data from the configured {@link Roo.data.Connection}, read the data object into
8441      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
8442      * process that block using the passed callback.
8443      * @param {Object} params An object containing properties which are to be used as HTTP parameters
8444      * for the request to the remote server.
8445      * @param {Roo.data.DataReader} reader The Reader object which converts the data
8446      * object into a block of Roo.data.Records.
8447      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
8448      * The function must be passed <ul>
8449      * <li>The Record block object</li>
8450      * <li>The "arg" argument from the load function</li>
8451      * <li>A boolean success indicator</li>
8452      * </ul>
8453      * @param {Object} scope The scope in which to call the callback
8454      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
8455      */
8456     load : function(params, reader, callback, scope, arg){
8457         if(this.fireEvent("beforeload", this, params) !== false){
8458             var  o = {
8459                 params : params || {},
8460                 request: {
8461                     callback : callback,
8462                     scope : scope,
8463                     arg : arg
8464                 },
8465                 reader: reader,
8466                 callback : this.loadResponse,
8467                 scope: this
8468             };
8469             if(this.useAjax){
8470                 Roo.applyIf(o, this.conn);
8471                 if(this.activeRequest){
8472                     Roo.Ajax.abort(this.activeRequest);
8473                 }
8474                 this.activeRequest = Roo.Ajax.request(o);
8475             }else{
8476                 this.conn.request(o);
8477             }
8478         }else{
8479             callback.call(scope||this, null, arg, false);
8480         }
8481     },
8482
8483     // private
8484     loadResponse : function(o, success, response){
8485         delete this.activeRequest;
8486         if(!success){
8487             this.fireEvent("loadexception", this, o, response);
8488             o.request.callback.call(o.request.scope, null, o.request.arg, false);
8489             return;
8490         }
8491         var result;
8492         try {
8493             result = o.reader.read(response);
8494         }catch(e){
8495             this.fireEvent("loadexception", this, o, response, e);
8496             o.request.callback.call(o.request.scope, null, o.request.arg, false);
8497             return;
8498         }
8499         
8500         this.fireEvent("load", this, o, o.request.arg);
8501         o.request.callback.call(o.request.scope, result, o.request.arg, true);
8502     },
8503
8504     // private
8505     update : function(dataSet){
8506
8507     },
8508
8509     // private
8510     updateResponse : function(dataSet){
8511
8512     }
8513 });/*
8514  * Based on:
8515  * Ext JS Library 1.1.1
8516  * Copyright(c) 2006-2007, Ext JS, LLC.
8517  *
8518  * Originally Released Under LGPL - original licence link has changed is not relivant.
8519  *
8520  * Fork - LGPL
8521  * <script type="text/javascript">
8522  */
8523
8524 /**
8525  * @class Roo.data.ScriptTagProxy
8526  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
8527  * other than the originating domain of the running page.<br><br>
8528  * <p>
8529  * <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
8530  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
8531  * <p>
8532  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
8533  * source code that is used as the source inside a &lt;script> tag.<br><br>
8534  * <p>
8535  * In order for the browser to process the returned data, the server must wrap the data object
8536  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
8537  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
8538  * depending on whether the callback name was passed:
8539  * <p>
8540  * <pre><code>
8541 boolean scriptTag = false;
8542 String cb = request.getParameter("callback");
8543 if (cb != null) {
8544     scriptTag = true;
8545     response.setContentType("text/javascript");
8546 } else {
8547     response.setContentType("application/x-json");
8548 }
8549 Writer out = response.getWriter();
8550 if (scriptTag) {
8551     out.write(cb + "(");
8552 }
8553 out.print(dataBlock.toJsonString());
8554 if (scriptTag) {
8555     out.write(");");
8556 }
8557 </pre></code>
8558  *
8559  * @constructor
8560  * @param {Object} config A configuration object.
8561  */
8562 Roo.data.ScriptTagProxy = function(config){
8563     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
8564     Roo.apply(this, config);
8565     this.head = document.getElementsByTagName("head")[0];
8566 };
8567
8568 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
8569
8570 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
8571     /**
8572      * @cfg {String} url The URL from which to request the data object.
8573      */
8574     /**
8575      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
8576      */
8577     timeout : 30000,
8578     /**
8579      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
8580      * the server the name of the callback function set up by the load call to process the returned data object.
8581      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
8582      * javascript output which calls this named function passing the data object as its only parameter.
8583      */
8584     callbackParam : "callback",
8585     /**
8586      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
8587      * name to the request.
8588      */
8589     nocache : true,
8590
8591     /**
8592      * Load data from the configured URL, read the data object into
8593      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
8594      * process that block using the passed callback.
8595      * @param {Object} params An object containing properties which are to be used as HTTP parameters
8596      * for the request to the remote server.
8597      * @param {Roo.data.DataReader} reader The Reader object which converts the data
8598      * object into a block of Roo.data.Records.
8599      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
8600      * The function must be passed <ul>
8601      * <li>The Record block object</li>
8602      * <li>The "arg" argument from the load function</li>
8603      * <li>A boolean success indicator</li>
8604      * </ul>
8605      * @param {Object} scope The scope in which to call the callback
8606      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
8607      */
8608     load : function(params, reader, callback, scope, arg){
8609         if(this.fireEvent("beforeload", this, params) !== false){
8610
8611             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
8612
8613             var url = this.url;
8614             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
8615             if(this.nocache){
8616                 url += "&_dc=" + (new Date().getTime());
8617             }
8618             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
8619             var trans = {
8620                 id : transId,
8621                 cb : "stcCallback"+transId,
8622                 scriptId : "stcScript"+transId,
8623                 params : params,
8624                 arg : arg,
8625                 url : url,
8626                 callback : callback,
8627                 scope : scope,
8628                 reader : reader
8629             };
8630             var conn = this;
8631
8632             window[trans.cb] = function(o){
8633                 conn.handleResponse(o, trans);
8634             };
8635
8636             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
8637
8638             if(this.autoAbort !== false){
8639                 this.abort();
8640             }
8641
8642             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
8643
8644             var script = document.createElement("script");
8645             script.setAttribute("src", url);
8646             script.setAttribute("type", "text/javascript");
8647             script.setAttribute("id", trans.scriptId);
8648             this.head.appendChild(script);
8649
8650             this.trans = trans;
8651         }else{
8652             callback.call(scope||this, null, arg, false);
8653         }
8654     },
8655
8656     // private
8657     isLoading : function(){
8658         return this.trans ? true : false;
8659     },
8660
8661     /**
8662      * Abort the current server request.
8663      */
8664     abort : function(){
8665         if(this.isLoading()){
8666             this.destroyTrans(this.trans);
8667         }
8668     },
8669
8670     // private
8671     destroyTrans : function(trans, isLoaded){
8672         this.head.removeChild(document.getElementById(trans.scriptId));
8673         clearTimeout(trans.timeoutId);
8674         if(isLoaded){
8675             window[trans.cb] = undefined;
8676             try{
8677                 delete window[trans.cb];
8678             }catch(e){}
8679         }else{
8680             // if hasn't been loaded, wait for load to remove it to prevent script error
8681             window[trans.cb] = function(){
8682                 window[trans.cb] = undefined;
8683                 try{
8684                     delete window[trans.cb];
8685                 }catch(e){}
8686             };
8687         }
8688     },
8689
8690     // private
8691     handleResponse : function(o, trans){
8692         this.trans = false;
8693         this.destroyTrans(trans, true);
8694         var result;
8695         try {
8696             result = trans.reader.readRecords(o);
8697         }catch(e){
8698             this.fireEvent("loadexception", this, o, trans.arg, e);
8699             trans.callback.call(trans.scope||window, null, trans.arg, false);
8700             return;
8701         }
8702         this.fireEvent("load", this, o, trans.arg);
8703         trans.callback.call(trans.scope||window, result, trans.arg, true);
8704     },
8705
8706     // private
8707     handleFailure : function(trans){
8708         this.trans = false;
8709         this.destroyTrans(trans, false);
8710         this.fireEvent("loadexception", this, null, trans.arg);
8711         trans.callback.call(trans.scope||window, null, trans.arg, false);
8712     }
8713 });/*
8714  * Based on:
8715  * Ext JS Library 1.1.1
8716  * Copyright(c) 2006-2007, Ext JS, LLC.
8717  *
8718  * Originally Released Under LGPL - original licence link has changed is not relivant.
8719  *
8720  * Fork - LGPL
8721  * <script type="text/javascript">
8722  */
8723
8724 /**
8725  * @class Roo.data.JsonReader
8726  * @extends Roo.data.DataReader
8727  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
8728  * based on mappings in a provided Roo.data.Record constructor.
8729  * 
8730  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
8731  * in the reply previously. 
8732  * 
8733  * <p>
8734  * Example code:
8735  * <pre><code>
8736 var RecordDef = Roo.data.Record.create([
8737     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
8738     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
8739 ]);
8740 var myReader = new Roo.data.JsonReader({
8741     totalProperty: "results",    // The property which contains the total dataset size (optional)
8742     root: "rows",                // The property which contains an Array of row objects
8743     id: "id"                     // The property within each row object that provides an ID for the record (optional)
8744 }, RecordDef);
8745 </code></pre>
8746  * <p>
8747  * This would consume a JSON file like this:
8748  * <pre><code>
8749 { 'results': 2, 'rows': [
8750     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
8751     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
8752 }
8753 </code></pre>
8754  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
8755  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
8756  * paged from the remote server.
8757  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
8758  * @cfg {String} root name of the property which contains the Array of row objects.
8759  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
8760  * @constructor
8761  * Create a new JsonReader
8762  * @param {Object} meta Metadata configuration options
8763  * @param {Object} recordType Either an Array of field definition objects,
8764  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
8765  */
8766 Roo.data.JsonReader = function(meta, recordType){
8767     
8768     meta = meta || {};
8769     // set some defaults:
8770     Roo.applyIf(meta, {
8771         totalProperty: 'total',
8772         successProperty : 'success',
8773         root : 'data',
8774         id : 'id'
8775     });
8776     
8777     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
8778 };
8779 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
8780     
8781     /**
8782      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
8783      * Used by Store query builder to append _requestMeta to params.
8784      * 
8785      */
8786     metaFromRemote : false,
8787     /**
8788      * This method is only used by a DataProxy which has retrieved data from a remote server.
8789      * @param {Object} response The XHR object which contains the JSON data in its responseText.
8790      * @return {Object} data A data block which is used by an Roo.data.Store object as
8791      * a cache of Roo.data.Records.
8792      */
8793     read : function(response){
8794         var json = response.responseText;
8795        
8796         var o = /* eval:var:o */ eval("("+json+")");
8797         if(!o) {
8798             throw {message: "JsonReader.read: Json object not found"};
8799         }
8800         
8801         if(o.metaData){
8802             
8803             delete this.ef;
8804             this.metaFromRemote = true;
8805             this.meta = o.metaData;
8806             this.recordType = Roo.data.Record.create(o.metaData.fields);
8807             this.onMetaChange(this.meta, this.recordType, o);
8808         }
8809         return this.readRecords(o);
8810     },
8811
8812     // private function a store will implement
8813     onMetaChange : function(meta, recordType, o){
8814
8815     },
8816
8817     /**
8818          * @ignore
8819          */
8820     simpleAccess: function(obj, subsc) {
8821         return obj[subsc];
8822     },
8823
8824         /**
8825          * @ignore
8826          */
8827     getJsonAccessor: function(){
8828         var re = /[\[\.]/;
8829         return function(expr) {
8830             try {
8831                 return(re.test(expr))
8832                     ? new Function("obj", "return obj." + expr)
8833                     : function(obj){
8834                         return obj[expr];
8835                     };
8836             } catch(e){}
8837             return Roo.emptyFn;
8838         };
8839     }(),
8840
8841     /**
8842      * Create a data block containing Roo.data.Records from an XML document.
8843      * @param {Object} o An object which contains an Array of row objects in the property specified
8844      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
8845      * which contains the total size of the dataset.
8846      * @return {Object} data A data block which is used by an Roo.data.Store object as
8847      * a cache of Roo.data.Records.
8848      */
8849     readRecords : function(o){
8850         /**
8851          * After any data loads, the raw JSON data is available for further custom processing.
8852          * @type Object
8853          */
8854         this.o = o;
8855         var s = this.meta, Record = this.recordType,
8856             f = Record.prototype.fields, fi = f.items, fl = f.length;
8857
8858 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
8859         if (!this.ef) {
8860             if(s.totalProperty) {
8861                     this.getTotal = this.getJsonAccessor(s.totalProperty);
8862                 }
8863                 if(s.successProperty) {
8864                     this.getSuccess = this.getJsonAccessor(s.successProperty);
8865                 }
8866                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
8867                 if (s.id) {
8868                         var g = this.getJsonAccessor(s.id);
8869                         this.getId = function(rec) {
8870                                 var r = g(rec);
8871                                 return (r === undefined || r === "") ? null : r;
8872                         };
8873                 } else {
8874                         this.getId = function(){return null;};
8875                 }
8876             this.ef = [];
8877             for(var jj = 0; jj < fl; jj++){
8878                 f = fi[jj];
8879                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
8880                 this.ef[jj] = this.getJsonAccessor(map);
8881             }
8882         }
8883
8884         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
8885         if(s.totalProperty){
8886             var vt = parseInt(this.getTotal(o), 10);
8887             if(!isNaN(vt)){
8888                 totalRecords = vt;
8889             }
8890         }
8891         if(s.successProperty){
8892             var vs = this.getSuccess(o);
8893             if(vs === false || vs === 'false'){
8894                 success = false;
8895             }
8896         }
8897         var records = [];
8898             for(var i = 0; i < c; i++){
8899                     var n = root[i];
8900                 var values = {};
8901                 var id = this.getId(n);
8902                 for(var j = 0; j < fl; j++){
8903                     f = fi[j];
8904                 var v = this.ef[j](n);
8905                 if (!f.convert) {
8906                     Roo.log('missing convert for ' + f.name);
8907                     Roo.log(f);
8908                     continue;
8909                 }
8910                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
8911                 }
8912                 var record = new Record(values, id);
8913                 record.json = n;
8914                 records[i] = record;
8915             }
8916             return {
8917             raw : o,
8918                 success : success,
8919                 records : records,
8920                 totalRecords : totalRecords
8921             };
8922     }
8923 });/*
8924  * Based on:
8925  * Ext JS Library 1.1.1
8926  * Copyright(c) 2006-2007, Ext JS, LLC.
8927  *
8928  * Originally Released Under LGPL - original licence link has changed is not relivant.
8929  *
8930  * Fork - LGPL
8931  * <script type="text/javascript">
8932  */
8933
8934 /**
8935  * @class Roo.data.ArrayReader
8936  * @extends Roo.data.DataReader
8937  * Data reader class to create an Array of Roo.data.Record objects from an Array.
8938  * Each element of that Array represents a row of data fields. The
8939  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
8940  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
8941  * <p>
8942  * Example code:.
8943  * <pre><code>
8944 var RecordDef = Roo.data.Record.create([
8945     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
8946     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
8947 ]);
8948 var myReader = new Roo.data.ArrayReader({
8949     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
8950 }, RecordDef);
8951 </code></pre>
8952  * <p>
8953  * This would consume an Array like this:
8954  * <pre><code>
8955 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
8956   </code></pre>
8957  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
8958  * @constructor
8959  * Create a new JsonReader
8960  * @param {Object} meta Metadata configuration options.
8961  * @param {Object} recordType Either an Array of field definition objects
8962  * as specified to {@link Roo.data.Record#create},
8963  * or an {@link Roo.data.Record} object
8964  * created using {@link Roo.data.Record#create}.
8965  */
8966 Roo.data.ArrayReader = function(meta, recordType){
8967     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
8968 };
8969
8970 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
8971     /**
8972      * Create a data block containing Roo.data.Records from an XML document.
8973      * @param {Object} o An Array of row objects which represents the dataset.
8974      * @return {Object} data A data block which is used by an Roo.data.Store object as
8975      * a cache of Roo.data.Records.
8976      */
8977     readRecords : function(o){
8978         var sid = this.meta ? this.meta.id : null;
8979         var recordType = this.recordType, fields = recordType.prototype.fields;
8980         var records = [];
8981         var root = o;
8982             for(var i = 0; i < root.length; i++){
8983                     var n = root[i];
8984                 var values = {};
8985                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
8986                 for(var j = 0, jlen = fields.length; j < jlen; j++){
8987                 var f = fields.items[j];
8988                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
8989                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
8990                 v = f.convert(v);
8991                 values[f.name] = v;
8992             }
8993                 var record = new recordType(values, id);
8994                 record.json = n;
8995                 records[records.length] = record;
8996             }
8997             return {
8998                 records : records,
8999                 totalRecords : records.length
9000             };
9001     }
9002 });/*
9003  * - LGPL
9004  * * 
9005  */
9006
9007 /**
9008  * @class Roo.bootstrap.ComboBox
9009  * @extends Roo.bootstrap.TriggerField
9010  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
9011  * @cfg {Boolean} append (true|false) default false
9012  * @constructor
9013  * Create a new ComboBox.
9014  * @param {Object} config Configuration options
9015  */
9016 Roo.bootstrap.ComboBox = function(config){
9017     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
9018     this.addEvents({
9019         /**
9020          * @event expand
9021          * Fires when the dropdown list is expanded
9022              * @param {Roo.bootstrap.ComboBox} combo This combo box
9023              */
9024         'expand' : true,
9025         /**
9026          * @event collapse
9027          * Fires when the dropdown list is collapsed
9028              * @param {Roo.bootstrap.ComboBox} combo This combo box
9029              */
9030         'collapse' : true,
9031         /**
9032          * @event beforeselect
9033          * Fires before a list item is selected. Return false to cancel the selection.
9034              * @param {Roo.bootstrap.ComboBox} combo This combo box
9035              * @param {Roo.data.Record} record The data record returned from the underlying store
9036              * @param {Number} index The index of the selected item in the dropdown list
9037              */
9038         'beforeselect' : true,
9039         /**
9040          * @event select
9041          * Fires when a list item is selected
9042              * @param {Roo.bootstrap.ComboBox} combo This combo box
9043              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
9044              * @param {Number} index The index of the selected item in the dropdown list
9045              */
9046         'select' : true,
9047         /**
9048          * @event beforequery
9049          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
9050          * The event object passed has these properties:
9051              * @param {Roo.bootstrap.ComboBox} combo This combo box
9052              * @param {String} query The query
9053              * @param {Boolean} forceAll true to force "all" query
9054              * @param {Boolean} cancel true to cancel the query
9055              * @param {Object} e The query event object
9056              */
9057         'beforequery': true,
9058          /**
9059          * @event add
9060          * Fires when the 'add' icon is pressed (add a listener to enable add button)
9061              * @param {Roo.bootstrap.ComboBox} combo This combo box
9062              */
9063         'add' : true,
9064         /**
9065          * @event edit
9066          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
9067              * @param {Roo.bootstrap.ComboBox} combo This combo box
9068              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
9069              */
9070         'edit' : true,
9071         /**
9072          * @event remove
9073          * Fires when the remove value from the combobox array
9074              * @param {Roo.bootstrap.ComboBox} combo This combo box
9075              */
9076         'remove' : true
9077         
9078     });
9079     
9080     
9081     this.selectedIndex = -1;
9082     if(this.mode == 'local'){
9083         if(config.queryDelay === undefined){
9084             this.queryDelay = 10;
9085         }
9086         if(config.minChars === undefined){
9087             this.minChars = 0;
9088         }
9089     }
9090 };
9091
9092 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
9093      
9094     /**
9095      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
9096      * rendering into an Roo.Editor, defaults to false)
9097      */
9098     /**
9099      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
9100      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
9101      */
9102     /**
9103      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
9104      */
9105     /**
9106      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
9107      * the dropdown list (defaults to undefined, with no header element)
9108      */
9109
9110      /**
9111      * @cfg {String/Roo.Template} tpl The template to use to render the output
9112      */
9113      
9114      /**
9115      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
9116      */
9117     listWidth: undefined,
9118     /**
9119      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
9120      * mode = 'remote' or 'text' if mode = 'local')
9121      */
9122     displayField: undefined,
9123     /**
9124      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
9125      * mode = 'remote' or 'value' if mode = 'local'). 
9126      * Note: use of a valueField requires the user make a selection
9127      * in order for a value to be mapped.
9128      */
9129     valueField: undefined,
9130     
9131     
9132     /**
9133      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
9134      * field's data value (defaults to the underlying DOM element's name)
9135      */
9136     hiddenName: undefined,
9137     /**
9138      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
9139      */
9140     listClass: '',
9141     /**
9142      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
9143      */
9144     selectedClass: 'active',
9145     
9146     /**
9147      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
9148      */
9149     shadow:'sides',
9150     /**
9151      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
9152      * anchor positions (defaults to 'tl-bl')
9153      */
9154     listAlign: 'tl-bl?',
9155     /**
9156      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
9157      */
9158     maxHeight: 300,
9159     /**
9160      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
9161      * query specified by the allQuery config option (defaults to 'query')
9162      */
9163     triggerAction: 'query',
9164     /**
9165      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
9166      * (defaults to 4, does not apply if editable = false)
9167      */
9168     minChars : 4,
9169     /**
9170      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
9171      * delay (typeAheadDelay) if it matches a known value (defaults to false)
9172      */
9173     typeAhead: false,
9174     /**
9175      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
9176      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
9177      */
9178     queryDelay: 500,
9179     /**
9180      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
9181      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
9182      */
9183     pageSize: 0,
9184     /**
9185      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
9186      * when editable = true (defaults to false)
9187      */
9188     selectOnFocus:false,
9189     /**
9190      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
9191      */
9192     queryParam: 'query',
9193     /**
9194      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
9195      * when mode = 'remote' (defaults to 'Loading...')
9196      */
9197     loadingText: 'Loading...',
9198     /**
9199      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
9200      */
9201     resizable: false,
9202     /**
9203      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
9204      */
9205     handleHeight : 8,
9206     /**
9207      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
9208      * traditional select (defaults to true)
9209      */
9210     editable: true,
9211     /**
9212      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
9213      */
9214     allQuery: '',
9215     /**
9216      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
9217      */
9218     mode: 'remote',
9219     /**
9220      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
9221      * listWidth has a higher value)
9222      */
9223     minListWidth : 70,
9224     /**
9225      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
9226      * allow the user to set arbitrary text into the field (defaults to false)
9227      */
9228     forceSelection:false,
9229     /**
9230      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
9231      * if typeAhead = true (defaults to 250)
9232      */
9233     typeAheadDelay : 250,
9234     /**
9235      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
9236      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
9237      */
9238     valueNotFoundText : undefined,
9239     /**
9240      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
9241      */
9242     blockFocus : false,
9243     
9244     /**
9245      * @cfg {Boolean} disableClear Disable showing of clear button.
9246      */
9247     disableClear : false,
9248     /**
9249      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
9250      */
9251     alwaysQuery : false,
9252     
9253     /**
9254      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
9255      */
9256     multiple : false,
9257     
9258     //private
9259     addicon : false,
9260     editicon: false,
9261     
9262     page: 0,
9263     hasQuery: false,
9264     append: false,
9265     loadNext: false,
9266     item: [],
9267     
9268     // element that contains real text value.. (when hidden is used..)
9269      
9270     // private
9271     initEvents: function(){
9272         
9273         if (!this.store) {
9274             throw "can not find store for combo";
9275         }
9276         this.store = Roo.factory(this.store, Roo.data);
9277         
9278         
9279         
9280         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
9281         
9282         
9283         if(this.hiddenName){
9284             
9285             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
9286             
9287             this.hiddenField.dom.value =
9288                 this.hiddenValue !== undefined ? this.hiddenValue :
9289                 this.value !== undefined ? this.value : '';
9290
9291             // prevent input submission
9292             this.el.dom.removeAttribute('name');
9293             this.hiddenField.dom.setAttribute('name', this.hiddenName);
9294              
9295              
9296         }
9297         //if(Roo.isGecko){
9298         //    this.el.dom.setAttribute('autocomplete', 'off');
9299         //}
9300
9301         var cls = 'x-combo-list';
9302         this.list = this.el.select('ul.dropdown-menu',true).first();
9303
9304         //this.list = new Roo.Layer({
9305         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
9306         //});
9307         
9308         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
9309         this.list.setWidth(lw);
9310         
9311         this.list.on('mouseover', this.onViewOver, this);
9312         this.list.on('mousemove', this.onViewMove, this);
9313         
9314         this.list.on('scroll', this.onViewScroll, this);
9315         
9316         /*
9317         this.list.swallowEvent('mousewheel');
9318         this.assetHeight = 0;
9319
9320         if(this.title){
9321             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
9322             this.assetHeight += this.header.getHeight();
9323         }
9324
9325         this.innerList = this.list.createChild({cls:cls+'-inner'});
9326         this.innerList.on('mouseover', this.onViewOver, this);
9327         this.innerList.on('mousemove', this.onViewMove, this);
9328         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
9329         
9330         if(this.allowBlank && !this.pageSize && !this.disableClear){
9331             this.footer = this.list.createChild({cls:cls+'-ft'});
9332             this.pageTb = new Roo.Toolbar(this.footer);
9333            
9334         }
9335         if(this.pageSize){
9336             this.footer = this.list.createChild({cls:cls+'-ft'});
9337             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
9338                     {pageSize: this.pageSize});
9339             
9340         }
9341         
9342         if (this.pageTb && this.allowBlank && !this.disableClear) {
9343             var _this = this;
9344             this.pageTb.add(new Roo.Toolbar.Fill(), {
9345                 cls: 'x-btn-icon x-btn-clear',
9346                 text: '&#160;',
9347                 handler: function()
9348                 {
9349                     _this.collapse();
9350                     _this.clearValue();
9351                     _this.onSelect(false, -1);
9352                 }
9353             });
9354         }
9355         if (this.footer) {
9356             this.assetHeight += this.footer.getHeight();
9357         }
9358         */
9359             
9360         if(!this.tpl){
9361             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
9362         }
9363
9364         this.view = new Roo.View(this.el.select('ul.dropdown-menu',true).first(), this.tpl, {
9365             singleSelect:true, store: this.store, selectedClass: this.selectedClass
9366         });
9367         //this.view.wrapEl.setDisplayed(false);
9368         this.view.on('click', this.onViewClick, this);
9369         
9370         
9371         
9372         this.store.on('beforeload', this.onBeforeLoad, this);
9373         this.store.on('load', this.onLoad, this);
9374         this.store.on('loadexception', this.onLoadException, this);
9375         /*
9376         if(this.resizable){
9377             this.resizer = new Roo.Resizable(this.list,  {
9378                pinned:true, handles:'se'
9379             });
9380             this.resizer.on('resize', function(r, w, h){
9381                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
9382                 this.listWidth = w;
9383                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
9384                 this.restrictHeight();
9385             }, this);
9386             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
9387         }
9388         */
9389         if(!this.editable){
9390             this.editable = true;
9391             this.setEditable(false);
9392         }
9393         
9394         /*
9395         
9396         if (typeof(this.events.add.listeners) != 'undefined') {
9397             
9398             this.addicon = this.wrap.createChild(
9399                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
9400        
9401             this.addicon.on('click', function(e) {
9402                 this.fireEvent('add', this);
9403             }, this);
9404         }
9405         if (typeof(this.events.edit.listeners) != 'undefined') {
9406             
9407             this.editicon = this.wrap.createChild(
9408                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
9409             if (this.addicon) {
9410                 this.editicon.setStyle('margin-left', '40px');
9411             }
9412             this.editicon.on('click', function(e) {
9413                 
9414                 // we fire even  if inothing is selected..
9415                 this.fireEvent('edit', this, this.lastData );
9416                 
9417             }, this);
9418         }
9419         */
9420         
9421         this.keyNav = new Roo.KeyNav(this.inputEl(), {
9422             "up" : function(e){
9423                 this.inKeyMode = true;
9424                 this.selectPrev();
9425             },
9426
9427             "down" : function(e){
9428                 if(!this.isExpanded()){
9429                     this.onTriggerClick();
9430                 }else{
9431                     this.inKeyMode = true;
9432                     this.selectNext();
9433                 }
9434             },
9435
9436             "enter" : function(e){
9437                 this.onViewClick();
9438                 //return true;
9439             },
9440
9441             "esc" : function(e){
9442                 this.collapse();
9443             },
9444
9445             "tab" : function(e){
9446                 this.collapse();
9447                 
9448                 if(this.fireEvent("specialkey", this, e)){
9449                     this.onViewClick(false);
9450                 }
9451                 
9452                 return true;
9453             },
9454
9455             scope : this,
9456
9457             doRelay : function(foo, bar, hname){
9458                 if(hname == 'down' || this.scope.isExpanded()){
9459                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
9460                 }
9461                 return true;
9462             },
9463
9464             forceKeyDown: true
9465         });
9466         
9467         
9468         this.queryDelay = Math.max(this.queryDelay || 10,
9469                 this.mode == 'local' ? 10 : 250);
9470         
9471         
9472         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
9473         
9474         if(this.typeAhead){
9475             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
9476         }
9477         if(this.editable !== false){
9478             this.inputEl().on("keyup", this.onKeyUp, this);
9479         }
9480         if(this.forceSelection){
9481             this.on('blur', this.doForce, this);
9482         }
9483         
9484         if(this.multiple){
9485             this.choices = this.el.select('ul.select2-choices', true).first();
9486             this.searchField = this.el.select('ul li.select2-search-field', true).first();
9487         }
9488     },
9489
9490     onDestroy : function(){
9491         if(this.view){
9492             this.view.setStore(null);
9493             this.view.el.removeAllListeners();
9494             this.view.el.remove();
9495             this.view.purgeListeners();
9496         }
9497         if(this.list){
9498             this.list.dom.innerHTML  = '';
9499         }
9500         if(this.store){
9501             this.store.un('beforeload', this.onBeforeLoad, this);
9502             this.store.un('load', this.onLoad, this);
9503             this.store.un('loadexception', this.onLoadException, this);
9504         }
9505         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
9506     },
9507
9508     // private
9509     fireKey : function(e){
9510         if(e.isNavKeyPress() && !this.list.isVisible()){
9511             this.fireEvent("specialkey", this, e);
9512         }
9513     },
9514
9515     // private
9516     onResize: function(w, h){
9517 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
9518 //        
9519 //        if(typeof w != 'number'){
9520 //            // we do not handle it!?!?
9521 //            return;
9522 //        }
9523 //        var tw = this.trigger.getWidth();
9524 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
9525 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
9526 //        var x = w - tw;
9527 //        this.inputEl().setWidth( this.adjustWidth('input', x));
9528 //            
9529 //        //this.trigger.setStyle('left', x+'px');
9530 //        
9531 //        if(this.list && this.listWidth === undefined){
9532 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
9533 //            this.list.setWidth(lw);
9534 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
9535 //        }
9536         
9537     
9538         
9539     },
9540
9541     /**
9542      * Allow or prevent the user from directly editing the field text.  If false is passed,
9543      * the user will only be able to select from the items defined in the dropdown list.  This method
9544      * is the runtime equivalent of setting the 'editable' config option at config time.
9545      * @param {Boolean} value True to allow the user to directly edit the field text
9546      */
9547     setEditable : function(value){
9548         if(value == this.editable){
9549             return;
9550         }
9551         this.editable = value;
9552         if(!value){
9553             this.inputEl().dom.setAttribute('readOnly', true);
9554             this.inputEl().on('mousedown', this.onTriggerClick,  this);
9555             this.inputEl().addClass('x-combo-noedit');
9556         }else{
9557             this.inputEl().dom.setAttribute('readOnly', false);
9558             this.inputEl().un('mousedown', this.onTriggerClick,  this);
9559             this.inputEl().removeClass('x-combo-noedit');
9560         }
9561     },
9562
9563     // private
9564     
9565     onBeforeLoad : function(combo,opts){
9566         if(!this.hasFocus){
9567             return;
9568         }
9569          if (!opts.add) {
9570             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
9571          }
9572         this.restrictHeight();
9573         this.selectedIndex = -1;
9574     },
9575
9576     // private
9577     onLoad : function(){
9578         
9579         this.hasQuery = false;
9580         
9581         if(!this.hasFocus){
9582             return;
9583         }
9584         
9585         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
9586             this.loading.hide();
9587         }
9588         
9589         if(this.store.getCount() > 0){
9590             this.expand();
9591             this.restrictHeight();
9592             if(this.lastQuery == this.allQuery){
9593                 if(this.editable){
9594                     this.inputEl().dom.select();
9595                 }
9596                 if(!this.selectByValue(this.value, true)){
9597                     this.select(0, true);
9598                 }
9599             }else{
9600                 this.selectNext();
9601                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
9602                     this.taTask.delay(this.typeAheadDelay);
9603                 }
9604             }
9605         }else{
9606             this.onEmptyResults();
9607         }
9608         
9609         //this.el.focus();
9610     },
9611     // private
9612     onLoadException : function()
9613     {
9614         this.hasQuery = false;
9615         
9616         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
9617             this.loading.hide();
9618         }
9619         
9620         this.collapse();
9621         Roo.log(this.store.reader.jsonData);
9622         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
9623             // fixme
9624             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
9625         }
9626         
9627         
9628     },
9629     // private
9630     onTypeAhead : function(){
9631         if(this.store.getCount() > 0){
9632             var r = this.store.getAt(0);
9633             var newValue = r.data[this.displayField];
9634             var len = newValue.length;
9635             var selStart = this.getRawValue().length;
9636             
9637             if(selStart != len){
9638                 this.setRawValue(newValue);
9639                 this.selectText(selStart, newValue.length);
9640             }
9641         }
9642     },
9643
9644     // private
9645     onSelect : function(record, index){
9646         
9647         if(this.fireEvent('beforeselect', this, record, index) !== false){
9648         
9649             this.setFromData(index > -1 ? record.data : false);
9650             
9651             this.collapse();
9652             this.fireEvent('select', this, record, index);
9653         }
9654     },
9655
9656     /**
9657      * Returns the currently selected field value or empty string if no value is set.
9658      * @return {String} value The selected value
9659      */
9660     getValue : function(){
9661         
9662         if(this.multiple){
9663             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
9664         }
9665         
9666         if(this.valueField){
9667             return typeof this.value != 'undefined' ? this.value : '';
9668         }else{
9669             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
9670         }
9671     },
9672
9673     /**
9674      * Clears any text/value currently set in the field
9675      */
9676     clearValue : function(){
9677         if(this.hiddenField){
9678             this.hiddenField.dom.value = '';
9679         }
9680         this.value = '';
9681         this.setRawValue('');
9682         this.lastSelectionText = '';
9683         
9684     },
9685
9686     /**
9687      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
9688      * will be displayed in the field.  If the value does not match the data value of an existing item,
9689      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
9690      * Otherwise the field will be blank (although the value will still be set).
9691      * @param {String} value The value to match
9692      */
9693     setValue : function(v){
9694         if(this.multiple){
9695             this.syncValue();
9696             return;
9697         }
9698         
9699         var text = v;
9700         if(this.valueField){
9701             var r = this.findRecord(this.valueField, v);
9702             if(r){
9703                 text = r.data[this.displayField];
9704             }else if(this.valueNotFoundText !== undefined){
9705                 text = this.valueNotFoundText;
9706             }
9707         }
9708         this.lastSelectionText = text;
9709         if(this.hiddenField){
9710             this.hiddenField.dom.value = v;
9711         }
9712         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
9713         this.value = v;
9714     },
9715     /**
9716      * @property {Object} the last set data for the element
9717      */
9718     
9719     lastData : false,
9720     /**
9721      * Sets the value of the field based on a object which is related to the record format for the store.
9722      * @param {Object} value the value to set as. or false on reset?
9723      */
9724     setFromData : function(o){
9725         
9726         if(this.multiple){
9727             this.addItem(o);
9728             return;
9729         }
9730             
9731         var dv = ''; // display value
9732         var vv = ''; // value value..
9733         this.lastData = o;
9734         if (this.displayField) {
9735             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
9736         } else {
9737             // this is an error condition!!!
9738             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
9739         }
9740         
9741         if(this.valueField){
9742             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
9743         }
9744         
9745         if(this.hiddenField){
9746             this.hiddenField.dom.value = vv;
9747             
9748             this.lastSelectionText = dv;
9749             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
9750             this.value = vv;
9751             return;
9752         }
9753         // no hidden field.. - we store the value in 'value', but still display
9754         // display field!!!!
9755         this.lastSelectionText = dv;
9756         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
9757         this.value = vv;
9758         
9759         
9760     },
9761     // private
9762     reset : function(){
9763         // overridden so that last data is reset..
9764         this.setValue(this.originalValue);
9765         this.clearInvalid();
9766         this.lastData = false;
9767         if (this.view) {
9768             this.view.clearSelections();
9769         }
9770     },
9771     // private
9772     findRecord : function(prop, value){
9773         var record;
9774         if(this.store.getCount() > 0){
9775             this.store.each(function(r){
9776                 if(r.data[prop] == value){
9777                     record = r;
9778                     return false;
9779                 }
9780                 return true;
9781             });
9782         }
9783         return record;
9784     },
9785     
9786     getName: function()
9787     {
9788         // returns hidden if it's set..
9789         if (!this.rendered) {return ''};
9790         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
9791         
9792     },
9793     // private
9794     onViewMove : function(e, t){
9795         this.inKeyMode = false;
9796     },
9797
9798     // private
9799     onViewOver : function(e, t){
9800         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
9801             return;
9802         }
9803         var item = this.view.findItemFromChild(t);
9804         if(item){
9805             var index = this.view.indexOf(item);
9806             this.select(index, false);
9807         }
9808     },
9809
9810     // private
9811     onViewClick : function(doFocus)
9812     {
9813         var index = this.view.getSelectedIndexes()[0];
9814         var r = this.store.getAt(index);
9815         if(r){
9816             this.onSelect(r, index);
9817         }
9818         if(doFocus !== false && !this.blockFocus){
9819             this.inputEl().focus();
9820         }
9821     },
9822
9823     // private
9824     restrictHeight : function(){
9825         //this.innerList.dom.style.height = '';
9826         //var inner = this.innerList.dom;
9827         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
9828         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
9829         //this.list.beginUpdate();
9830         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
9831         this.list.alignTo(this.inputEl(), this.listAlign);
9832         //this.list.endUpdate();
9833     },
9834
9835     // private
9836     onEmptyResults : function(){
9837         this.collapse();
9838     },
9839
9840     /**
9841      * Returns true if the dropdown list is expanded, else false.
9842      */
9843     isExpanded : function(){
9844         return this.list.isVisible();
9845     },
9846
9847     /**
9848      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
9849      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
9850      * @param {String} value The data value of the item to select
9851      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
9852      * selected item if it is not currently in view (defaults to true)
9853      * @return {Boolean} True if the value matched an item in the list, else false
9854      */
9855     selectByValue : function(v, scrollIntoView){
9856         if(v !== undefined && v !== null){
9857             var r = this.findRecord(this.valueField || this.displayField, v);
9858             if(r){
9859                 this.select(this.store.indexOf(r), scrollIntoView);
9860                 return true;
9861             }
9862         }
9863         return false;
9864     },
9865
9866     /**
9867      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
9868      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
9869      * @param {Number} index The zero-based index of the list item to select
9870      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
9871      * selected item if it is not currently in view (defaults to true)
9872      */
9873     select : function(index, scrollIntoView){
9874         this.selectedIndex = index;
9875         this.view.select(index);
9876         if(scrollIntoView !== false){
9877             var el = this.view.getNode(index);
9878             if(el){
9879                 //this.innerList.scrollChildIntoView(el, false);
9880                 
9881             }
9882         }
9883     },
9884
9885     // private
9886     selectNext : function(){
9887         var ct = this.store.getCount();
9888         if(ct > 0){
9889             if(this.selectedIndex == -1){
9890                 this.select(0);
9891             }else if(this.selectedIndex < ct-1){
9892                 this.select(this.selectedIndex+1);
9893             }
9894         }
9895     },
9896
9897     // private
9898     selectPrev : function(){
9899         var ct = this.store.getCount();
9900         if(ct > 0){
9901             if(this.selectedIndex == -1){
9902                 this.select(0);
9903             }else if(this.selectedIndex != 0){
9904                 this.select(this.selectedIndex-1);
9905             }
9906         }
9907     },
9908
9909     // private
9910     onKeyUp : function(e){
9911         if(this.editable !== false && !e.isSpecialKey()){
9912             this.lastKey = e.getKey();
9913             this.dqTask.delay(this.queryDelay);
9914         }
9915     },
9916
9917     // private
9918     validateBlur : function(){
9919         return !this.list || !this.list.isVisible();   
9920     },
9921
9922     // private
9923     initQuery : function(){
9924         this.doQuery(this.getRawValue());
9925     },
9926
9927     // private
9928     doForce : function(){
9929         if(this.el.dom.value.length > 0){
9930             this.el.dom.value =
9931                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
9932              
9933         }
9934     },
9935
9936     /**
9937      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
9938      * query allowing the query action to be canceled if needed.
9939      * @param {String} query The SQL query to execute
9940      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
9941      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
9942      * saved in the current store (defaults to false)
9943      */
9944     doQuery : function(q, forceAll){
9945         
9946         if(q === undefined || q === null){
9947             q = '';
9948         }
9949         var qe = {
9950             query: q,
9951             forceAll: forceAll,
9952             combo: this,
9953             cancel:false
9954         };
9955         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
9956             return false;
9957         }
9958         q = qe.query;
9959         
9960         forceAll = qe.forceAll;
9961         if(forceAll === true || (q.length >= this.minChars)){
9962             
9963             this.hasQuery = true;
9964             
9965             if(this.lastQuery != q || this.alwaysQuery){
9966                 this.lastQuery = q;
9967                 if(this.mode == 'local'){
9968                     this.selectedIndex = -1;
9969                     if(forceAll){
9970                         this.store.clearFilter();
9971                     }else{
9972                         this.store.filter(this.displayField, q);
9973                     }
9974                     this.onLoad();
9975                 }else{
9976                     this.store.baseParams[this.queryParam] = q;
9977                     
9978                     var options = {params : this.getParams(q)};
9979                     
9980                     if(this.loadNext){
9981                         options.add = true;
9982                         options.params.start = this.page * this.pageSize;
9983                     }
9984                     
9985                     this.store.load(options);
9986                     this.expand();
9987                 }
9988             }else{
9989                 this.selectedIndex = -1;
9990                 this.onLoad();   
9991             }
9992         }
9993         
9994         this.loadNext = false;
9995     },
9996
9997     // private
9998     getParams : function(q){
9999         var p = {};
10000         //p[this.queryParam] = q;
10001         
10002         if(this.pageSize){
10003             p.start = 0;
10004             p.limit = this.pageSize;
10005         }
10006         return p;
10007     },
10008
10009     /**
10010      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
10011      */
10012     collapse : function(){
10013         if(!this.isExpanded()){
10014             return;
10015         }
10016         
10017         this.list.hide();
10018         Roo.get(document).un('mousedown', this.collapseIf, this);
10019         Roo.get(document).un('mousewheel', this.collapseIf, this);
10020         if (!this.editable) {
10021             Roo.get(document).un('keydown', this.listKeyPress, this);
10022         }
10023         this.fireEvent('collapse', this);
10024     },
10025
10026     // private
10027     collapseIf : function(e){
10028         var in_combo  = e.within(this.el);
10029         var in_list =  e.within(this.list);
10030         
10031         if (in_combo || in_list) {
10032             //e.stopPropagation();
10033             return;
10034         }
10035
10036         this.collapse();
10037         
10038     },
10039
10040     /**
10041      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
10042      */
10043     expand : function(){
10044        
10045         if(this.isExpanded() || !this.hasFocus){
10046             return;
10047         }
10048          Roo.log('expand');
10049         this.list.alignTo(this.inputEl(), this.listAlign);
10050         this.list.show();
10051         Roo.get(document).on('mousedown', this.collapseIf, this);
10052         Roo.get(document).on('mousewheel', this.collapseIf, this);
10053         if (!this.editable) {
10054             Roo.get(document).on('keydown', this.listKeyPress, this);
10055         }
10056         
10057         this.fireEvent('expand', this);
10058     },
10059
10060     // private
10061     // Implements the default empty TriggerField.onTriggerClick function
10062     onTriggerClick : function()
10063     {
10064         Roo.log('trigger click');
10065         
10066         if(this.disabled){
10067             return;
10068         }
10069         
10070         this.page = 0;
10071         this.loadNext = false;
10072         
10073         if(this.isExpanded()){
10074             this.collapse();
10075             if (!this.blockFocus) {
10076                 this.inputEl().focus();
10077             }
10078             
10079         }else {
10080             this.hasFocus = true;
10081             if(this.triggerAction == 'all') {
10082                 this.doQuery(this.allQuery, true);
10083             } else {
10084                 this.doQuery(this.getRawValue());
10085             }
10086             if (!this.blockFocus) {
10087                 this.inputEl().focus();
10088             }
10089         }
10090     },
10091     listKeyPress : function(e)
10092     {
10093         //Roo.log('listkeypress');
10094         // scroll to first matching element based on key pres..
10095         if (e.isSpecialKey()) {
10096             return false;
10097         }
10098         var k = String.fromCharCode(e.getKey()).toUpperCase();
10099         //Roo.log(k);
10100         var match  = false;
10101         var csel = this.view.getSelectedNodes();
10102         var cselitem = false;
10103         if (csel.length) {
10104             var ix = this.view.indexOf(csel[0]);
10105             cselitem  = this.store.getAt(ix);
10106             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
10107                 cselitem = false;
10108             }
10109             
10110         }
10111         
10112         this.store.each(function(v) { 
10113             if (cselitem) {
10114                 // start at existing selection.
10115                 if (cselitem.id == v.id) {
10116                     cselitem = false;
10117                 }
10118                 return true;
10119             }
10120                 
10121             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
10122                 match = this.store.indexOf(v);
10123                 return false;
10124             }
10125             return true;
10126         }, this);
10127         
10128         if (match === false) {
10129             return true; // no more action?
10130         }
10131         // scroll to?
10132         this.view.select(match);
10133         var sn = Roo.get(this.view.getSelectedNodes()[0])
10134         //sn.scrollIntoView(sn.dom.parentNode, false);
10135     },
10136     
10137     onViewScroll : function(e, t){
10138         
10139         if(this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
10140             return;
10141         }
10142         
10143         this.hasQuery = true;
10144         
10145         this.loading = this.list.select('.loading', true).first();
10146         
10147         if(this.loading === null){
10148             this.list.createChild({
10149                 tag: 'div',
10150                 cls: 'loading select2-more-results select2-active',
10151                 html: 'Loading more results...'
10152             })
10153             
10154             this.loading = this.list.select('.loading', true).first();
10155             
10156             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
10157             
10158             this.loading.hide();
10159         }
10160         
10161         this.loading.show();
10162         
10163         var _combo = this;
10164         
10165         this.page++;
10166         this.loadNext = true;
10167         
10168         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
10169         
10170         return;
10171     },
10172     
10173     addItem : function(o)
10174     {   
10175         var dv = ''; // display value
10176         
10177         if (this.displayField) {
10178             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
10179         } else {
10180             // this is an error condition!!!
10181             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
10182         }
10183         
10184         if(!dv.length){
10185             return;
10186         }
10187         
10188         var choice = this.choices.createChild({
10189             tag: 'li',
10190             cls: 'select2-search-choice',
10191             cn: [
10192                 {
10193                     tag: 'div',
10194                     html: dv
10195                 },
10196                 {
10197                     tag: 'a',
10198                     href: '#',
10199                     cls: 'select2-search-choice-close',
10200                     tabindex: '-1'
10201                 }
10202             ]
10203             
10204         }, this.searchField);
10205         
10206         var close = choice.select('a.select2-search-choice-close', true).first()
10207         
10208         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
10209         
10210         this.item.push(o);
10211         this.lastData = o;
10212         
10213         this.syncValue();
10214         
10215         this.inputEl().dom.value = '';
10216         
10217     },
10218     
10219     onRemoveItem : function(e, _self, o)
10220     {
10221         Roo.log('remove item');
10222         var index = this.item.indexOf(o.data) * 1;
10223         
10224         if( index < 0){
10225             Roo.log('not this item?!');
10226             return;
10227         }
10228         
10229         this.item.splice(index, 1);
10230         o.item.remove();
10231         
10232         this.syncValue();
10233         
10234         this.fireEvent('remove', this, e);
10235         
10236     },
10237     
10238     syncValue : function()
10239     {
10240         if(!this.item.length){
10241             this.clearValue();
10242             return;
10243         }
10244             
10245         var value = [];
10246         var _this = this;
10247         Roo.each(this.item, function(i){
10248             if(_this.valueField){
10249                 value.push(i[_this.valueField]);
10250                 return;
10251             }
10252
10253             value.push(i);
10254         });
10255
10256         this.value = value.join(',');
10257
10258         if(this.hiddenField){
10259             this.hiddenField.dom.value = this.value;
10260         }
10261     },
10262     
10263     clearItem : function()
10264     {
10265         if(!this.multiple){
10266             return;
10267         }
10268         
10269         this.item = [];
10270         
10271         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
10272            c.remove();
10273         });
10274         
10275         this.syncValue();
10276     }
10277     
10278     
10279
10280     /** 
10281     * @cfg {Boolean} grow 
10282     * @hide 
10283     */
10284     /** 
10285     * @cfg {Number} growMin 
10286     * @hide 
10287     */
10288     /** 
10289     * @cfg {Number} growMax 
10290     * @hide 
10291     */
10292     /**
10293      * @hide
10294      * @method autoSize
10295      */
10296 });
10297 /*
10298  * Based on:
10299  * Ext JS Library 1.1.1
10300  * Copyright(c) 2006-2007, Ext JS, LLC.
10301  *
10302  * Originally Released Under LGPL - original licence link has changed is not relivant.
10303  *
10304  * Fork - LGPL
10305  * <script type="text/javascript">
10306  */
10307
10308 /**
10309  * @class Roo.View
10310  * @extends Roo.util.Observable
10311  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
10312  * This class also supports single and multi selection modes. <br>
10313  * Create a data model bound view:
10314  <pre><code>
10315  var store = new Roo.data.Store(...);
10316
10317  var view = new Roo.View({
10318     el : "my-element",
10319     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
10320  
10321     singleSelect: true,
10322     selectedClass: "ydataview-selected",
10323     store: store
10324  });
10325
10326  // listen for node click?
10327  view.on("click", function(vw, index, node, e){
10328  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
10329  });
10330
10331  // load XML data
10332  dataModel.load("foobar.xml");
10333  </code></pre>
10334  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
10335  * <br><br>
10336  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
10337  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
10338  * 
10339  * Note: old style constructor is still suported (container, template, config)
10340  * 
10341  * @constructor
10342  * Create a new View
10343  * @param {Object} config The config object
10344  * 
10345  */
10346 Roo.View = function(config, depreciated_tpl, depreciated_config){
10347     
10348     if (typeof(depreciated_tpl) == 'undefined') {
10349         // new way.. - universal constructor.
10350         Roo.apply(this, config);
10351         this.el  = Roo.get(this.el);
10352     } else {
10353         // old format..
10354         this.el  = Roo.get(config);
10355         this.tpl = depreciated_tpl;
10356         Roo.apply(this, depreciated_config);
10357     }
10358     this.wrapEl  = this.el.wrap().wrap();
10359     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
10360     
10361     
10362     if(typeof(this.tpl) == "string"){
10363         this.tpl = new Roo.Template(this.tpl);
10364     } else {
10365         // support xtype ctors..
10366         this.tpl = new Roo.factory(this.tpl, Roo);
10367     }
10368     
10369     
10370     this.tpl.compile();
10371    
10372   
10373     
10374      
10375     /** @private */
10376     this.addEvents({
10377         /**
10378          * @event beforeclick
10379          * Fires before a click is processed. Returns false to cancel the default action.
10380          * @param {Roo.View} this
10381          * @param {Number} index The index of the target node
10382          * @param {HTMLElement} node The target node
10383          * @param {Roo.EventObject} e The raw event object
10384          */
10385             "beforeclick" : true,
10386         /**
10387          * @event click
10388          * Fires when a template node is clicked.
10389          * @param {Roo.View} this
10390          * @param {Number} index The index of the target node
10391          * @param {HTMLElement} node The target node
10392          * @param {Roo.EventObject} e The raw event object
10393          */
10394             "click" : true,
10395         /**
10396          * @event dblclick
10397          * Fires when a template node is double clicked.
10398          * @param {Roo.View} this
10399          * @param {Number} index The index of the target node
10400          * @param {HTMLElement} node The target node
10401          * @param {Roo.EventObject} e The raw event object
10402          */
10403             "dblclick" : true,
10404         /**
10405          * @event contextmenu
10406          * Fires when a template node is right clicked.
10407          * @param {Roo.View} this
10408          * @param {Number} index The index of the target node
10409          * @param {HTMLElement} node The target node
10410          * @param {Roo.EventObject} e The raw event object
10411          */
10412             "contextmenu" : true,
10413         /**
10414          * @event selectionchange
10415          * Fires when the selected nodes change.
10416          * @param {Roo.View} this
10417          * @param {Array} selections Array of the selected nodes
10418          */
10419             "selectionchange" : true,
10420     
10421         /**
10422          * @event beforeselect
10423          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
10424          * @param {Roo.View} this
10425          * @param {HTMLElement} node The node to be selected
10426          * @param {Array} selections Array of currently selected nodes
10427          */
10428             "beforeselect" : true,
10429         /**
10430          * @event preparedata
10431          * Fires on every row to render, to allow you to change the data.
10432          * @param {Roo.View} this
10433          * @param {Object} data to be rendered (change this)
10434          */
10435           "preparedata" : true
10436           
10437           
10438         });
10439
10440
10441
10442     this.el.on({
10443         "click": this.onClick,
10444         "dblclick": this.onDblClick,
10445         "contextmenu": this.onContextMenu,
10446         scope:this
10447     });
10448
10449     this.selections = [];
10450     this.nodes = [];
10451     this.cmp = new Roo.CompositeElementLite([]);
10452     if(this.store){
10453         this.store = Roo.factory(this.store, Roo.data);
10454         this.setStore(this.store, true);
10455     }
10456     
10457     if ( this.footer && this.footer.xtype) {
10458            
10459          var fctr = this.wrapEl.appendChild(document.createElement("div"));
10460         
10461         this.footer.dataSource = this.store
10462         this.footer.container = fctr;
10463         this.footer = Roo.factory(this.footer, Roo);
10464         fctr.insertFirst(this.el);
10465         
10466         // this is a bit insane - as the paging toolbar seems to detach the el..
10467 //        dom.parentNode.parentNode.parentNode
10468          // they get detached?
10469     }
10470     
10471     
10472     Roo.View.superclass.constructor.call(this);
10473     
10474     
10475 };
10476
10477 Roo.extend(Roo.View, Roo.util.Observable, {
10478     
10479      /**
10480      * @cfg {Roo.data.Store} store Data store to load data from.
10481      */
10482     store : false,
10483     
10484     /**
10485      * @cfg {String|Roo.Element} el The container element.
10486      */
10487     el : '',
10488     
10489     /**
10490      * @cfg {String|Roo.Template} tpl The template used by this View 
10491      */
10492     tpl : false,
10493     /**
10494      * @cfg {String} dataName the named area of the template to use as the data area
10495      *                          Works with domtemplates roo-name="name"
10496      */
10497     dataName: false,
10498     /**
10499      * @cfg {String} selectedClass The css class to add to selected nodes
10500      */
10501     selectedClass : "x-view-selected",
10502      /**
10503      * @cfg {String} emptyText The empty text to show when nothing is loaded.
10504      */
10505     emptyText : "",
10506     
10507     /**
10508      * @cfg {String} text to display on mask (default Loading)
10509      */
10510     mask : false,
10511     /**
10512      * @cfg {Boolean} multiSelect Allow multiple selection
10513      */
10514     multiSelect : false,
10515     /**
10516      * @cfg {Boolean} singleSelect Allow single selection
10517      */
10518     singleSelect:  false,
10519     
10520     /**
10521      * @cfg {Boolean} toggleSelect - selecting 
10522      */
10523     toggleSelect : false,
10524     
10525     /**
10526      * Returns the element this view is bound to.
10527      * @return {Roo.Element}
10528      */
10529     getEl : function(){
10530         return this.wrapEl;
10531     },
10532     
10533     
10534
10535     /**
10536      * Refreshes the view. - called by datachanged on the store. - do not call directly.
10537      */
10538     refresh : function(){
10539         Roo.log('refresh');
10540         var t = this.tpl;
10541         
10542         // if we are using something like 'domtemplate', then
10543         // the what gets used is:
10544         // t.applySubtemplate(NAME, data, wrapping data..)
10545         // the outer template then get' applied with
10546         //     the store 'extra data'
10547         // and the body get's added to the
10548         //      roo-name="data" node?
10549         //      <span class='roo-tpl-{name}'></span> ?????
10550         
10551         
10552         
10553         this.clearSelections();
10554         this.el.update("");
10555         var html = [];
10556         var records = this.store.getRange();
10557         if(records.length < 1) {
10558             
10559             // is this valid??  = should it render a template??
10560             
10561             this.el.update(this.emptyText);
10562             return;
10563         }
10564         var el = this.el;
10565         if (this.dataName) {
10566             this.el.update(t.apply(this.store.meta)); //????
10567             el = this.el.child('.roo-tpl-' + this.dataName);
10568         }
10569         
10570         for(var i = 0, len = records.length; i < len; i++){
10571             var data = this.prepareData(records[i].data, i, records[i]);
10572             this.fireEvent("preparedata", this, data, i, records[i]);
10573             html[html.length] = Roo.util.Format.trim(
10574                 this.dataName ?
10575                     t.applySubtemplate(this.dataName, data, this.store.meta) :
10576                     t.apply(data)
10577             );
10578         }
10579         
10580         
10581         
10582         el.update(html.join(""));
10583         this.nodes = el.dom.childNodes;
10584         this.updateIndexes(0);
10585     },
10586     
10587
10588     /**
10589      * Function to override to reformat the data that is sent to
10590      * the template for each node.
10591      * DEPRICATED - use the preparedata event handler.
10592      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
10593      * a JSON object for an UpdateManager bound view).
10594      */
10595     prepareData : function(data, index, record)
10596     {
10597         this.fireEvent("preparedata", this, data, index, record);
10598         return data;
10599     },
10600
10601     onUpdate : function(ds, record){
10602          Roo.log('on update');   
10603         this.clearSelections();
10604         var index = this.store.indexOf(record);
10605         var n = this.nodes[index];
10606         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
10607         n.parentNode.removeChild(n);
10608         this.updateIndexes(index, index);
10609     },
10610
10611     
10612     
10613 // --------- FIXME     
10614     onAdd : function(ds, records, index)
10615     {
10616         Roo.log(['on Add', ds, records, index] );        
10617         this.clearSelections();
10618         if(this.nodes.length == 0){
10619             this.refresh();
10620             return;
10621         }
10622         var n = this.nodes[index];
10623         for(var i = 0, len = records.length; i < len; i++){
10624             var d = this.prepareData(records[i].data, i, records[i]);
10625             if(n){
10626                 this.tpl.insertBefore(n, d);
10627             }else{
10628                 
10629                 this.tpl.append(this.el, d);
10630             }
10631         }
10632         this.updateIndexes(index);
10633     },
10634
10635     onRemove : function(ds, record, index){
10636         Roo.log('onRemove');
10637         this.clearSelections();
10638         var el = this.dataName  ?
10639             this.el.child('.roo-tpl-' + this.dataName) :
10640             this.el; 
10641         
10642         el.dom.removeChild(this.nodes[index]);
10643         this.updateIndexes(index);
10644     },
10645
10646     /**
10647      * Refresh an individual node.
10648      * @param {Number} index
10649      */
10650     refreshNode : function(index){
10651         this.onUpdate(this.store, this.store.getAt(index));
10652     },
10653
10654     updateIndexes : function(startIndex, endIndex){
10655         var ns = this.nodes;
10656         startIndex = startIndex || 0;
10657         endIndex = endIndex || ns.length - 1;
10658         for(var i = startIndex; i <= endIndex; i++){
10659             ns[i].nodeIndex = i;
10660         }
10661     },
10662
10663     /**
10664      * Changes the data store this view uses and refresh the view.
10665      * @param {Store} store
10666      */
10667     setStore : function(store, initial){
10668         if(!initial && this.store){
10669             this.store.un("datachanged", this.refresh);
10670             this.store.un("add", this.onAdd);
10671             this.store.un("remove", this.onRemove);
10672             this.store.un("update", this.onUpdate);
10673             this.store.un("clear", this.refresh);
10674             this.store.un("beforeload", this.onBeforeLoad);
10675             this.store.un("load", this.onLoad);
10676             this.store.un("loadexception", this.onLoad);
10677         }
10678         if(store){
10679           
10680             store.on("datachanged", this.refresh, this);
10681             store.on("add", this.onAdd, this);
10682             store.on("remove", this.onRemove, this);
10683             store.on("update", this.onUpdate, this);
10684             store.on("clear", this.refresh, this);
10685             store.on("beforeload", this.onBeforeLoad, this);
10686             store.on("load", this.onLoad, this);
10687             store.on("loadexception", this.onLoad, this);
10688         }
10689         
10690         if(store){
10691             this.refresh();
10692         }
10693     },
10694     /**
10695      * onbeforeLoad - masks the loading area.
10696      *
10697      */
10698     onBeforeLoad : function(store,opts)
10699     {
10700          Roo.log('onBeforeLoad');   
10701         if (!opts.add) {
10702             this.el.update("");
10703         }
10704         this.el.mask(this.mask ? this.mask : "Loading" ); 
10705     },
10706     onLoad : function ()
10707     {
10708         this.el.unmask();
10709     },
10710     
10711
10712     /**
10713      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
10714      * @param {HTMLElement} node
10715      * @return {HTMLElement} The template node
10716      */
10717     findItemFromChild : function(node){
10718         var el = this.dataName  ?
10719             this.el.child('.roo-tpl-' + this.dataName,true) :
10720             this.el.dom; 
10721         
10722         if(!node || node.parentNode == el){
10723                     return node;
10724             }
10725             var p = node.parentNode;
10726             while(p && p != el){
10727             if(p.parentNode == el){
10728                 return p;
10729             }
10730             p = p.parentNode;
10731         }
10732             return null;
10733     },
10734
10735     /** @ignore */
10736     onClick : function(e){
10737         var item = this.findItemFromChild(e.getTarget());
10738         if(item){
10739             var index = this.indexOf(item);
10740             if(this.onItemClick(item, index, e) !== false){
10741                 this.fireEvent("click", this, index, item, e);
10742             }
10743         }else{
10744             this.clearSelections();
10745         }
10746     },
10747
10748     /** @ignore */
10749     onContextMenu : function(e){
10750         var item = this.findItemFromChild(e.getTarget());
10751         if(item){
10752             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
10753         }
10754     },
10755
10756     /** @ignore */
10757     onDblClick : function(e){
10758         var item = this.findItemFromChild(e.getTarget());
10759         if(item){
10760             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
10761         }
10762     },
10763
10764     onItemClick : function(item, index, e)
10765     {
10766         if(this.fireEvent("beforeclick", this, index, item, e) === false){
10767             return false;
10768         }
10769         if (this.toggleSelect) {
10770             var m = this.isSelected(item) ? 'unselect' : 'select';
10771             Roo.log(m);
10772             var _t = this;
10773             _t[m](item, true, false);
10774             return true;
10775         }
10776         if(this.multiSelect || this.singleSelect){
10777             if(this.multiSelect && e.shiftKey && this.lastSelection){
10778                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
10779             }else{
10780                 this.select(item, this.multiSelect && e.ctrlKey);
10781                 this.lastSelection = item;
10782             }
10783             e.preventDefault();
10784         }
10785         return true;
10786     },
10787
10788     /**
10789      * Get the number of selected nodes.
10790      * @return {Number}
10791      */
10792     getSelectionCount : function(){
10793         return this.selections.length;
10794     },
10795
10796     /**
10797      * Get the currently selected nodes.
10798      * @return {Array} An array of HTMLElements
10799      */
10800     getSelectedNodes : function(){
10801         return this.selections;
10802     },
10803
10804     /**
10805      * Get the indexes of the selected nodes.
10806      * @return {Array}
10807      */
10808     getSelectedIndexes : function(){
10809         var indexes = [], s = this.selections;
10810         for(var i = 0, len = s.length; i < len; i++){
10811             indexes.push(s[i].nodeIndex);
10812         }
10813         return indexes;
10814     },
10815
10816     /**
10817      * Clear all selections
10818      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
10819      */
10820     clearSelections : function(suppressEvent){
10821         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
10822             this.cmp.elements = this.selections;
10823             this.cmp.removeClass(this.selectedClass);
10824             this.selections = [];
10825             if(!suppressEvent){
10826                 this.fireEvent("selectionchange", this, this.selections);
10827             }
10828         }
10829     },
10830
10831     /**
10832      * Returns true if the passed node is selected
10833      * @param {HTMLElement/Number} node The node or node index
10834      * @return {Boolean}
10835      */
10836     isSelected : function(node){
10837         var s = this.selections;
10838         if(s.length < 1){
10839             return false;
10840         }
10841         node = this.getNode(node);
10842         return s.indexOf(node) !== -1;
10843     },
10844
10845     /**
10846      * Selects nodes.
10847      * @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
10848      * @param {Boolean} keepExisting (optional) true to keep existing selections
10849      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
10850      */
10851     select : function(nodeInfo, keepExisting, suppressEvent){
10852         if(nodeInfo instanceof Array){
10853             if(!keepExisting){
10854                 this.clearSelections(true);
10855             }
10856             for(var i = 0, len = nodeInfo.length; i < len; i++){
10857                 this.select(nodeInfo[i], true, true);
10858             }
10859             return;
10860         } 
10861         var node = this.getNode(nodeInfo);
10862         if(!node || this.isSelected(node)){
10863             return; // already selected.
10864         }
10865         if(!keepExisting){
10866             this.clearSelections(true);
10867         }
10868         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
10869             Roo.fly(node).addClass(this.selectedClass);
10870             this.selections.push(node);
10871             if(!suppressEvent){
10872                 this.fireEvent("selectionchange", this, this.selections);
10873             }
10874         }
10875         
10876         
10877     },
10878       /**
10879      * Unselects nodes.
10880      * @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
10881      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
10882      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
10883      */
10884     unselect : function(nodeInfo, keepExisting, suppressEvent)
10885     {
10886         if(nodeInfo instanceof Array){
10887             Roo.each(this.selections, function(s) {
10888                 this.unselect(s, nodeInfo);
10889             }, this);
10890             return;
10891         }
10892         var node = this.getNode(nodeInfo);
10893         if(!node || !this.isSelected(node)){
10894             Roo.log("not selected");
10895             return; // not selected.
10896         }
10897         // fireevent???
10898         var ns = [];
10899         Roo.each(this.selections, function(s) {
10900             if (s == node ) {
10901                 Roo.fly(node).removeClass(this.selectedClass);
10902
10903                 return;
10904             }
10905             ns.push(s);
10906         },this);
10907         
10908         this.selections= ns;
10909         this.fireEvent("selectionchange", this, this.selections);
10910     },
10911
10912     /**
10913      * Gets a template node.
10914      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
10915      * @return {HTMLElement} The node or null if it wasn't found
10916      */
10917     getNode : function(nodeInfo){
10918         if(typeof nodeInfo == "string"){
10919             return document.getElementById(nodeInfo);
10920         }else if(typeof nodeInfo == "number"){
10921             return this.nodes[nodeInfo];
10922         }
10923         return nodeInfo;
10924     },
10925
10926     /**
10927      * Gets a range template nodes.
10928      * @param {Number} startIndex
10929      * @param {Number} endIndex
10930      * @return {Array} An array of nodes
10931      */
10932     getNodes : function(start, end){
10933         var ns = this.nodes;
10934         start = start || 0;
10935         end = typeof end == "undefined" ? ns.length - 1 : end;
10936         var nodes = [];
10937         if(start <= end){
10938             for(var i = start; i <= end; i++){
10939                 nodes.push(ns[i]);
10940             }
10941         } else{
10942             for(var i = start; i >= end; i--){
10943                 nodes.push(ns[i]);
10944             }
10945         }
10946         return nodes;
10947     },
10948
10949     /**
10950      * Finds the index of the passed node
10951      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
10952      * @return {Number} The index of the node or -1
10953      */
10954     indexOf : function(node){
10955         node = this.getNode(node);
10956         if(typeof node.nodeIndex == "number"){
10957             return node.nodeIndex;
10958         }
10959         var ns = this.nodes;
10960         for(var i = 0, len = ns.length; i < len; i++){
10961             if(ns[i] == node){
10962                 return i;
10963             }
10964         }
10965         return -1;
10966     }
10967 });
10968 /*
10969  * - LGPL
10970  *
10971  * based on jquery fullcalendar
10972  * 
10973  */
10974
10975 Roo.bootstrap = Roo.bootstrap || {};
10976 /**
10977  * @class Roo.bootstrap.Calendar
10978  * @extends Roo.bootstrap.Component
10979  * Bootstrap Calendar class
10980  * @cfg {Boolean} loadMask (true|false) default false
10981  * @cfg {Object} header generate the user specific header of the calendar, default false
10982
10983  * @constructor
10984  * Create a new Container
10985  * @param {Object} config The config object
10986  */
10987
10988
10989
10990 Roo.bootstrap.Calendar = function(config){
10991     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
10992      this.addEvents({
10993         /**
10994              * @event select
10995              * Fires when a date is selected
10996              * @param {DatePicker} this
10997              * @param {Date} date The selected date
10998              */
10999         'select': true,
11000         /**
11001              * @event monthchange
11002              * Fires when the displayed month changes 
11003              * @param {DatePicker} this
11004              * @param {Date} date The selected month
11005              */
11006         'monthchange': true,
11007         /**
11008              * @event evententer
11009              * Fires when mouse over an event
11010              * @param {Calendar} this
11011              * @param {event} Event
11012              */
11013         'evententer': true,
11014         /**
11015              * @event eventleave
11016              * Fires when the mouse leaves an
11017              * @param {Calendar} this
11018              * @param {event}
11019              */
11020         'eventleave': true,
11021         /**
11022              * @event eventclick
11023              * Fires when the mouse click an
11024              * @param {Calendar} this
11025              * @param {event}
11026              */
11027         'eventclick': true
11028         
11029     });
11030
11031 };
11032
11033 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
11034     
11035      /**
11036      * @cfg {Number} startDay
11037      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
11038      */
11039     startDay : 0,
11040     
11041     loadMask : false,
11042     
11043     header : false,
11044       
11045     getAutoCreate : function(){
11046         
11047         
11048         var fc_button = function(name, corner, style, content ) {
11049             return Roo.apply({},{
11050                 tag : 'span',
11051                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
11052                          (corner.length ?
11053                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
11054                             ''
11055                         ),
11056                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
11057                 unselectable: 'on'
11058             });
11059         };
11060         
11061         var header = {};
11062         
11063         if(!this.header){
11064             header = {
11065                 tag : 'table',
11066                 cls : 'fc-header',
11067                 style : 'width:100%',
11068                 cn : [
11069                     {
11070                         tag: 'tr',
11071                         cn : [
11072                             {
11073                                 tag : 'td',
11074                                 cls : 'fc-header-left',
11075                                 cn : [
11076                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
11077                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
11078                                     { tag: 'span', cls: 'fc-header-space' },
11079                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
11080
11081
11082                                 ]
11083                             },
11084
11085                             {
11086                                 tag : 'td',
11087                                 cls : 'fc-header-center',
11088                                 cn : [
11089                                     {
11090                                         tag: 'span',
11091                                         cls: 'fc-header-title',
11092                                         cn : {
11093                                             tag: 'H2',
11094                                             html : 'month / year'
11095                                         }
11096                                     }
11097
11098                                 ]
11099                             },
11100                             {
11101                                 tag : 'td',
11102                                 cls : 'fc-header-right',
11103                                 cn : [
11104                               /*      fc_button('month', 'left', '', 'month' ),
11105                                     fc_button('week', '', '', 'week' ),
11106                                     fc_button('day', 'right', '', 'day' )
11107                                 */    
11108
11109                                 ]
11110                             }
11111
11112                         ]
11113                     }
11114                 ]
11115             };
11116         }
11117         
11118         header = this.header;
11119         
11120        
11121         var cal_heads = function() {
11122             var ret = [];
11123             // fixme - handle this.
11124             
11125             for (var i =0; i < Date.dayNames.length; i++) {
11126                 var d = Date.dayNames[i];
11127                 ret.push({
11128                     tag: 'th',
11129                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
11130                     html : d.substring(0,3)
11131                 });
11132                 
11133             }
11134             ret[0].cls += ' fc-first';
11135             ret[6].cls += ' fc-last';
11136             return ret;
11137         };
11138         var cal_cell = function(n) {
11139             return  {
11140                 tag: 'td',
11141                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
11142                 cn : [
11143                     {
11144                         cn : [
11145                             {
11146                                 cls: 'fc-day-number',
11147                                 html: 'D'
11148                             },
11149                             {
11150                                 cls: 'fc-day-content',
11151                              
11152                                 cn : [
11153                                      {
11154                                         style: 'position: relative;' // height: 17px;
11155                                     }
11156                                 ]
11157                             }
11158                             
11159                             
11160                         ]
11161                     }
11162                 ]
11163                 
11164             }
11165         };
11166         var cal_rows = function() {
11167             
11168             var ret = []
11169             for (var r = 0; r < 6; r++) {
11170                 var row= {
11171                     tag : 'tr',
11172                     cls : 'fc-week',
11173                     cn : []
11174                 };
11175                 
11176                 for (var i =0; i < Date.dayNames.length; i++) {
11177                     var d = Date.dayNames[i];
11178                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
11179
11180                 }
11181                 row.cn[0].cls+=' fc-first';
11182                 row.cn[0].cn[0].style = 'min-height:90px';
11183                 row.cn[6].cls+=' fc-last';
11184                 ret.push(row);
11185                 
11186             }
11187             ret[0].cls += ' fc-first';
11188             ret[4].cls += ' fc-prev-last';
11189             ret[5].cls += ' fc-last';
11190             return ret;
11191             
11192         };
11193         
11194         var cal_table = {
11195             tag: 'table',
11196             cls: 'fc-border-separate',
11197             style : 'width:100%',
11198             cellspacing  : 0,
11199             cn : [
11200                 { 
11201                     tag: 'thead',
11202                     cn : [
11203                         { 
11204                             tag: 'tr',
11205                             cls : 'fc-first fc-last',
11206                             cn : cal_heads()
11207                         }
11208                     ]
11209                 },
11210                 { 
11211                     tag: 'tbody',
11212                     cn : cal_rows()
11213                 }
11214                   
11215             ]
11216         };
11217          
11218          var cfg = {
11219             cls : 'fc fc-ltr',
11220             cn : [
11221                 header,
11222                 {
11223                     cls : 'fc-content',
11224                     style : "position: relative;",
11225                     cn : [
11226                         {
11227                             cls : 'fc-view fc-view-month fc-grid',
11228                             style : 'position: relative',
11229                             unselectable : 'on',
11230                             cn : [
11231                                 {
11232                                     cls : 'fc-event-container',
11233                                     style : 'position:absolute;z-index:8;top:0;left:0;'
11234                                 },
11235                                 cal_table
11236                             ]
11237                         }
11238                     ]
11239     
11240                 }
11241            ] 
11242             
11243         };
11244         
11245          
11246         
11247         return cfg;
11248     },
11249     
11250     
11251     initEvents : function()
11252     {
11253         if(!this.store){
11254             throw "can not find store for calendar";
11255         }
11256         
11257         var mark = {
11258             tag: "div",
11259             cls:"x-dlg-mask",
11260             style: "text-align:center",
11261             cn: [
11262                 {
11263                     tag: "div",
11264                     style: "background-color:white;width:50%;margin:250 auto",
11265                     cn: [
11266                         {
11267                             tag: "img",
11268                             src: rootURL + '/roojs1/images/ux/lightbox/loading.gif'
11269                         },
11270                         {
11271                             tag: "span",
11272                             html: "Loading"
11273                         }
11274                         
11275                     ]
11276                 }
11277             ]
11278         }
11279         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
11280         
11281         var size = this.el.select('.fc-content', true).first().getSize();
11282         this.maskEl.setSize(size.width, size.height);
11283         this.maskEl.enableDisplayMode("block");
11284         if(!this.loadMask){
11285             this.maskEl.hide();
11286         }
11287         
11288         this.store = Roo.factory(this.store, Roo.data);
11289         this.store.on('load', this.onLoad, this);
11290         this.store.on('beforeload', this.onBeforeLoad, this);
11291         
11292         this.resize();
11293         
11294         this.cells = this.el.select('.fc-day',true);
11295         //Roo.log(this.cells);
11296         this.textNodes = this.el.query('.fc-day-number');
11297         this.cells.addClassOnOver('fc-state-hover');
11298         
11299         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
11300         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
11301         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
11302         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
11303         
11304         this.on('monthchange', this.onMonthChange, this);
11305         
11306         this.update(new Date().clearTime());
11307     },
11308     
11309     resize : function() {
11310         var sz  = this.el.getSize();
11311         
11312         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
11313         this.el.select('.fc-day-content div',true).setHeight(34);
11314     },
11315     
11316     
11317     // private
11318     showPrevMonth : function(e){
11319         this.update(this.activeDate.add("mo", -1));
11320     },
11321     showToday : function(e){
11322         this.update(new Date().clearTime());
11323     },
11324     // private
11325     showNextMonth : function(e){
11326         this.update(this.activeDate.add("mo", 1));
11327     },
11328
11329     // private
11330     showPrevYear : function(){
11331         this.update(this.activeDate.add("y", -1));
11332     },
11333
11334     // private
11335     showNextYear : function(){
11336         this.update(this.activeDate.add("y", 1));
11337     },
11338
11339     
11340    // private
11341     update : function(date)
11342     {
11343         var vd = this.activeDate;
11344         this.activeDate = date;
11345 //        if(vd && this.el){
11346 //            var t = date.getTime();
11347 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
11348 //                Roo.log('using add remove');
11349 //                
11350 //                this.fireEvent('monthchange', this, date);
11351 //                
11352 //                this.cells.removeClass("fc-state-highlight");
11353 //                this.cells.each(function(c){
11354 //                   if(c.dateValue == t){
11355 //                       c.addClass("fc-state-highlight");
11356 //                       setTimeout(function(){
11357 //                            try{c.dom.firstChild.focus();}catch(e){}
11358 //                       }, 50);
11359 //                       return false;
11360 //                   }
11361 //                   return true;
11362 //                });
11363 //                return;
11364 //            }
11365 //        }
11366         
11367         var days = date.getDaysInMonth();
11368         
11369         var firstOfMonth = date.getFirstDateOfMonth();
11370         var startingPos = firstOfMonth.getDay()-this.startDay;
11371         
11372         if(startingPos < this.startDay){
11373             startingPos += 7;
11374         }
11375         
11376         var pm = date.add(Date.MONTH, -1);
11377         var prevStart = pm.getDaysInMonth()-startingPos;
11378 //        
11379         this.cells = this.el.select('.fc-day',true);
11380         this.textNodes = this.el.query('.fc-day-number');
11381         this.cells.addClassOnOver('fc-state-hover');
11382         
11383         var cells = this.cells.elements;
11384         var textEls = this.textNodes;
11385         
11386         Roo.each(cells, function(cell){
11387             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
11388         });
11389         
11390         days += startingPos;
11391
11392         // convert everything to numbers so it's fast
11393         var day = 86400000;
11394         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
11395         //Roo.log(d);
11396         //Roo.log(pm);
11397         //Roo.log(prevStart);
11398         
11399         var today = new Date().clearTime().getTime();
11400         var sel = date.clearTime().getTime();
11401         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
11402         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
11403         var ddMatch = this.disabledDatesRE;
11404         var ddText = this.disabledDatesText;
11405         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
11406         var ddaysText = this.disabledDaysText;
11407         var format = this.format;
11408         
11409         var setCellClass = function(cal, cell){
11410             
11411             //Roo.log('set Cell Class');
11412             cell.title = "";
11413             var t = d.getTime();
11414             
11415             //Roo.log(d);
11416             
11417             cell.dateValue = t;
11418             if(t == today){
11419                 cell.className += " fc-today";
11420                 cell.className += " fc-state-highlight";
11421                 cell.title = cal.todayText;
11422             }
11423             if(t == sel){
11424                 // disable highlight in other month..
11425                 //cell.className += " fc-state-highlight";
11426                 
11427             }
11428             // disabling
11429             if(t < min) {
11430                 cell.className = " fc-state-disabled";
11431                 cell.title = cal.minText;
11432                 return;
11433             }
11434             if(t > max) {
11435                 cell.className = " fc-state-disabled";
11436                 cell.title = cal.maxText;
11437                 return;
11438             }
11439             if(ddays){
11440                 if(ddays.indexOf(d.getDay()) != -1){
11441                     cell.title = ddaysText;
11442                     cell.className = " fc-state-disabled";
11443                 }
11444             }
11445             if(ddMatch && format){
11446                 var fvalue = d.dateFormat(format);
11447                 if(ddMatch.test(fvalue)){
11448                     cell.title = ddText.replace("%0", fvalue);
11449                     cell.className = " fc-state-disabled";
11450                 }
11451             }
11452             
11453             if (!cell.initialClassName) {
11454                 cell.initialClassName = cell.dom.className;
11455             }
11456             
11457             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
11458         };
11459
11460         var i = 0;
11461         
11462         for(; i < startingPos; i++) {
11463             textEls[i].innerHTML = (++prevStart);
11464             d.setDate(d.getDate()+1);
11465             
11466             cells[i].className = "fc-past fc-other-month";
11467             setCellClass(this, cells[i]);
11468         }
11469         
11470         var intDay = 0;
11471         
11472         for(; i < days; i++){
11473             intDay = i - startingPos + 1;
11474             textEls[i].innerHTML = (intDay);
11475             d.setDate(d.getDate()+1);
11476             
11477             cells[i].className = ''; // "x-date-active";
11478             setCellClass(this, cells[i]);
11479         }
11480         var extraDays = 0;
11481         
11482         for(; i < 42; i++) {
11483             textEls[i].innerHTML = (++extraDays);
11484             d.setDate(d.getDate()+1);
11485             
11486             cells[i].className = "fc-future fc-other-month";
11487             setCellClass(this, cells[i]);
11488         }
11489         
11490         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
11491         
11492         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
11493         
11494         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
11495         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
11496         
11497         if(totalRows != 6){
11498             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
11499             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
11500         }
11501         
11502         this.fireEvent('monthchange', this, date);
11503         
11504         
11505         /*
11506         if(!this.internalRender){
11507             var main = this.el.dom.firstChild;
11508             var w = main.offsetWidth;
11509             this.el.setWidth(w + this.el.getBorderWidth("lr"));
11510             Roo.fly(main).setWidth(w);
11511             this.internalRender = true;
11512             // opera does not respect the auto grow header center column
11513             // then, after it gets a width opera refuses to recalculate
11514             // without a second pass
11515             if(Roo.isOpera && !this.secondPass){
11516                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
11517                 this.secondPass = true;
11518                 this.update.defer(10, this, [date]);
11519             }
11520         }
11521         */
11522         
11523     },
11524     
11525     findCell : function(dt) {
11526         dt = dt.clearTime().getTime();
11527         var ret = false;
11528         this.cells.each(function(c){
11529             //Roo.log("check " +c.dateValue + '?=' + dt);
11530             if(c.dateValue == dt){
11531                 ret = c;
11532                 return false;
11533             }
11534             return true;
11535         });
11536         
11537         return ret;
11538     },
11539     
11540     findCells : function(ev) {
11541         var s = ev.start.clone().clearTime().getTime();
11542        // Roo.log(s);
11543         var e= ev.end.clone().clearTime().getTime();
11544        // Roo.log(e);
11545         var ret = [];
11546         this.cells.each(function(c){
11547              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
11548             
11549             if(c.dateValue > e){
11550                 return ;
11551             }
11552             if(c.dateValue < s){
11553                 return ;
11554             }
11555             ret.push(c);
11556         });
11557         
11558         return ret;    
11559     },
11560     
11561     findBestRow: function(cells)
11562     {
11563         var ret = 0;
11564         
11565         for (var i =0 ; i < cells.length;i++) {
11566             ret  = Math.max(cells[i].rows || 0,ret);
11567         }
11568         return ret;
11569         
11570     },
11571     
11572     
11573     addItem : function(ev)
11574     {
11575         // look for vertical location slot in
11576         var cells = this.findCells(ev);
11577         
11578         ev.row = this.findBestRow(cells);
11579         
11580         // work out the location.
11581         
11582         var crow = false;
11583         var rows = [];
11584         for(var i =0; i < cells.length; i++) {
11585             if (!crow) {
11586                 crow = {
11587                     start : cells[i],
11588                     end :  cells[i]
11589                 };
11590                 continue;
11591             }
11592             if (crow.start.getY() == cells[i].getY()) {
11593                 // on same row.
11594                 crow.end = cells[i];
11595                 continue;
11596             }
11597             // different row.
11598             rows.push(crow);
11599             crow = {
11600                 start: cells[i],
11601                 end : cells[i]
11602             };
11603             
11604         }
11605         
11606         rows.push(crow);
11607         ev.els = [];
11608         ev.rows = rows;
11609         ev.cells = cells;
11610         for (var i = 0; i < cells.length;i++) {
11611             cells[i].rows = Math.max(cells[i].rows || 0 , ev.row + 1 );
11612             
11613         }
11614         
11615         this.calevents.push(ev);
11616     },
11617     
11618     clearEvents: function() {
11619         
11620         if(!this.calevents){
11621             return;
11622         }
11623         
11624         Roo.each(this.cells.elements, function(c){
11625             c.rows = 0;
11626         });
11627         
11628         Roo.each(this.calevents, function(e) {
11629             Roo.each(e.els, function(el) {
11630                 el.un('mouseenter' ,this.onEventEnter, this);
11631                 el.un('mouseleave' ,this.onEventLeave, this);
11632                 el.remove();
11633             },this);
11634         },this);
11635         
11636     },
11637     
11638     renderEvents: function()
11639     {   
11640         // first make sure there is enough space..
11641         
11642         this.cells.each(function(c) {
11643         
11644             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.rows * 20));
11645         });
11646         
11647         for (var e = 0; e < this.calevents.length; e++) {
11648             var ev = this.calevents[e];
11649             var cells = ev.cells;
11650             var rows = ev.rows;
11651             
11652             for(var i =0; i < rows.length; i++) {
11653                 
11654                  
11655                 // how many rows should it span..
11656                 
11657                 var  cfg = {
11658                     cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
11659                     style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
11660                     
11661                     unselectable : "on",
11662                     cn : [
11663                         {
11664                             cls: 'fc-event-inner',
11665                             cn : [
11666 //                                {
11667 //                                  tag:'span',
11668 //                                  cls: 'fc-event-time',
11669 //                                  html : cells.length > 1 ? '' : ev.time
11670 //                                },
11671                                 {
11672                                   tag:'span',
11673                                   cls: 'fc-event-title',
11674                                   html : String.format('{0}', ev.title)
11675                                 }
11676                                 
11677                                 
11678                             ]
11679                         },
11680                         {
11681                             cls: 'ui-resizable-handle ui-resizable-e',
11682                             html : '&nbsp;&nbsp;&nbsp'
11683                         }
11684                         
11685                     ]
11686                 };
11687                 if (i == 0) {
11688                     cfg.cls += ' fc-event-start';
11689                 }
11690                 if ((i+1) == rows.length) {
11691                     cfg.cls += ' fc-event-end';
11692                 }
11693                 
11694                 var ctr = this.el.select('.fc-event-container',true).first();
11695                 var cg = ctr.createChild(cfg);
11696                 
11697                 cg.on('mouseenter' ,this.onEventEnter, this, ev);
11698                 cg.on('mouseleave' ,this.onEventLeave, this, ev);
11699                 cg.on('click', this.onEventClick, this, ev);
11700                 
11701                 ev.els.push(cg);
11702                 
11703                 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
11704                 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
11705                 //Roo.log(cg);
11706                 cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
11707                 cg.setWidth(ebox.right - sbox.x -2);
11708             }
11709             
11710             
11711         }
11712         
11713     },
11714     
11715     onEventEnter: function (e, el,event,d) {
11716         this.fireEvent('evententer', this, el, event);
11717     },
11718     
11719     onEventLeave: function (e, el,event,d) {
11720         this.fireEvent('eventleave', this, el, event);
11721     },
11722     
11723     onEventClick: function (e, el,event,d) {
11724         this.fireEvent('eventclick', this, el, event);
11725     },
11726     
11727     onMonthChange: function () {
11728         this.store.load();
11729     },
11730     
11731     onLoad: function () 
11732     {   
11733         this.calevents = [];
11734         var cal = this;
11735         
11736         if(this.store.getCount() > 0){
11737             this.store.data.each(function(d){
11738                cal.addItem({
11739                     id : d.data.id,
11740                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
11741                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
11742                     time : d.data.start_time,
11743                     title : d.data.title,
11744                     description : d.data.description,
11745                     venue : d.data.venue
11746                 });
11747             });
11748         }
11749         
11750         this.renderEvents();
11751         
11752         if(this.loadMask){
11753             this.maskEl.hide();
11754         }
11755     },
11756     
11757     onBeforeLoad: function()
11758     {
11759         this.clearEvents();
11760         
11761         if(this.loadMask){
11762             this.maskEl.show();
11763         }
11764     }
11765 });
11766
11767  
11768  /*
11769  * - LGPL
11770  *
11771  * element
11772  * 
11773  */
11774
11775 /**
11776  * @class Roo.bootstrap.Popover
11777  * @extends Roo.bootstrap.Component
11778  * Bootstrap Popover class
11779  * @cfg {String} html contents of the popover   (or false to use children..)
11780  * @cfg {String} title of popover (or false to hide)
11781  * @cfg {String} placement how it is placed
11782  * @cfg {String} trigger click || hover (or false to trigger manually)
11783  * @cfg {String} over what (parent or false to trigger manually.)
11784  * 
11785  * @constructor
11786  * Create a new Popover
11787  * @param {Object} config The config object
11788  */
11789
11790 Roo.bootstrap.Popover = function(config){
11791     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
11792 };
11793
11794 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
11795     
11796     title: 'Fill in a title',
11797     html: false,
11798     
11799     placement : 'right',
11800     trigger : 'hover', // hover
11801     
11802     over: 'parent',
11803     
11804     can_build_overlaid : false,
11805     
11806     getChildContainer : function()
11807     {
11808         return this.el.select('.popover-content',true).first();
11809     },
11810     
11811     getAutoCreate : function(){
11812          Roo.log('make popover?');
11813         var cfg = {
11814            cls : 'popover roo-dynamic',
11815            style: 'display:block',
11816            cn : [
11817                 {
11818                     cls : 'arrow'
11819                 },
11820                 {
11821                     cls : 'popover-inner',
11822                     cn : [
11823                         {
11824                             tag: 'h3',
11825                             cls: 'popover-title',
11826                             html : this.title
11827                         },
11828                         {
11829                             cls : 'popover-content',
11830                             html : this.html
11831                         }
11832                     ]
11833                     
11834                 }
11835            ]
11836         };
11837         
11838         return cfg;
11839     },
11840     setTitle: function(str)
11841     {
11842         this.el.select('.popover-title',true).first().dom.innerHTML = str;
11843     },
11844     setContent: function(str)
11845     {
11846         this.el.select('.popover-content',true).first().dom.innerHTML = str;
11847     },
11848     // as it get's added to the bottom of the page.
11849     onRender : function(ct, position)
11850     {
11851         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
11852         if(!this.el){
11853             var cfg = Roo.apply({},  this.getAutoCreate());
11854             cfg.id = Roo.id();
11855             
11856             if (this.cls) {
11857                 cfg.cls += ' ' + this.cls;
11858             }
11859             if (this.style) {
11860                 cfg.style = this.style;
11861             }
11862             Roo.log("adding to ")
11863             this.el = Roo.get(document.body).createChild(cfg, position);
11864             Roo.log(this.el);
11865         }
11866         this.initEvents();
11867     },
11868     
11869     initEvents : function()
11870     {
11871         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
11872         this.el.enableDisplayMode('block');
11873         this.el.hide();
11874         if (this.over === false) {
11875             return; 
11876         }
11877         if (this.triggers === false) {
11878             return;
11879         }
11880         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
11881         var triggers = this.trigger ? this.trigger.split(' ') : [];
11882         Roo.each(triggers, function(trigger) {
11883         
11884             if (trigger == 'click') {
11885                 on_el.on('click', this.toggle, this);
11886             } else if (trigger != 'manual') {
11887                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
11888                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
11889       
11890                 on_el.on(eventIn  ,this.enter, this);
11891                 on_el.on(eventOut, this.leave, this);
11892             }
11893         }, this);
11894         
11895     },
11896     
11897     
11898     // private
11899     timeout : null,
11900     hoverState : null,
11901     
11902     toggle : function () {
11903         this.hoverState == 'in' ? this.leave() : this.enter();
11904     },
11905     
11906     enter : function () {
11907        
11908     
11909         clearTimeout(this.timeout);
11910     
11911         this.hoverState = 'in'
11912     
11913         if (!this.delay || !this.delay.show) {
11914             this.show();
11915             return 
11916         }
11917         var _t = this;
11918         this.timeout = setTimeout(function () {
11919             if (_t.hoverState == 'in') {
11920                 _t.show();
11921             }
11922         }, this.delay.show)
11923     },
11924     leave : function() {
11925         clearTimeout(this.timeout);
11926     
11927         this.hoverState = 'out'
11928     
11929         if (!this.delay || !this.delay.hide) {
11930             this.hide();
11931             return 
11932         }
11933         var _t = this;
11934         this.timeout = setTimeout(function () {
11935             if (_t.hoverState == 'out') {
11936                 _t.hide();
11937             }
11938         }, this.delay.hide)
11939     },
11940     
11941     show : function (on_el)
11942     {
11943         if (!on_el) {
11944             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
11945         }
11946         // set content.
11947         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
11948         if (this.html !== false) {
11949             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
11950         }
11951         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
11952         if (!this.title.length) {
11953             this.el.select('.popover-title',true).hide();
11954         }
11955         
11956         var placement = typeof this.placement == 'function' ?
11957             this.placement.call(this, this.el, on_el) :
11958             this.placement;
11959             
11960         var autoToken = /\s?auto?\s?/i;
11961         var autoPlace = autoToken.test(placement);
11962         if (autoPlace) {
11963             placement = placement.replace(autoToken, '') || 'top';
11964         }
11965         
11966         //this.el.detach()
11967         //this.el.setXY([0,0]);
11968         this.el.show();
11969         this.el.dom.style.display='block';
11970         this.el.addClass(placement);
11971         
11972         //this.el.appendTo(on_el);
11973         
11974         var p = this.getPosition();
11975         var box = this.el.getBox();
11976         
11977         if (autoPlace) {
11978             // fixme..
11979         }
11980         var align = Roo.bootstrap.Popover.alignment[placement]
11981         this.el.alignTo(on_el, align[0],align[1]);
11982         //var arrow = this.el.select('.arrow',true).first();
11983         //arrow.set(align[2], 
11984         
11985         this.el.addClass('in');
11986         this.hoverState = null;
11987         
11988         if (this.el.hasClass('fade')) {
11989             // fade it?
11990         }
11991         
11992     },
11993     hide : function()
11994     {
11995         this.el.setXY([0,0]);
11996         this.el.removeClass('in');
11997         this.el.hide();
11998         
11999     }
12000     
12001 });
12002
12003 Roo.bootstrap.Popover.alignment = {
12004     'left' : ['r-l', [-10,0], 'right'],
12005     'right' : ['l-r', [10,0], 'left'],
12006     'bottom' : ['t-b', [0,10], 'top'],
12007     'top' : [ 'b-t', [0,-10], 'bottom']
12008 };
12009
12010  /*
12011  * - LGPL
12012  *
12013  * Progress
12014  * 
12015  */
12016
12017 /**
12018  * @class Roo.bootstrap.Progress
12019  * @extends Roo.bootstrap.Component
12020  * Bootstrap Progress class
12021  * @cfg {Boolean} striped striped of the progress bar
12022  * @cfg {Boolean} active animated of the progress bar
12023  * 
12024  * 
12025  * @constructor
12026  * Create a new Progress
12027  * @param {Object} config The config object
12028  */
12029
12030 Roo.bootstrap.Progress = function(config){
12031     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
12032 };
12033
12034 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
12035     
12036     striped : false,
12037     active: false,
12038     
12039     getAutoCreate : function(){
12040         var cfg = {
12041             tag: 'div',
12042             cls: 'progress'
12043         };
12044         
12045         
12046         if(this.striped){
12047             cfg.cls += ' progress-striped';
12048         }
12049       
12050         if(this.active){
12051             cfg.cls += ' active';
12052         }
12053         
12054         
12055         return cfg;
12056     }
12057    
12058 });
12059
12060  
12061
12062  /*
12063  * - LGPL
12064  *
12065  * ProgressBar
12066  * 
12067  */
12068
12069 /**
12070  * @class Roo.bootstrap.ProgressBar
12071  * @extends Roo.bootstrap.Component
12072  * Bootstrap ProgressBar class
12073  * @cfg {Number} aria_valuenow aria-value now
12074  * @cfg {Number} aria_valuemin aria-value min
12075  * @cfg {Number} aria_valuemax aria-value max
12076  * @cfg {String} label label for the progress bar
12077  * @cfg {String} panel (success | info | warning | danger )
12078  * @cfg {String} role role of the progress bar
12079  * @cfg {String} sr_only text
12080  * 
12081  * 
12082  * @constructor
12083  * Create a new ProgressBar
12084  * @param {Object} config The config object
12085  */
12086
12087 Roo.bootstrap.ProgressBar = function(config){
12088     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
12089 };
12090
12091 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
12092     
12093     aria_valuenow : 0,
12094     aria_valuemin : 0,
12095     aria_valuemax : 100,
12096     label : false,
12097     panel : false,
12098     role : false,
12099     sr_only: false,
12100     
12101     getAutoCreate : function()
12102     {
12103         
12104         var cfg = {
12105             tag: 'div',
12106             cls: 'progress-bar',
12107             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
12108         };
12109         
12110         if(this.sr_only){
12111             cfg.cn = {
12112                 tag: 'span',
12113                 cls: 'sr-only',
12114                 html: this.sr_only
12115             }
12116         }
12117         
12118         if(this.role){
12119             cfg.role = this.role;
12120         }
12121         
12122         if(this.aria_valuenow){
12123             cfg['aria-valuenow'] = this.aria_valuenow;
12124         }
12125         
12126         if(this.aria_valuemin){
12127             cfg['aria-valuemin'] = this.aria_valuemin;
12128         }
12129         
12130         if(this.aria_valuemax){
12131             cfg['aria-valuemax'] = this.aria_valuemax;
12132         }
12133         
12134         if(this.label && !this.sr_only){
12135             cfg.html = this.label;
12136         }
12137         
12138         if(this.panel){
12139             cfg.cls += ' progress-bar-' + this.panel;
12140         }
12141         
12142         return cfg;
12143     },
12144     
12145     update : function(aria_valuenow)
12146     {
12147         this.aria_valuenow = aria_valuenow;
12148         
12149         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
12150     }
12151    
12152 });
12153
12154  
12155
12156  /*
12157  * - LGPL
12158  *
12159  * TabPanel
12160  * 
12161  */
12162
12163 /**
12164  * @class Roo.bootstrap.TabPanel
12165  * @extends Roo.bootstrap.Component
12166  * Bootstrap TabPanel class
12167  * @cfg {Boolean} active panel active
12168  * @cfg {String} html panel content
12169  * @cfg {String} tabId tab relate id
12170  * @cfg {String} navId The navbar which triggers show hide
12171  * 
12172  * 
12173  * @constructor
12174  * Create a new TabPanel
12175  * @param {Object} config The config object
12176  */
12177
12178 Roo.bootstrap.TabPanel = function(config){
12179     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
12180      this.addEvents({
12181         /**
12182              * @event changed
12183              * Fires when the active status changes
12184              * @param {Roo.bootstrap.TabPanel} this
12185              * @param {Boolean} state the new state
12186             
12187          */
12188         'changed': true
12189      });
12190 };
12191
12192 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
12193     
12194     active: false,
12195     html: false,
12196     tabId: false,
12197     navId : false,
12198     
12199     getAutoCreate : function(){
12200         var cfg = {
12201             tag: 'div',
12202             cls: 'tab-pane',
12203             html: this.html || ''
12204         };
12205         
12206         if(this.active){
12207             cfg.cls += ' active';
12208         }
12209         
12210         if(this.tabId){
12211             cfg.tabId = this.tabId;
12212         }
12213         
12214         return cfg;
12215     },
12216     onRender : function(ct, position)
12217     {
12218        // Roo.log("Call onRender: " + this.xtype);
12219         
12220         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
12221         
12222         if (this.navId && this.tabId) {
12223             var item = Roo.bootstrap.NavGroup.get(this.navId).getNavItem(this.tabId);
12224             if (!item) {
12225                 Roo.log("could not find navID:"  + this.navId + ", tabId: " + this.tabId);
12226             } else {
12227                 item.on('changed', function(item, state) {
12228                     this.setActive(state);
12229                 }, this);
12230             }
12231         }
12232         
12233     },
12234     setActive: function(state)
12235     {
12236         Roo.log("panel - set active " + this.tabId + "=" + state);
12237         
12238         this.active = state;
12239         if (!state) {
12240             this.el.removeClass('active');
12241             
12242         } else  if (!this.el.hasClass('active')) {
12243             this.el.addClass('active');
12244         }
12245         this.fireEvent('changed', this, state);
12246     }
12247     
12248     
12249 });
12250  
12251
12252  
12253
12254  /*
12255  * - LGPL
12256  *
12257  * DateField
12258  * 
12259  */
12260
12261 /**
12262  * @class Roo.bootstrap.DateField
12263  * @extends Roo.bootstrap.Input
12264  * Bootstrap DateField class
12265  * @cfg {Number} weekStart default 0
12266  * @cfg {Number} weekStart default 0
12267  * @cfg {Number} viewMode default empty, (months|years)
12268  * @cfg {Number} minViewMode default empty, (months|years)
12269  * @cfg {Number} startDate default -Infinity
12270  * @cfg {Number} endDate default Infinity
12271  * @cfg {Boolean} todayHighlight default false
12272  * @cfg {Boolean} todayBtn default false
12273  * @cfg {Boolean} calendarWeeks default false
12274  * @cfg {Object} daysOfWeekDisabled default empty
12275  * 
12276  * @cfg {Boolean} keyboardNavigation default true
12277  * @cfg {String} language default en
12278  * 
12279  * @constructor
12280  * Create a new DateField
12281  * @param {Object} config The config object
12282  */
12283
12284 Roo.bootstrap.DateField = function(config){
12285     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
12286      this.addEvents({
12287             /**
12288              * @event show
12289              * Fires when this field show.
12290              * @param {Roo.bootstrap.DateField} this
12291              * @param {Mixed} date The date value
12292              */
12293             show : true,
12294             /**
12295              * @event show
12296              * Fires when this field hide.
12297              * @param {Roo.bootstrap.DateField} this
12298              * @param {Mixed} date The date value
12299              */
12300             hide : true,
12301             /**
12302              * @event select
12303              * Fires when select a date.
12304              * @param {Roo.bootstrap.DateField} this
12305              * @param {Mixed} date The date value
12306              */
12307             select : true
12308         });
12309 };
12310
12311 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
12312     
12313     /**
12314      * @cfg {String} format
12315      * The default date format string which can be overriden for localization support.  The format must be
12316      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
12317      */
12318     format : "m/d/y",
12319     /**
12320      * @cfg {String} altFormats
12321      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
12322      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
12323      */
12324     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
12325     
12326     weekStart : 0,
12327     
12328     viewMode : '',
12329     
12330     minViewMode : '',
12331     
12332     todayHighlight : false,
12333     
12334     todayBtn: false,
12335     
12336     language: 'en',
12337     
12338     keyboardNavigation: true,
12339     
12340     calendarWeeks: false,
12341     
12342     startDate: -Infinity,
12343     
12344     endDate: Infinity,
12345     
12346     daysOfWeekDisabled: [],
12347     
12348     _events: [],
12349     
12350     UTCDate: function()
12351     {
12352         return new Date(Date.UTC.apply(Date, arguments));
12353     },
12354     
12355     UTCToday: function()
12356     {
12357         var today = new Date();
12358         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
12359     },
12360     
12361     getDate: function() {
12362             var d = this.getUTCDate();
12363             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
12364     },
12365     
12366     getUTCDate: function() {
12367             return this.date;
12368     },
12369     
12370     setDate: function(d) {
12371             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
12372     },
12373     
12374     setUTCDate: function(d) {
12375             this.date = d;
12376             this.setValue(this.formatDate(this.date));
12377     },
12378         
12379     onRender: function(ct, position)
12380     {
12381         
12382         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
12383         
12384         this.language = this.language || 'en';
12385         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
12386         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
12387         
12388         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
12389         this.format = this.format || 'm/d/y';
12390         this.isInline = false;
12391         this.isInput = true;
12392         this.component = this.el.select('.add-on', true).first() || false;
12393         this.component = (this.component && this.component.length === 0) ? false : this.component;
12394         this.hasInput = this.component && this.inputEL().length;
12395         
12396         if (typeof(this.minViewMode === 'string')) {
12397             switch (this.minViewMode) {
12398                 case 'months':
12399                     this.minViewMode = 1;
12400                     break;
12401                 case 'years':
12402                     this.minViewMode = 2;
12403                     break;
12404                 default:
12405                     this.minViewMode = 0;
12406                     break;
12407             }
12408         }
12409         
12410         if (typeof(this.viewMode === 'string')) {
12411             switch (this.viewMode) {
12412                 case 'months':
12413                     this.viewMode = 1;
12414                     break;
12415                 case 'years':
12416                     this.viewMode = 2;
12417                     break;
12418                 default:
12419                     this.viewMode = 0;
12420                     break;
12421             }
12422         }
12423                 
12424         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
12425         
12426         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
12427         
12428         this.picker().on('mousedown', this.onMousedown, this);
12429         this.picker().on('click', this.onClick, this);
12430         
12431         this.picker().addClass('datepicker-dropdown');
12432         
12433         this.startViewMode = this.viewMode;
12434         
12435         
12436         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
12437             if(!this.calendarWeeks){
12438                 v.remove();
12439                 return;
12440             };
12441             
12442             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
12443             v.attr('colspan', function(i, val){
12444                 return parseInt(val) + 1;
12445             });
12446         })
12447                         
12448         
12449         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
12450         
12451         this.setStartDate(this.startDate);
12452         this.setEndDate(this.endDate);
12453         
12454         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
12455         
12456         this.fillDow();
12457         this.fillMonths();
12458         this.update();
12459         this.showMode();
12460         
12461         if(this.isInline) {
12462             this.show();
12463         }
12464     },
12465     
12466     picker : function()
12467     {
12468         return this.el.select('.datepicker', true).first();
12469     },
12470     
12471     fillDow: function()
12472     {
12473         var dowCnt = this.weekStart;
12474         
12475         var dow = {
12476             tag: 'tr',
12477             cn: [
12478                 
12479             ]
12480         };
12481         
12482         if(this.calendarWeeks){
12483             dow.cn.push({
12484                 tag: 'th',
12485                 cls: 'cw',
12486                 html: '&nbsp;'
12487             })
12488         }
12489         
12490         while (dowCnt < this.weekStart + 7) {
12491             dow.cn.push({
12492                 tag: 'th',
12493                 cls: 'dow',
12494                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
12495             });
12496         }
12497         
12498         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
12499     },
12500     
12501     fillMonths: function()
12502     {    
12503         var i = 0
12504         var months = this.picker().select('>.datepicker-months td', true).first();
12505         
12506         months.dom.innerHTML = '';
12507         
12508         while (i < 12) {
12509             var month = {
12510                 tag: 'span',
12511                 cls: 'month',
12512                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
12513             }
12514             
12515             months.createChild(month);
12516         }
12517         
12518     },
12519     
12520     update: function(){
12521         
12522         this.date = (typeof(this.date) === 'undefined') ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
12523         
12524         if (this.date < this.startDate) {
12525             this.viewDate = new Date(this.startDate);
12526         } else if (this.date > this.endDate) {
12527             this.viewDate = new Date(this.endDate);
12528         } else {
12529             this.viewDate = new Date(this.date);
12530         }
12531         
12532         this.fill();
12533     },
12534     
12535     fill: function() {
12536         var d = new Date(this.viewDate),
12537                 year = d.getUTCFullYear(),
12538                 month = d.getUTCMonth(),
12539                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
12540                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
12541                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
12542                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
12543                 currentDate = this.date && this.date.valueOf(),
12544                 today = this.UTCToday();
12545         
12546         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
12547         
12548 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
12549         
12550 //        this.picker.select('>tfoot th.today').
12551 //                                              .text(dates[this.language].today)
12552 //                                              .toggle(this.todayBtn !== false);
12553     
12554         this.updateNavArrows();
12555         this.fillMonths();
12556                                                 
12557         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
12558         
12559         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
12560          
12561         prevMonth.setUTCDate(day);
12562         
12563         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
12564         
12565         var nextMonth = new Date(prevMonth);
12566         
12567         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
12568         
12569         nextMonth = nextMonth.valueOf();
12570         
12571         var fillMonths = false;
12572         
12573         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
12574         
12575         while(prevMonth.valueOf() < nextMonth) {
12576             var clsName = '';
12577             
12578             if (prevMonth.getUTCDay() === this.weekStart) {
12579                 if(fillMonths){
12580                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
12581                 }
12582                     
12583                 fillMonths = {
12584                     tag: 'tr',
12585                     cn: []
12586                 };
12587                 
12588                 if(this.calendarWeeks){
12589                     // ISO 8601: First week contains first thursday.
12590                     // ISO also states week starts on Monday, but we can be more abstract here.
12591                     var
12592                     // Start of current week: based on weekstart/current date
12593                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
12594                     // Thursday of this week
12595                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
12596                     // First Thursday of year, year from thursday
12597                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
12598                     // Calendar week: ms between thursdays, div ms per day, div 7 days
12599                     calWeek =  (th - yth) / 864e5 / 7 + 1;
12600                     
12601                     fillMonths.cn.push({
12602                         tag: 'td',
12603                         cls: 'cw',
12604                         html: calWeek
12605                     });
12606                 }
12607             }
12608             
12609             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
12610                 clsName += ' old';
12611             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
12612                 clsName += ' new';
12613             }
12614             if (this.todayHighlight &&
12615                 prevMonth.getUTCFullYear() == today.getFullYear() &&
12616                 prevMonth.getUTCMonth() == today.getMonth() &&
12617                 prevMonth.getUTCDate() == today.getDate()) {
12618                 clsName += ' today';
12619             }
12620             
12621             if (currentDate && prevMonth.valueOf() === currentDate) {
12622                 clsName += ' active';
12623             }
12624             
12625             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
12626                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
12627                     clsName += ' disabled';
12628             }
12629             
12630             fillMonths.cn.push({
12631                 tag: 'td',
12632                 cls: 'day ' + clsName,
12633                 html: prevMonth.getDate()
12634             })
12635             
12636             prevMonth.setDate(prevMonth.getDate()+1);
12637         }
12638           
12639         var currentYear = this.date && this.date.getUTCFullYear();
12640         var currentMonth = this.date && this.date.getUTCMonth();
12641         
12642         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
12643         
12644         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
12645             v.removeClass('active');
12646             
12647             if(currentYear === year && k === currentMonth){
12648                 v.addClass('active');
12649             }
12650             
12651             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
12652                 v.addClass('disabled');
12653             }
12654             
12655         });
12656         
12657         
12658         year = parseInt(year/10, 10) * 10;
12659         
12660         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
12661         
12662         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
12663         
12664         year -= 1;
12665         for (var i = -1; i < 11; i++) {
12666             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
12667                 tag: 'span',
12668                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
12669                 html: year
12670             })
12671             
12672             year += 1;
12673         }
12674     },
12675     
12676     showMode: function(dir) {
12677         if (dir) {
12678             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
12679         }
12680         Roo.each(this.picker().select('>div',true).elements, function(v){
12681             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
12682             v.hide();
12683         });
12684         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
12685     },
12686     
12687     place: function()
12688     {
12689         if(this.isInline) return;
12690         
12691         this.picker().removeClass(['bottom', 'top']);
12692         
12693         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
12694             /*
12695              * place to the top of element!
12696              *
12697              */
12698             
12699             this.picker().addClass('top');
12700             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12701             
12702             return;
12703         }
12704         
12705         this.picker().addClass('bottom');
12706         
12707         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12708     },
12709     
12710     parseDate : function(value){
12711         if(!value || value instanceof Date){
12712             return value;
12713         }
12714         var v = Date.parseDate(value, this.format);
12715         if (!v && this.useIso) {
12716             v = Date.parseDate(value, 'Y-m-d');
12717         }
12718         if(!v && this.altFormats){
12719             if(!this.altFormatsArray){
12720                 this.altFormatsArray = this.altFormats.split("|");
12721             }
12722             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
12723                 v = Date.parseDate(value, this.altFormatsArray[i]);
12724             }
12725         }
12726         return v;
12727     },
12728     
12729     formatDate : function(date, fmt){
12730         return (!date || !(date instanceof Date)) ?
12731         date : date.dateFormat(fmt || this.format);
12732     },
12733     
12734     onFocus : function()
12735     {
12736         Roo.bootstrap.DateField.superclass.onFocus.call(this);
12737         this.show();
12738     },
12739     
12740     onBlur : function()
12741     {
12742         Roo.bootstrap.DateField.superclass.onBlur.call(this);
12743         this.hide();
12744     },
12745     
12746     show : function()
12747     {
12748         this.picker().show();
12749         this.update();
12750         this.place();
12751         
12752         this.fireEvent('show', this, this.date);
12753     },
12754     
12755     hide : function()
12756     {
12757         if(this.isInline) return;
12758         this.picker().hide();
12759         this.viewMode = this.startViewMode;
12760         this.showMode();
12761         
12762         this.fireEvent('hide', this, this.date);
12763         
12764     },
12765     
12766     onMousedown: function(e){
12767         e.stopPropagation();
12768         e.preventDefault();
12769     },
12770     
12771     keyup: function(e){
12772         Roo.bootstrap.DateField.superclass.keyup.call(this);
12773         this.update();
12774         
12775     },
12776
12777     setValue: function(v){
12778         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
12779         
12780         this.fireEvent('select', this, this.date);
12781         
12782     },
12783     
12784     fireKey: function(e){
12785         if (!this.picker().isVisible()){
12786             if (e.keyCode == 27) // allow escape to hide and re-show picker
12787                 this.show();
12788             return;
12789         }
12790         var dateChanged = false,
12791         dir, day, month,
12792         newDate, newViewDate;
12793         switch(e.keyCode){
12794             case 27: // escape
12795                 this.hide();
12796                 e.preventDefault();
12797                 break;
12798             case 37: // left
12799             case 39: // right
12800                 if (!this.keyboardNavigation) break;
12801                 dir = e.keyCode == 37 ? -1 : 1;
12802                 
12803                 if (e.ctrlKey){
12804                     newDate = this.moveYear(this.date, dir);
12805                     newViewDate = this.moveYear(this.viewDate, dir);
12806                 } else if (e.shiftKey){
12807                     newDate = this.moveMonth(this.date, dir);
12808                     newViewDate = this.moveMonth(this.viewDate, dir);
12809                 } else {
12810                     newDate = new Date(this.date);
12811                     newDate.setUTCDate(this.date.getUTCDate() + dir);
12812                     newViewDate = new Date(this.viewDate);
12813                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
12814                 }
12815                 if (this.dateWithinRange(newDate)){
12816                     this.date = newDate;
12817                     this.viewDate = newViewDate;
12818                     this.setValue(this.formatDate(this.date));
12819                     this.update();
12820                     e.preventDefault();
12821                     dateChanged = true;
12822                 }
12823                 break;
12824             case 38: // up
12825             case 40: // down
12826                 if (!this.keyboardNavigation) break;
12827                 dir = e.keyCode == 38 ? -1 : 1;
12828                 if (e.ctrlKey){
12829                     newDate = this.moveYear(this.date, dir);
12830                     newViewDate = this.moveYear(this.viewDate, dir);
12831                 } else if (e.shiftKey){
12832                     newDate = this.moveMonth(this.date, dir);
12833                     newViewDate = this.moveMonth(this.viewDate, dir);
12834                 } else {
12835                     newDate = new Date(this.date);
12836                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
12837                     newViewDate = new Date(this.viewDate);
12838                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
12839                 }
12840                 if (this.dateWithinRange(newDate)){
12841                     this.date = newDate;
12842                     this.viewDate = newViewDate;
12843                     this.setValue(this.formatDate(this.date));
12844                     this.update();
12845                     e.preventDefault();
12846                     dateChanged = true;
12847                 }
12848                 break;
12849             case 13: // enter
12850                 this.setValue(this.formatDate(this.date));
12851                 this.hide();
12852                 e.preventDefault();
12853                 break;
12854             case 9: // tab
12855                 this.setValue(this.formatDate(this.date));
12856                 this.hide();
12857                 break;
12858         }
12859     },
12860     
12861     
12862     onClick: function(e) {
12863         e.stopPropagation();
12864         e.preventDefault();
12865         
12866         var target = e.getTarget();
12867         
12868         if(target.nodeName.toLowerCase() === 'i'){
12869             target = Roo.get(target).dom.parentNode;
12870         }
12871         
12872         var nodeName = target.nodeName;
12873         var className = target.className;
12874         var html = target.innerHTML;
12875         
12876         switch(nodeName.toLowerCase()) {
12877             case 'th':
12878                 switch(className) {
12879                     case 'switch':
12880                         this.showMode(1);
12881                         break;
12882                     case 'prev':
12883                     case 'next':
12884                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
12885                         switch(this.viewMode){
12886                                 case 0:
12887                                         this.viewDate = this.moveMonth(this.viewDate, dir);
12888                                         break;
12889                                 case 1:
12890                                 case 2:
12891                                         this.viewDate = this.moveYear(this.viewDate, dir);
12892                                         break;
12893                         }
12894                         this.fill();
12895                         break;
12896                     case 'today':
12897                         var date = new Date();
12898                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
12899                         this.fill()
12900                         this.setValue(this.formatDate(this.date));
12901                         this.hide();
12902                         break;
12903                 }
12904                 break;
12905             case 'span':
12906                 if (className.indexOf('disabled') === -1) {
12907                     this.viewDate.setUTCDate(1);
12908                     if (className.indexOf('month') !== -1) {
12909                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
12910                     } else {
12911                         var year = parseInt(html, 10) || 0;
12912                         this.viewDate.setUTCFullYear(year);
12913                         
12914                     }
12915                     this.showMode(-1);
12916                     this.fill();
12917                 }
12918                 break;
12919                 
12920             case 'td':
12921                 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
12922                     var day = parseInt(html, 10) || 1;
12923                     var year = this.viewDate.getUTCFullYear(),
12924                         month = this.viewDate.getUTCMonth();
12925
12926                     if (className.indexOf('old') !== -1) {
12927                         if(month === 0 ){
12928                             month = 11;
12929                             year -= 1;
12930                         }else{
12931                             month -= 1;
12932                         }
12933                     } else if (className.indexOf('new') !== -1) {
12934                         if (month == 11) {
12935                             month = 0;
12936                             year += 1;
12937                         } else {
12938                             month += 1;
12939                         }
12940                     }
12941                     this.date = this.UTCDate(year, month, day,0,0,0,0);
12942                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
12943                     this.fill();
12944                     this.setValue(this.formatDate(this.date));
12945                     this.hide();
12946                 }
12947                 break;
12948         }
12949     },
12950     
12951     setStartDate: function(startDate){
12952         this.startDate = startDate || -Infinity;
12953         if (this.startDate !== -Infinity) {
12954             this.startDate = this.parseDate(this.startDate);
12955         }
12956         this.update();
12957         this.updateNavArrows();
12958     },
12959
12960     setEndDate: function(endDate){
12961         this.endDate = endDate || Infinity;
12962         if (this.endDate !== Infinity) {
12963             this.endDate = this.parseDate(this.endDate);
12964         }
12965         this.update();
12966         this.updateNavArrows();
12967     },
12968     
12969     setDaysOfWeekDisabled: function(daysOfWeekDisabled){
12970         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
12971         if (typeof(this.daysOfWeekDisabled) !== 'object') {
12972             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
12973         }
12974         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
12975             return parseInt(d, 10);
12976         });
12977         this.update();
12978         this.updateNavArrows();
12979     },
12980     
12981     updateNavArrows: function() {
12982         var d = new Date(this.viewDate),
12983         year = d.getUTCFullYear(),
12984         month = d.getUTCMonth();
12985         
12986         Roo.each(this.picker().select('.prev', true).elements, function(v){
12987             v.show();
12988             switch (this.viewMode) {
12989                 case 0:
12990
12991                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
12992                         v.hide();
12993                     }
12994                     break;
12995                 case 1:
12996                 case 2:
12997                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
12998                         v.hide();
12999                     }
13000                     break;
13001             }
13002         });
13003         
13004         Roo.each(this.picker().select('.next', true).elements, function(v){
13005             v.show();
13006             switch (this.viewMode) {
13007                 case 0:
13008
13009                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
13010                         v.hide();
13011                     }
13012                     break;
13013                 case 1:
13014                 case 2:
13015                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
13016                         v.hide();
13017                     }
13018                     break;
13019             }
13020         })
13021     },
13022     
13023     moveMonth: function(date, dir){
13024         if (!dir) return date;
13025         var new_date = new Date(date.valueOf()),
13026         day = new_date.getUTCDate(),
13027         month = new_date.getUTCMonth(),
13028         mag = Math.abs(dir),
13029         new_month, test;
13030         dir = dir > 0 ? 1 : -1;
13031         if (mag == 1){
13032             test = dir == -1
13033             // If going back one month, make sure month is not current month
13034             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
13035             ? function(){
13036                 return new_date.getUTCMonth() == month;
13037             }
13038             // If going forward one month, make sure month is as expected
13039             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
13040             : function(){
13041                 return new_date.getUTCMonth() != new_month;
13042             };
13043             new_month = month + dir;
13044             new_date.setUTCMonth(new_month);
13045             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
13046             if (new_month < 0 || new_month > 11)
13047                 new_month = (new_month + 12) % 12;
13048         } else {
13049             // For magnitudes >1, move one month at a time...
13050             for (var i=0; i<mag; i++)
13051                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
13052                 new_date = this.moveMonth(new_date, dir);
13053             // ...then reset the day, keeping it in the new month
13054             new_month = new_date.getUTCMonth();
13055             new_date.setUTCDate(day);
13056             test = function(){
13057                 return new_month != new_date.getUTCMonth();
13058             };
13059         }
13060         // Common date-resetting loop -- if date is beyond end of month, make it
13061         // end of month
13062         while (test()){
13063             new_date.setUTCDate(--day);
13064             new_date.setUTCMonth(new_month);
13065         }
13066         return new_date;
13067     },
13068
13069     moveYear: function(date, dir){
13070         return this.moveMonth(date, dir*12);
13071     },
13072
13073     dateWithinRange: function(date){
13074         return date >= this.startDate && date <= this.endDate;
13075     },
13076
13077     
13078     remove: function() {
13079         this.picker().remove();
13080     }
13081    
13082 });
13083
13084 Roo.apply(Roo.bootstrap.DateField,  {
13085     
13086     head : {
13087         tag: 'thead',
13088         cn: [
13089         {
13090             tag: 'tr',
13091             cn: [
13092             {
13093                 tag: 'th',
13094                 cls: 'prev',
13095                 html: '<i class="icon-arrow-left"/>'
13096             },
13097             {
13098                 tag: 'th',
13099                 cls: 'switch',
13100                 colspan: '5'
13101             },
13102             {
13103                 tag: 'th',
13104                 cls: 'next',
13105                 html: '<i class="icon-arrow-right"/>'
13106             }
13107
13108             ]
13109         }
13110         ]
13111     },
13112     
13113     content : {
13114         tag: 'tbody',
13115         cn: [
13116         {
13117             tag: 'tr',
13118             cn: [
13119             {
13120                 tag: 'td',
13121                 colspan: '7'
13122             }
13123             ]
13124         }
13125         ]
13126     },
13127     
13128     footer : {
13129         tag: 'tfoot',
13130         cn: [
13131         {
13132             tag: 'tr',
13133             cn: [
13134             {
13135                 tag: 'th',
13136                 colspan: '7',
13137                 cls: 'today'
13138             }
13139                     
13140             ]
13141         }
13142         ]
13143     },
13144     
13145     dates:{
13146         en: {
13147             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
13148             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
13149             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
13150             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
13151             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
13152             today: "Today"
13153         }
13154     },
13155     
13156     modes: [
13157     {
13158         clsName: 'days',
13159         navFnc: 'Month',
13160         navStep: 1
13161     },
13162     {
13163         clsName: 'months',
13164         navFnc: 'FullYear',
13165         navStep: 1
13166     },
13167     {
13168         clsName: 'years',
13169         navFnc: 'FullYear',
13170         navStep: 10
13171     }]
13172 });
13173
13174 Roo.apply(Roo.bootstrap.DateField,  {
13175   
13176     template : {
13177         tag: 'div',
13178         cls: 'datepicker dropdown-menu',
13179         cn: [
13180         {
13181             tag: 'div',
13182             cls: 'datepicker-days',
13183             cn: [
13184             {
13185                 tag: 'table',
13186                 cls: 'table-condensed',
13187                 cn:[
13188                 Roo.bootstrap.DateField.head,
13189                 {
13190                     tag: 'tbody'
13191                 },
13192                 Roo.bootstrap.DateField.footer
13193                 ]
13194             }
13195             ]
13196         },
13197         {
13198             tag: 'div',
13199             cls: 'datepicker-months',
13200             cn: [
13201             {
13202                 tag: 'table',
13203                 cls: 'table-condensed',
13204                 cn:[
13205                 Roo.bootstrap.DateField.head,
13206                 Roo.bootstrap.DateField.content,
13207                 Roo.bootstrap.DateField.footer
13208                 ]
13209             }
13210             ]
13211         },
13212         {
13213             tag: 'div',
13214             cls: 'datepicker-years',
13215             cn: [
13216             {
13217                 tag: 'table',
13218                 cls: 'table-condensed',
13219                 cn:[
13220                 Roo.bootstrap.DateField.head,
13221                 Roo.bootstrap.DateField.content,
13222                 Roo.bootstrap.DateField.footer
13223                 ]
13224             }
13225             ]
13226         }
13227         ]
13228     }
13229 });
13230
13231  
13232
13233  /*
13234  * - LGPL
13235  *
13236  * TimeField
13237  * 
13238  */
13239
13240 /**
13241  * @class Roo.bootstrap.TimeField
13242  * @extends Roo.bootstrap.Input
13243  * Bootstrap DateField class
13244  * 
13245  * 
13246  * @constructor
13247  * Create a new TimeField
13248  * @param {Object} config The config object
13249  */
13250
13251 Roo.bootstrap.TimeField = function(config){
13252     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
13253     this.addEvents({
13254             /**
13255              * @event show
13256              * Fires when this field show.
13257              * @param {Roo.bootstrap.DateField} this
13258              * @param {Mixed} date The date value
13259              */
13260             show : true,
13261             /**
13262              * @event show
13263              * Fires when this field hide.
13264              * @param {Roo.bootstrap.DateField} this
13265              * @param {Mixed} date The date value
13266              */
13267             hide : true,
13268             /**
13269              * @event select
13270              * Fires when select a date.
13271              * @param {Roo.bootstrap.DateField} this
13272              * @param {Mixed} date The date value
13273              */
13274             select : true
13275         });
13276 };
13277
13278 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
13279     
13280     /**
13281      * @cfg {String} format
13282      * The default time format string which can be overriden for localization support.  The format must be
13283      * valid according to {@link Date#parseDate} (defaults to 'H:i').
13284      */
13285     format : "H:i",
13286        
13287     onRender: function(ct, position)
13288     {
13289         
13290         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
13291                 
13292         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
13293         
13294         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13295         
13296         this.pop = this.picker().select('>.datepicker-time',true).first();
13297         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block' 
13298         
13299         this.picker().on('mousedown', this.onMousedown, this);
13300         this.picker().on('click', this.onClick, this);
13301         
13302         this.picker().addClass('datepicker-dropdown');
13303     
13304         this.fillTime();
13305         this.update();
13306             
13307         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
13308         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
13309         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
13310         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
13311         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
13312         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
13313
13314     },
13315     
13316     fireKey: function(e){
13317         if (!this.picker().isVisible()){
13318             if (e.keyCode == 27) // allow escape to hide and re-show picker
13319                 this.show();
13320             return;
13321         }
13322
13323         e.preventDefault();
13324         
13325         switch(e.keyCode){
13326             case 27: // escape
13327                 this.hide();
13328                 break;
13329             case 37: // left
13330             case 39: // right
13331                 this.onTogglePeriod();
13332                 break;
13333             case 38: // up
13334                 this.onIncrementMinutes();
13335                 break;
13336             case 40: // down
13337                 this.onDecrementMinutes();
13338                 break;
13339             case 13: // enter
13340             case 9: // tab
13341                 this.setTime();
13342                 break;
13343         }
13344     },
13345     
13346     onClick: function(e) {
13347         e.stopPropagation();
13348         e.preventDefault();
13349     },
13350     
13351     picker : function()
13352     {
13353         return this.el.select('.datepicker', true).first();
13354     },
13355     
13356     fillTime: function()
13357     {    
13358         var time = this.pop.select('tbody', true).first();
13359         
13360         time.dom.innerHTML = '';
13361         
13362         time.createChild({
13363             tag: 'tr',
13364             cn: [
13365                 {
13366                     tag: 'td',
13367                     cn: [
13368                         {
13369                             tag: 'a',
13370                             href: '#',
13371                             cls: 'btn',
13372                             cn: [
13373                                 {
13374                                     tag: 'span',
13375                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
13376                                 }
13377                             ]
13378                         } 
13379                     ]
13380                 },
13381                 {
13382                     tag: 'td',
13383                     cls: 'separator'
13384                 },
13385                 {
13386                     tag: 'td',
13387                     cn: [
13388                         {
13389                             tag: 'a',
13390                             href: '#',
13391                             cls: 'btn',
13392                             cn: [
13393                                 {
13394                                     tag: 'span',
13395                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
13396                                 }
13397                             ]
13398                         }
13399                     ]
13400                 },
13401                 {
13402                     tag: 'td',
13403                     cls: 'separator'
13404                 }
13405             ]
13406         });
13407         
13408         time.createChild({
13409             tag: 'tr',
13410             cn: [
13411                 {
13412                     tag: 'td',
13413                     cn: [
13414                         {
13415                             tag: 'span',
13416                             cls: 'timepicker-hour',
13417                             html: '00'
13418                         }  
13419                     ]
13420                 },
13421                 {
13422                     tag: 'td',
13423                     cls: 'separator',
13424                     html: ':'
13425                 },
13426                 {
13427                     tag: 'td',
13428                     cn: [
13429                         {
13430                             tag: 'span',
13431                             cls: 'timepicker-minute',
13432                             html: '00'
13433                         }  
13434                     ]
13435                 },
13436                 {
13437                     tag: 'td',
13438                     cls: 'separator'
13439                 },
13440                 {
13441                     tag: 'td',
13442                     cn: [
13443                         {
13444                             tag: 'button',
13445                             type: 'button',
13446                             cls: 'btn btn-primary period',
13447                             html: 'AM'
13448                             
13449                         }
13450                     ]
13451                 }
13452             ]
13453         });
13454         
13455         time.createChild({
13456             tag: 'tr',
13457             cn: [
13458                 {
13459                     tag: 'td',
13460                     cn: [
13461                         {
13462                             tag: 'a',
13463                             href: '#',
13464                             cls: 'btn',
13465                             cn: [
13466                                 {
13467                                     tag: 'span',
13468                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
13469                                 }
13470                             ]
13471                         }
13472                     ]
13473                 },
13474                 {
13475                     tag: 'td',
13476                     cls: 'separator'
13477                 },
13478                 {
13479                     tag: 'td',
13480                     cn: [
13481                         {
13482                             tag: 'a',
13483                             href: '#',
13484                             cls: 'btn',
13485                             cn: [
13486                                 {
13487                                     tag: 'span',
13488                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
13489                                 }
13490                             ]
13491                         }
13492                     ]
13493                 },
13494                 {
13495                     tag: 'td',
13496                     cls: 'separator'
13497                 }
13498             ]
13499         });
13500         
13501     },
13502     
13503     update: function()
13504     {
13505         
13506         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
13507         
13508         this.fill();
13509     },
13510     
13511     fill: function() 
13512     {
13513         var hours = this.time.getHours();
13514         var minutes = this.time.getMinutes();
13515         var period = 'AM';
13516         
13517         if(hours > 11){
13518             period = 'PM';
13519         }
13520         
13521         if(hours == 0){
13522             hours = 12;
13523         }
13524         
13525         
13526         if(hours > 12){
13527             hours = hours - 12;
13528         }
13529         
13530         if(hours < 10){
13531             hours = '0' + hours;
13532         }
13533         
13534         if(minutes < 10){
13535             minutes = '0' + minutes;
13536         }
13537         
13538         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
13539         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
13540         this.pop.select('button', true).first().dom.innerHTML = period;
13541         
13542     },
13543     
13544     place: function()
13545     {   
13546         this.picker().removeClass(['bottom', 'top']);
13547         
13548         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
13549             /*
13550              * place to the top of element!
13551              *
13552              */
13553             
13554             this.picker().addClass('top');
13555             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
13556             
13557             return;
13558         }
13559         
13560         this.picker().addClass('bottom');
13561         
13562         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
13563     },
13564   
13565     onFocus : function()
13566     {
13567         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
13568         this.show();
13569     },
13570     
13571     onBlur : function()
13572     {
13573         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
13574         this.hide();
13575     },
13576     
13577     show : function()
13578     {
13579         this.picker().show();
13580         this.pop.show();
13581         this.update();
13582         this.place();
13583         
13584         this.fireEvent('show', this, this.date);
13585     },
13586     
13587     hide : function()
13588     {
13589         this.picker().hide();
13590         this.pop.hide();
13591         
13592         this.fireEvent('hide', this, this.date);
13593     },
13594     
13595     setTime : function()
13596     {
13597         this.hide();
13598         this.setValue(this.time.format(this.format));
13599         
13600         this.fireEvent('select', this, this.date);
13601         
13602         
13603     },
13604     
13605     onMousedown: function(e){
13606         e.stopPropagation();
13607         e.preventDefault();
13608     },
13609     
13610     onIncrementHours: function()
13611     {
13612         Roo.log('onIncrementHours');
13613         this.time = this.time.add(Date.HOUR, 1);
13614         this.update();
13615         
13616     },
13617     
13618     onDecrementHours: function()
13619     {
13620         Roo.log('onDecrementHours');
13621         this.time = this.time.add(Date.HOUR, -1);
13622         this.update();
13623     },
13624     
13625     onIncrementMinutes: function()
13626     {
13627         Roo.log('onIncrementMinutes');
13628         this.time = this.time.add(Date.MINUTE, 1);
13629         this.update();
13630     },
13631     
13632     onDecrementMinutes: function()
13633     {
13634         Roo.log('onDecrementMinutes');
13635         this.time = this.time.add(Date.MINUTE, -1);
13636         this.update();
13637     },
13638     
13639     onTogglePeriod: function()
13640     {
13641         Roo.log('onTogglePeriod');
13642         this.time = this.time.add(Date.HOUR, 12);
13643         this.update();
13644     }
13645     
13646    
13647 });
13648
13649 Roo.apply(Roo.bootstrap.TimeField,  {
13650     
13651     content : {
13652         tag: 'tbody',
13653         cn: [
13654             {
13655                 tag: 'tr',
13656                 cn: [
13657                 {
13658                     tag: 'td',
13659                     colspan: '7'
13660                 }
13661                 ]
13662             }
13663         ]
13664     },
13665     
13666     footer : {
13667         tag: 'tfoot',
13668         cn: [
13669             {
13670                 tag: 'tr',
13671                 cn: [
13672                 {
13673                     tag: 'th',
13674                     colspan: '7',
13675                     cls: '',
13676                     cn: [
13677                         {
13678                             tag: 'button',
13679                             cls: 'btn btn-info ok',
13680                             html: 'OK'
13681                         }
13682                     ]
13683                 }
13684
13685                 ]
13686             }
13687         ]
13688     }
13689 });
13690
13691 Roo.apply(Roo.bootstrap.TimeField,  {
13692   
13693     template : {
13694         tag: 'div',
13695         cls: 'datepicker dropdown-menu',
13696         cn: [
13697             {
13698                 tag: 'div',
13699                 cls: 'datepicker-time',
13700                 cn: [
13701                 {
13702                     tag: 'table',
13703                     cls: 'table-condensed',
13704                     cn:[
13705                     Roo.bootstrap.TimeField.content,
13706                     Roo.bootstrap.TimeField.footer
13707                     ]
13708                 }
13709                 ]
13710             }
13711         ]
13712     }
13713 });
13714
13715  
13716
13717  /*
13718  * - LGPL
13719  *
13720  * CheckBox
13721  * 
13722  */
13723
13724 /**
13725  * @class Roo.bootstrap.CheckBox
13726  * @extends Roo.bootstrap.Input
13727  * Bootstrap CheckBox class
13728  * 
13729  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
13730  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
13731  * @cfg {String} boxLabel The text that appears beside the checkbox
13732  * @cfg {Boolean} checked initnal the element
13733  * 
13734  * @constructor
13735  * Create a new CheckBox
13736  * @param {Object} config The config object
13737  */
13738
13739 Roo.bootstrap.CheckBox = function(config){
13740     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
13741    
13742         this.addEvents({
13743             /**
13744             * @event check
13745             * Fires when the element is checked or unchecked.
13746             * @param {Roo.bootstrap.CheckBox} this This input
13747             * @param {Boolean} checked The new checked value
13748             */
13749            check : true
13750         });
13751 };
13752
13753 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
13754     
13755     inputType: 'checkbox',
13756     inputValue: 1,
13757     valueOff: 0,
13758     boxLabel: false,
13759     checked: false,
13760     
13761     getAutoCreate : function()
13762     {
13763         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
13764         
13765         var id = Roo.id();
13766         
13767         var cfg = {};
13768         
13769         cfg.cls = 'form-group' //input-group
13770         
13771         var input =  {
13772             tag: 'input',
13773             id : id,
13774             type : this.inputType,
13775             value : (!this.checked) ? this.valueOff : this.inputValue,
13776             cls : 'form-box',
13777             placeholder : this.placeholder || ''
13778             
13779         };
13780         
13781         if (this.disabled) {
13782             input.disabled=true;
13783         }
13784         
13785         if(this.checked){
13786             input.checked = this.checked;
13787         }
13788         
13789         if (this.name) {
13790             input.name = this.name;
13791         }
13792         
13793         if (this.size) {
13794             input.cls += ' input-' + this.size;
13795         }
13796         
13797         var settings=this;
13798         ['xs','sm','md','lg'].map(function(size){
13799             if (settings[size]) {
13800                 cfg.cls += ' col-' + size + '-' + settings[size];
13801             }
13802         });
13803         
13804         var inputblock = input;
13805         
13806         if (this.before || this.after) {
13807             
13808             inputblock = {
13809                 cls : 'input-group',
13810                 cn :  [] 
13811             };
13812             if (this.before) {
13813                 inputblock.cn.push({
13814                     tag :'span',
13815                     cls : 'input-group-addon',
13816                     html : this.before
13817                 });
13818             }
13819             inputblock.cn.push(input);
13820             if (this.after) {
13821                 inputblock.cn.push({
13822                     tag :'span',
13823                     cls : 'input-group-addon',
13824                     html : this.after
13825                 });
13826             }
13827             
13828         };
13829         
13830         if (align ==='left' && this.fieldLabel.length) {
13831                 Roo.log("left and has label");
13832                 cfg.cn = [
13833                     
13834                     {
13835                         tag: 'label',
13836                         'for' :  id,
13837                         cls : 'control-label col-md-' + this.labelWidth,
13838                         html : this.fieldLabel
13839                         
13840                     },
13841                     {
13842                         cls : "col-md-" + (12 - this.labelWidth), 
13843                         cn: [
13844                             inputblock
13845                         ]
13846                     }
13847                     
13848                 ];
13849         } else if ( this.fieldLabel.length) {
13850                 Roo.log(" label");
13851                 cfg.cn = [
13852                    
13853                     {
13854                         tag: this.boxLabel ? 'span' : 'label',
13855                         'for': id,
13856                         cls: 'control-label box-input-label',
13857                         //cls : 'input-group-addon',
13858                         html : this.fieldLabel
13859                         
13860                     },
13861                     
13862                     inputblock
13863                     
13864                 ];
13865
13866         } else {
13867             
13868                    Roo.log(" no label && no align");
13869                 cfg.cn = [
13870                     
13871                         inputblock
13872                     
13873                 ];
13874                 
13875                 
13876         };
13877         
13878         if(this.boxLabel){
13879             cfg.cn.push({
13880                 tag: 'label',
13881                 'for': id,
13882                 cls: 'box-label',
13883                 html: this.boxLabel
13884             })
13885         }
13886         
13887         return cfg;
13888         
13889     },
13890     
13891     /**
13892      * return the real input element.
13893      */
13894     inputEl: function ()
13895     {
13896         return this.el.select('input.form-box',true).first();
13897     },
13898     
13899     label: function()
13900     {
13901         return this.el.select('label.control-label',true).first();
13902     },
13903     
13904     initEvents : function()
13905     {
13906 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
13907         
13908         this.inputEl().on('click', this.onClick,  this);
13909         
13910     },
13911     
13912     onClick : function()
13913     {   
13914         this.setChecked(!this.checked);
13915     },
13916     
13917     setChecked : function(state,suppressEvent)
13918     {
13919         this.checked = state;
13920         
13921         this.inputEl().dom.checked = state;
13922         
13923         if(suppressEvent !== true){
13924             this.fireEvent('check', this, state);
13925         }
13926         
13927         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
13928         
13929     },
13930     
13931     setValue : function(v,suppressEvent)
13932     {
13933         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
13934     }
13935     
13936 });
13937
13938  
13939 /*
13940  * - LGPL
13941  *
13942  * Radio
13943  * 
13944  */
13945
13946 /**
13947  * @class Roo.bootstrap.Radio
13948  * @extends Roo.bootstrap.CheckBox
13949  * Bootstrap Radio class
13950
13951  * @constructor
13952  * Create a new Radio
13953  * @param {Object} config The config object
13954  */
13955
13956 Roo.bootstrap.Radio = function(config){
13957     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
13958    
13959 };
13960
13961 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
13962     
13963     inputType: 'radio',
13964     inputValue: '',
13965     valueOff: '',
13966     
13967     getAutoCreate : function()
13968     {
13969         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
13970         
13971         var id = Roo.id();
13972         
13973         var cfg = {};
13974         
13975         cfg.cls = 'form-group' //input-group
13976         
13977         var input =  {
13978             tag: 'input',
13979             id : id,
13980             type : this.inputType,
13981             value : (!this.checked) ? this.valueOff : this.inputValue,
13982             cls : 'form-box',
13983             placeholder : this.placeholder || ''
13984             
13985         };
13986         
13987         if (this.disabled) {
13988             input.disabled=true;
13989         }
13990         
13991         if(this.checked){
13992             input.checked = this.checked;
13993         }
13994         
13995         if (this.name) {
13996             input.name = this.name;
13997         }
13998         
13999         if (this.size) {
14000             input.cls += ' input-' + this.size;
14001         }
14002         
14003         var settings=this;
14004         ['xs','sm','md','lg'].map(function(size){
14005             if (settings[size]) {
14006                 cfg.cls += ' col-' + size + '-' + settings[size];
14007             }
14008         });
14009         
14010         var inputblock = input;
14011         
14012         if (this.before || this.after) {
14013             
14014             inputblock = {
14015                 cls : 'input-group',
14016                 cn :  [] 
14017             };
14018             if (this.before) {
14019                 inputblock.cn.push({
14020                     tag :'span',
14021                     cls : 'input-group-addon',
14022                     html : this.before
14023                 });
14024             }
14025             inputblock.cn.push(input);
14026             if (this.after) {
14027                 inputblock.cn.push({
14028                     tag :'span',
14029                     cls : 'input-group-addon',
14030                     html : this.after
14031                 });
14032             }
14033             
14034         };
14035         
14036         if (align ==='left' && this.fieldLabel.length) {
14037                 Roo.log("left and has label");
14038                 cfg.cn = [
14039                     
14040                     {
14041                         tag: 'label',
14042                         'for' :  id,
14043                         cls : 'control-label col-md-' + this.labelWidth,
14044                         html : this.fieldLabel
14045                         
14046                     },
14047                     {
14048                         cls : "col-md-" + (12 - this.labelWidth), 
14049                         cn: [
14050                             inputblock
14051                         ]
14052                     }
14053                     
14054                 ];
14055         } else if ( this.fieldLabel.length) {
14056                 Roo.log(" label");
14057                  cfg.cn = [
14058                    
14059                     {
14060                         tag: 'label',
14061                         'for': id,
14062                         cls: 'control-label box-input-label',
14063                         //cls : 'input-group-addon',
14064                         html : this.fieldLabel
14065                         
14066                     },
14067                     
14068                     inputblock
14069                     
14070                 ];
14071
14072         } else {
14073             
14074                    Roo.log(" no label && no align");
14075                 cfg.cn = [
14076                     
14077                         inputblock
14078                     
14079                 ];
14080                 
14081                 
14082         };
14083         
14084         if(this.boxLabel){
14085             cfg.cn.push({
14086                 tag: 'label',
14087                 'for': id,
14088                 cls: 'box-label',
14089                 html: this.boxLabel
14090             })
14091         }
14092         
14093         return cfg;
14094         
14095     },
14096    
14097     onClick : function()
14098     {   
14099         this.setChecked(true);
14100     },
14101     
14102     setChecked : function(state,suppressEvent)
14103     {
14104         if(state){
14105             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
14106                 v.dom.checked = false;
14107             });
14108         }
14109         
14110         this.checked = state;
14111         this.inputEl().dom.checked = state;
14112         
14113         if(suppressEvent !== true){
14114             this.fireEvent('check', this, state);
14115         }
14116         
14117         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
14118         
14119     },
14120     
14121     getGroupValue : function()
14122     {
14123         var value = ''
14124         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
14125             if(v.dom.checked == true){
14126                 value = v.dom.value;
14127             }
14128         });
14129         
14130         return value;
14131     },
14132     
14133     /**
14134      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
14135      * @return {Mixed} value The field value
14136      */
14137     getValue : function(){
14138         return this.getGroupValue();
14139     }
14140     
14141 });
14142
14143  
14144 //<script type="text/javascript">
14145
14146 /*
14147  * Based  Ext JS Library 1.1.1
14148  * Copyright(c) 2006-2007, Ext JS, LLC.
14149  * LGPL
14150  *
14151  */
14152  
14153 /**
14154  * @class Roo.HtmlEditorCore
14155  * @extends Roo.Component
14156  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
14157  *
14158  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
14159  */
14160
14161 Roo.HtmlEditorCore = function(config){
14162     
14163     
14164     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
14165     this.addEvents({
14166         /**
14167          * @event initialize
14168          * Fires when the editor is fully initialized (including the iframe)
14169          * @param {Roo.HtmlEditorCore} this
14170          */
14171         initialize: true,
14172         /**
14173          * @event activate
14174          * Fires when the editor is first receives the focus. Any insertion must wait
14175          * until after this event.
14176          * @param {Roo.HtmlEditorCore} this
14177          */
14178         activate: true,
14179          /**
14180          * @event beforesync
14181          * Fires before the textarea is updated with content from the editor iframe. Return false
14182          * to cancel the sync.
14183          * @param {Roo.HtmlEditorCore} this
14184          * @param {String} html
14185          */
14186         beforesync: true,
14187          /**
14188          * @event beforepush
14189          * Fires before the iframe editor is updated with content from the textarea. Return false
14190          * to cancel the push.
14191          * @param {Roo.HtmlEditorCore} this
14192          * @param {String} html
14193          */
14194         beforepush: true,
14195          /**
14196          * @event sync
14197          * Fires when the textarea is updated with content from the editor iframe.
14198          * @param {Roo.HtmlEditorCore} this
14199          * @param {String} html
14200          */
14201         sync: true,
14202          /**
14203          * @event push
14204          * Fires when the iframe editor is updated with content from the textarea.
14205          * @param {Roo.HtmlEditorCore} this
14206          * @param {String} html
14207          */
14208         push: true,
14209         
14210         /**
14211          * @event editorevent
14212          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
14213          * @param {Roo.HtmlEditorCore} this
14214          */
14215         editorevent: true
14216     });
14217      
14218 };
14219
14220
14221 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
14222
14223
14224      /**
14225      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
14226      */
14227     
14228     owner : false,
14229     
14230      /**
14231      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
14232      *                        Roo.resizable.
14233      */
14234     resizable : false,
14235      /**
14236      * @cfg {Number} height (in pixels)
14237      */   
14238     height: 300,
14239    /**
14240      * @cfg {Number} width (in pixels)
14241      */   
14242     width: 500,
14243     
14244     /**
14245      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
14246      * 
14247      */
14248     stylesheets: false,
14249     
14250     // id of frame..
14251     frameId: false,
14252     
14253     // private properties
14254     validationEvent : false,
14255     deferHeight: true,
14256     initialized : false,
14257     activated : false,
14258     sourceEditMode : false,
14259     onFocus : Roo.emptyFn,
14260     iframePad:3,
14261     hideMode:'offsets',
14262     
14263     clearUp: true,
14264     
14265      
14266     
14267
14268     /**
14269      * Protected method that will not generally be called directly. It
14270      * is called when the editor initializes the iframe with HTML contents. Override this method if you
14271      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
14272      */
14273     getDocMarkup : function(){
14274         // body styles..
14275         var st = '';
14276         Roo.log(this.stylesheets);
14277         
14278         // inherit styels from page...?? 
14279         if (this.stylesheets === false) {
14280             
14281             Roo.get(document.head).select('style').each(function(node) {
14282                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
14283             });
14284             
14285             Roo.get(document.head).select('link').each(function(node) { 
14286                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
14287             });
14288             
14289         } else if (!this.stylesheets.length) {
14290                 // simple..
14291                 st = '<style type="text/css">' +
14292                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
14293                    '</style>';
14294         } else {
14295             Roo.each(this.stylesheets, function(s) {
14296                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
14297             });
14298             
14299         }
14300         
14301         st +=  '<style type="text/css">' +
14302             'IMG { cursor: pointer } ' +
14303         '</style>';
14304
14305         
14306         return '<html><head>' + st  +
14307             //<style type="text/css">' +
14308             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
14309             //'</style>' +
14310             ' </head><body class="roo-htmleditor-body"></body></html>';
14311     },
14312
14313     // private
14314     onRender : function(ct, position)
14315     {
14316         var _t = this;
14317         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
14318         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
14319         
14320         
14321         this.el.dom.style.border = '0 none';
14322         this.el.dom.setAttribute('tabIndex', -1);
14323         this.el.addClass('x-hidden hide');
14324         
14325         
14326         
14327         if(Roo.isIE){ // fix IE 1px bogus margin
14328             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
14329         }
14330        
14331         
14332         this.frameId = Roo.id();
14333         
14334          
14335         
14336         var iframe = this.owner.wrap.createChild({
14337             tag: 'iframe',
14338             cls: 'form-control', // bootstrap..
14339             id: this.frameId,
14340             name: this.frameId,
14341             frameBorder : 'no',
14342             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
14343         }, this.el
14344         );
14345         
14346         
14347         this.iframe = iframe.dom;
14348
14349          this.assignDocWin();
14350         
14351         this.doc.designMode = 'on';
14352        
14353         this.doc.open();
14354         this.doc.write(this.getDocMarkup());
14355         this.doc.close();
14356
14357         
14358         var task = { // must defer to wait for browser to be ready
14359             run : function(){
14360                 //console.log("run task?" + this.doc.readyState);
14361                 this.assignDocWin();
14362                 if(this.doc.body || this.doc.readyState == 'complete'){
14363                     try {
14364                         this.doc.designMode="on";
14365                     } catch (e) {
14366                         return;
14367                     }
14368                     Roo.TaskMgr.stop(task);
14369                     this.initEditor.defer(10, this);
14370                 }
14371             },
14372             interval : 10,
14373             duration: 10000,
14374             scope: this
14375         };
14376         Roo.TaskMgr.start(task);
14377
14378         
14379          
14380     },
14381
14382     // private
14383     onResize : function(w, h)
14384     {
14385          Roo.log('resize: ' +w + ',' + h );
14386         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
14387         if(!this.iframe){
14388             return;
14389         }
14390         if(typeof w == 'number'){
14391             
14392             this.iframe.style.width = w + 'px';
14393         }
14394         if(typeof h == 'number'){
14395             
14396             this.iframe.style.height = h + 'px';
14397             if(this.doc){
14398                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
14399             }
14400         }
14401         
14402     },
14403
14404     /**
14405      * Toggles the editor between standard and source edit mode.
14406      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
14407      */
14408     toggleSourceEdit : function(sourceEditMode){
14409         
14410         this.sourceEditMode = sourceEditMode === true;
14411         
14412         if(this.sourceEditMode){
14413  
14414             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
14415             
14416         }else{
14417             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
14418             //this.iframe.className = '';
14419             this.deferFocus();
14420         }
14421         //this.setSize(this.owner.wrap.getSize());
14422         //this.fireEvent('editmodechange', this, this.sourceEditMode);
14423     },
14424
14425     
14426   
14427
14428     /**
14429      * Protected method that will not generally be called directly. If you need/want
14430      * custom HTML cleanup, this is the method you should override.
14431      * @param {String} html The HTML to be cleaned
14432      * return {String} The cleaned HTML
14433      */
14434     cleanHtml : function(html){
14435         html = String(html);
14436         if(html.length > 5){
14437             if(Roo.isSafari){ // strip safari nonsense
14438                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
14439             }
14440         }
14441         if(html == '&nbsp;'){
14442             html = '';
14443         }
14444         return html;
14445     },
14446
14447     /**
14448      * HTML Editor -> Textarea
14449      * Protected method that will not generally be called directly. Syncs the contents
14450      * of the editor iframe with the textarea.
14451      */
14452     syncValue : function(){
14453         if(this.initialized){
14454             var bd = (this.doc.body || this.doc.documentElement);
14455             //this.cleanUpPaste(); -- this is done else where and causes havoc..
14456             var html = bd.innerHTML;
14457             if(Roo.isSafari){
14458                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
14459                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
14460                 if(m && m[1]){
14461                     html = '<div style="'+m[0]+'">' + html + '</div>';
14462                 }
14463             }
14464             html = this.cleanHtml(html);
14465             // fix up the special chars.. normaly like back quotes in word...
14466             // however we do not want to do this with chinese..
14467             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
14468                 var cc = b.charCodeAt();
14469                 if (
14470                     (cc >= 0x4E00 && cc < 0xA000 ) ||
14471                     (cc >= 0x3400 && cc < 0x4E00 ) ||
14472                     (cc >= 0xf900 && cc < 0xfb00 )
14473                 ) {
14474                         return b;
14475                 }
14476                 return "&#"+cc+";" 
14477             });
14478             if(this.owner.fireEvent('beforesync', this, html) !== false){
14479                 this.el.dom.value = html;
14480                 this.owner.fireEvent('sync', this, html);
14481             }
14482         }
14483     },
14484
14485     /**
14486      * Protected method that will not generally be called directly. Pushes the value of the textarea
14487      * into the iframe editor.
14488      */
14489     pushValue : function(){
14490         if(this.initialized){
14491             var v = this.el.dom.value.trim();
14492             
14493 //            if(v.length < 1){
14494 //                v = '&#160;';
14495 //            }
14496             
14497             if(this.owner.fireEvent('beforepush', this, v) !== false){
14498                 var d = (this.doc.body || this.doc.documentElement);
14499                 d.innerHTML = v;
14500                 this.cleanUpPaste();
14501                 this.el.dom.value = d.innerHTML;
14502                 this.owner.fireEvent('push', this, v);
14503             }
14504         }
14505     },
14506
14507     // private
14508     deferFocus : function(){
14509         this.focus.defer(10, this);
14510     },
14511
14512     // doc'ed in Field
14513     focus : function(){
14514         if(this.win && !this.sourceEditMode){
14515             this.win.focus();
14516         }else{
14517             this.el.focus();
14518         }
14519     },
14520     
14521     assignDocWin: function()
14522     {
14523         var iframe = this.iframe;
14524         
14525          if(Roo.isIE){
14526             this.doc = iframe.contentWindow.document;
14527             this.win = iframe.contentWindow;
14528         } else {
14529             if (!Roo.get(this.frameId)) {
14530                 return;
14531             }
14532             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
14533             this.win = Roo.get(this.frameId).dom.contentWindow;
14534         }
14535     },
14536     
14537     // private
14538     initEditor : function(){
14539         //console.log("INIT EDITOR");
14540         this.assignDocWin();
14541         
14542         
14543         
14544         this.doc.designMode="on";
14545         this.doc.open();
14546         this.doc.write(this.getDocMarkup());
14547         this.doc.close();
14548         
14549         var dbody = (this.doc.body || this.doc.documentElement);
14550         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
14551         // this copies styles from the containing element into thsi one..
14552         // not sure why we need all of this..
14553         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
14554         ss['background-attachment'] = 'fixed'; // w3c
14555         dbody.bgProperties = 'fixed'; // ie
14556         Roo.DomHelper.applyStyles(dbody, ss);
14557         Roo.EventManager.on(this.doc, {
14558             //'mousedown': this.onEditorEvent,
14559             'mouseup': this.onEditorEvent,
14560             'dblclick': this.onEditorEvent,
14561             'click': this.onEditorEvent,
14562             'keyup': this.onEditorEvent,
14563             buffer:100,
14564             scope: this
14565         });
14566         if(Roo.isGecko){
14567             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
14568         }
14569         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
14570             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
14571         }
14572         this.initialized = true;
14573
14574         this.owner.fireEvent('initialize', this);
14575         this.pushValue();
14576     },
14577
14578     // private
14579     onDestroy : function(){
14580         
14581         
14582         
14583         if(this.rendered){
14584             
14585             //for (var i =0; i < this.toolbars.length;i++) {
14586             //    // fixme - ask toolbars for heights?
14587             //    this.toolbars[i].onDestroy();
14588            // }
14589             
14590             //this.wrap.dom.innerHTML = '';
14591             //this.wrap.remove();
14592         }
14593     },
14594
14595     // private
14596     onFirstFocus : function(){
14597         
14598         this.assignDocWin();
14599         
14600         
14601         this.activated = true;
14602          
14603     
14604         if(Roo.isGecko){ // prevent silly gecko errors
14605             this.win.focus();
14606             var s = this.win.getSelection();
14607             if(!s.focusNode || s.focusNode.nodeType != 3){
14608                 var r = s.getRangeAt(0);
14609                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
14610                 r.collapse(true);
14611                 this.deferFocus();
14612             }
14613             try{
14614                 this.execCmd('useCSS', true);
14615                 this.execCmd('styleWithCSS', false);
14616             }catch(e){}
14617         }
14618         this.owner.fireEvent('activate', this);
14619     },
14620
14621     // private
14622     adjustFont: function(btn){
14623         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
14624         //if(Roo.isSafari){ // safari
14625         //    adjust *= 2;
14626        // }
14627         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
14628         if(Roo.isSafari){ // safari
14629             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
14630             v =  (v < 10) ? 10 : v;
14631             v =  (v > 48) ? 48 : v;
14632             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
14633             
14634         }
14635         
14636         
14637         v = Math.max(1, v+adjust);
14638         
14639         this.execCmd('FontSize', v  );
14640     },
14641
14642     onEditorEvent : function(e){
14643         this.owner.fireEvent('editorevent', this, e);
14644       //  this.updateToolbar();
14645         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
14646     },
14647
14648     insertTag : function(tg)
14649     {
14650         // could be a bit smarter... -> wrap the current selected tRoo..
14651         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
14652             
14653             range = this.createRange(this.getSelection());
14654             var wrappingNode = this.doc.createElement(tg.toLowerCase());
14655             wrappingNode.appendChild(range.extractContents());
14656             range.insertNode(wrappingNode);
14657
14658             return;
14659             
14660             
14661             
14662         }
14663         this.execCmd("formatblock",   tg);
14664         
14665     },
14666     
14667     insertText : function(txt)
14668     {
14669         
14670         
14671         var range = this.createRange();
14672         range.deleteContents();
14673                //alert(Sender.getAttribute('label'));
14674                
14675         range.insertNode(this.doc.createTextNode(txt));
14676     } ,
14677     
14678      
14679
14680     /**
14681      * Executes a Midas editor command on the editor document and performs necessary focus and
14682      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
14683      * @param {String} cmd The Midas command
14684      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
14685      */
14686     relayCmd : function(cmd, value){
14687         this.win.focus();
14688         this.execCmd(cmd, value);
14689         this.owner.fireEvent('editorevent', this);
14690         //this.updateToolbar();
14691         this.owner.deferFocus();
14692     },
14693
14694     /**
14695      * Executes a Midas editor command directly on the editor document.
14696      * For visual commands, you should use {@link #relayCmd} instead.
14697      * <b>This should only be called after the editor is initialized.</b>
14698      * @param {String} cmd The Midas command
14699      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
14700      */
14701     execCmd : function(cmd, value){
14702         this.doc.execCommand(cmd, false, value === undefined ? null : value);
14703         this.syncValue();
14704     },
14705  
14706  
14707    
14708     /**
14709      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
14710      * to insert tRoo.
14711      * @param {String} text | dom node.. 
14712      */
14713     insertAtCursor : function(text)
14714     {
14715         
14716         
14717         
14718         if(!this.activated){
14719             return;
14720         }
14721         /*
14722         if(Roo.isIE){
14723             this.win.focus();
14724             var r = this.doc.selection.createRange();
14725             if(r){
14726                 r.collapse(true);
14727                 r.pasteHTML(text);
14728                 this.syncValue();
14729                 this.deferFocus();
14730             
14731             }
14732             return;
14733         }
14734         */
14735         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
14736             this.win.focus();
14737             
14738             
14739             // from jquery ui (MIT licenced)
14740             var range, node;
14741             var win = this.win;
14742             
14743             if (win.getSelection && win.getSelection().getRangeAt) {
14744                 range = win.getSelection().getRangeAt(0);
14745                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
14746                 range.insertNode(node);
14747             } else if (win.document.selection && win.document.selection.createRange) {
14748                 // no firefox support
14749                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
14750                 win.document.selection.createRange().pasteHTML(txt);
14751             } else {
14752                 // no firefox support
14753                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
14754                 this.execCmd('InsertHTML', txt);
14755             } 
14756             
14757             this.syncValue();
14758             
14759             this.deferFocus();
14760         }
14761     },
14762  // private
14763     mozKeyPress : function(e){
14764         if(e.ctrlKey){
14765             var c = e.getCharCode(), cmd;
14766           
14767             if(c > 0){
14768                 c = String.fromCharCode(c).toLowerCase();
14769                 switch(c){
14770                     case 'b':
14771                         cmd = 'bold';
14772                         break;
14773                     case 'i':
14774                         cmd = 'italic';
14775                         break;
14776                     
14777                     case 'u':
14778                         cmd = 'underline';
14779                         break;
14780                     
14781                     case 'v':
14782                         this.cleanUpPaste.defer(100, this);
14783                         return;
14784                         
14785                 }
14786                 if(cmd){
14787                     this.win.focus();
14788                     this.execCmd(cmd);
14789                     this.deferFocus();
14790                     e.preventDefault();
14791                 }
14792                 
14793             }
14794         }
14795     },
14796
14797     // private
14798     fixKeys : function(){ // load time branching for fastest keydown performance
14799         if(Roo.isIE){
14800             return function(e){
14801                 var k = e.getKey(), r;
14802                 if(k == e.TAB){
14803                     e.stopEvent();
14804                     r = this.doc.selection.createRange();
14805                     if(r){
14806                         r.collapse(true);
14807                         r.pasteHTML('&#160;&#160;&#160;&#160;');
14808                         this.deferFocus();
14809                     }
14810                     return;
14811                 }
14812                 
14813                 if(k == e.ENTER){
14814                     r = this.doc.selection.createRange();
14815                     if(r){
14816                         var target = r.parentElement();
14817                         if(!target || target.tagName.toLowerCase() != 'li'){
14818                             e.stopEvent();
14819                             r.pasteHTML('<br />');
14820                             r.collapse(false);
14821                             r.select();
14822                         }
14823                     }
14824                 }
14825                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
14826                     this.cleanUpPaste.defer(100, this);
14827                     return;
14828                 }
14829                 
14830                 
14831             };
14832         }else if(Roo.isOpera){
14833             return function(e){
14834                 var k = e.getKey();
14835                 if(k == e.TAB){
14836                     e.stopEvent();
14837                     this.win.focus();
14838                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
14839                     this.deferFocus();
14840                 }
14841                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
14842                     this.cleanUpPaste.defer(100, this);
14843                     return;
14844                 }
14845                 
14846             };
14847         }else if(Roo.isSafari){
14848             return function(e){
14849                 var k = e.getKey();
14850                 
14851                 if(k == e.TAB){
14852                     e.stopEvent();
14853                     this.execCmd('InsertText','\t');
14854                     this.deferFocus();
14855                     return;
14856                 }
14857                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
14858                     this.cleanUpPaste.defer(100, this);
14859                     return;
14860                 }
14861                 
14862              };
14863         }
14864     }(),
14865     
14866     getAllAncestors: function()
14867     {
14868         var p = this.getSelectedNode();
14869         var a = [];
14870         if (!p) {
14871             a.push(p); // push blank onto stack..
14872             p = this.getParentElement();
14873         }
14874         
14875         
14876         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
14877             a.push(p);
14878             p = p.parentNode;
14879         }
14880         a.push(this.doc.body);
14881         return a;
14882     },
14883     lastSel : false,
14884     lastSelNode : false,
14885     
14886     
14887     getSelection : function() 
14888     {
14889         this.assignDocWin();
14890         return Roo.isIE ? this.doc.selection : this.win.getSelection();
14891     },
14892     
14893     getSelectedNode: function() 
14894     {
14895         // this may only work on Gecko!!!
14896         
14897         // should we cache this!!!!
14898         
14899         
14900         
14901          
14902         var range = this.createRange(this.getSelection()).cloneRange();
14903         
14904         if (Roo.isIE) {
14905             var parent = range.parentElement();
14906             while (true) {
14907                 var testRange = range.duplicate();
14908                 testRange.moveToElementText(parent);
14909                 if (testRange.inRange(range)) {
14910                     break;
14911                 }
14912                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
14913                     break;
14914                 }
14915                 parent = parent.parentElement;
14916             }
14917             return parent;
14918         }
14919         
14920         // is ancestor a text element.
14921         var ac =  range.commonAncestorContainer;
14922         if (ac.nodeType == 3) {
14923             ac = ac.parentNode;
14924         }
14925         
14926         var ar = ac.childNodes;
14927          
14928         var nodes = [];
14929         var other_nodes = [];
14930         var has_other_nodes = false;
14931         for (var i=0;i<ar.length;i++) {
14932             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
14933                 continue;
14934             }
14935             // fullly contained node.
14936             
14937             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
14938                 nodes.push(ar[i]);
14939                 continue;
14940             }
14941             
14942             // probably selected..
14943             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
14944                 other_nodes.push(ar[i]);
14945                 continue;
14946             }
14947             // outer..
14948             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
14949                 continue;
14950             }
14951             
14952             
14953             has_other_nodes = true;
14954         }
14955         if (!nodes.length && other_nodes.length) {
14956             nodes= other_nodes;
14957         }
14958         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
14959             return false;
14960         }
14961         
14962         return nodes[0];
14963     },
14964     createRange: function(sel)
14965     {
14966         // this has strange effects when using with 
14967         // top toolbar - not sure if it's a great idea.
14968         //this.editor.contentWindow.focus();
14969         if (typeof sel != "undefined") {
14970             try {
14971                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
14972             } catch(e) {
14973                 return this.doc.createRange();
14974             }
14975         } else {
14976             return this.doc.createRange();
14977         }
14978     },
14979     getParentElement: function()
14980     {
14981         
14982         this.assignDocWin();
14983         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
14984         
14985         var range = this.createRange(sel);
14986          
14987         try {
14988             var p = range.commonAncestorContainer;
14989             while (p.nodeType == 3) { // text node
14990                 p = p.parentNode;
14991             }
14992             return p;
14993         } catch (e) {
14994             return null;
14995         }
14996     
14997     },
14998     /***
14999      *
15000      * Range intersection.. the hard stuff...
15001      *  '-1' = before
15002      *  '0' = hits..
15003      *  '1' = after.
15004      *         [ -- selected range --- ]
15005      *   [fail]                        [fail]
15006      *
15007      *    basically..
15008      *      if end is before start or  hits it. fail.
15009      *      if start is after end or hits it fail.
15010      *
15011      *   if either hits (but other is outside. - then it's not 
15012      *   
15013      *    
15014      **/
15015     
15016     
15017     // @see http://www.thismuchiknow.co.uk/?p=64.
15018     rangeIntersectsNode : function(range, node)
15019     {
15020         var nodeRange = node.ownerDocument.createRange();
15021         try {
15022             nodeRange.selectNode(node);
15023         } catch (e) {
15024             nodeRange.selectNodeContents(node);
15025         }
15026     
15027         var rangeStartRange = range.cloneRange();
15028         rangeStartRange.collapse(true);
15029     
15030         var rangeEndRange = range.cloneRange();
15031         rangeEndRange.collapse(false);
15032     
15033         var nodeStartRange = nodeRange.cloneRange();
15034         nodeStartRange.collapse(true);
15035     
15036         var nodeEndRange = nodeRange.cloneRange();
15037         nodeEndRange.collapse(false);
15038     
15039         return rangeStartRange.compareBoundaryPoints(
15040                  Range.START_TO_START, nodeEndRange) == -1 &&
15041                rangeEndRange.compareBoundaryPoints(
15042                  Range.START_TO_START, nodeStartRange) == 1;
15043         
15044          
15045     },
15046     rangeCompareNode : function(range, node)
15047     {
15048         var nodeRange = node.ownerDocument.createRange();
15049         try {
15050             nodeRange.selectNode(node);
15051         } catch (e) {
15052             nodeRange.selectNodeContents(node);
15053         }
15054         
15055         
15056         range.collapse(true);
15057     
15058         nodeRange.collapse(true);
15059      
15060         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
15061         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
15062          
15063         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
15064         
15065         var nodeIsBefore   =  ss == 1;
15066         var nodeIsAfter    = ee == -1;
15067         
15068         if (nodeIsBefore && nodeIsAfter)
15069             return 0; // outer
15070         if (!nodeIsBefore && nodeIsAfter)
15071             return 1; //right trailed.
15072         
15073         if (nodeIsBefore && !nodeIsAfter)
15074             return 2;  // left trailed.
15075         // fully contined.
15076         return 3;
15077     },
15078
15079     // private? - in a new class?
15080     cleanUpPaste :  function()
15081     {
15082         // cleans up the whole document..
15083         Roo.log('cleanuppaste');
15084         
15085         this.cleanUpChildren(this.doc.body);
15086         var clean = this.cleanWordChars(this.doc.body.innerHTML);
15087         if (clean != this.doc.body.innerHTML) {
15088             this.doc.body.innerHTML = clean;
15089         }
15090         
15091     },
15092     
15093     cleanWordChars : function(input) {// change the chars to hex code
15094         var he = Roo.HtmlEditorCore;
15095         
15096         var output = input;
15097         Roo.each(he.swapCodes, function(sw) { 
15098             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
15099             
15100             output = output.replace(swapper, sw[1]);
15101         });
15102         
15103         return output;
15104     },
15105     
15106     
15107     cleanUpChildren : function (n)
15108     {
15109         if (!n.childNodes.length) {
15110             return;
15111         }
15112         for (var i = n.childNodes.length-1; i > -1 ; i--) {
15113            this.cleanUpChild(n.childNodes[i]);
15114         }
15115     },
15116     
15117     
15118         
15119     
15120     cleanUpChild : function (node)
15121     {
15122         var ed = this;
15123         //console.log(node);
15124         if (node.nodeName == "#text") {
15125             // clean up silly Windows -- stuff?
15126             return; 
15127         }
15128         if (node.nodeName == "#comment") {
15129             node.parentNode.removeChild(node);
15130             // clean up silly Windows -- stuff?
15131             return; 
15132         }
15133         
15134         if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1 && this.clearUp) {
15135             // remove node.
15136             node.parentNode.removeChild(node);
15137             return;
15138             
15139         }
15140         
15141         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
15142         
15143         // remove <a name=....> as rendering on yahoo mailer is borked with this.
15144         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
15145         
15146         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
15147         //    remove_keep_children = true;
15148         //}
15149         
15150         if (remove_keep_children) {
15151             this.cleanUpChildren(node);
15152             // inserts everything just before this node...
15153             while (node.childNodes.length) {
15154                 var cn = node.childNodes[0];
15155                 node.removeChild(cn);
15156                 node.parentNode.insertBefore(cn, node);
15157             }
15158             node.parentNode.removeChild(node);
15159             return;
15160         }
15161         
15162         if (!node.attributes || !node.attributes.length) {
15163             this.cleanUpChildren(node);
15164             return;
15165         }
15166         
15167         function cleanAttr(n,v)
15168         {
15169             
15170             if (v.match(/^\./) || v.match(/^\//)) {
15171                 return;
15172             }
15173             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
15174                 return;
15175             }
15176             if (v.match(/^#/)) {
15177                 return;
15178             }
15179 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
15180             node.removeAttribute(n);
15181             
15182         }
15183         
15184         function cleanStyle(n,v)
15185         {
15186             if (v.match(/expression/)) { //XSS?? should we even bother..
15187                 node.removeAttribute(n);
15188                 return;
15189             }
15190             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
15191             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
15192             
15193             
15194             var parts = v.split(/;/);
15195             var clean = [];
15196             
15197             Roo.each(parts, function(p) {
15198                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
15199                 if (!p.length) {
15200                     return true;
15201                 }
15202                 var l = p.split(':').shift().replace(/\s+/g,'');
15203                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
15204                 
15205                 if ( cblack.indexOf(l) > -1) {
15206 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
15207                     //node.removeAttribute(n);
15208                     return true;
15209                 }
15210                 //Roo.log()
15211                 // only allow 'c whitelisted system attributes'
15212                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
15213 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
15214                     //node.removeAttribute(n);
15215                     return true;
15216                 }
15217                 
15218                 
15219                  
15220                 
15221                 clean.push(p);
15222                 return true;
15223             });
15224             if (clean.length) { 
15225                 node.setAttribute(n, clean.join(';'));
15226             } else {
15227                 node.removeAttribute(n);
15228             }
15229             
15230         }
15231         
15232         
15233         for (var i = node.attributes.length-1; i > -1 ; i--) {
15234             var a = node.attributes[i];
15235             //console.log(a);
15236             
15237             if (a.name.toLowerCase().substr(0,2)=='on')  {
15238                 node.removeAttribute(a.name);
15239                 continue;
15240             }
15241             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
15242                 node.removeAttribute(a.name);
15243                 continue;
15244             }
15245             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
15246                 cleanAttr(a.name,a.value); // fixme..
15247                 continue;
15248             }
15249             if (a.name == 'style') {
15250                 cleanStyle(a.name,a.value);
15251                 continue;
15252             }
15253             /// clean up MS crap..
15254             // tecnically this should be a list of valid class'es..
15255             
15256             
15257             if (a.name == 'class') {
15258                 if (a.value.match(/^Mso/)) {
15259                     node.className = '';
15260                 }
15261                 
15262                 if (a.value.match(/body/)) {
15263                     node.className = '';
15264                 }
15265                 continue;
15266             }
15267             
15268             // style cleanup!?
15269             // class cleanup?
15270             
15271         }
15272         
15273         
15274         this.cleanUpChildren(node);
15275         
15276         
15277     }
15278     
15279     
15280     // hide stuff that is not compatible
15281     /**
15282      * @event blur
15283      * @hide
15284      */
15285     /**
15286      * @event change
15287      * @hide
15288      */
15289     /**
15290      * @event focus
15291      * @hide
15292      */
15293     /**
15294      * @event specialkey
15295      * @hide
15296      */
15297     /**
15298      * @cfg {String} fieldClass @hide
15299      */
15300     /**
15301      * @cfg {String} focusClass @hide
15302      */
15303     /**
15304      * @cfg {String} autoCreate @hide
15305      */
15306     /**
15307      * @cfg {String} inputType @hide
15308      */
15309     /**
15310      * @cfg {String} invalidClass @hide
15311      */
15312     /**
15313      * @cfg {String} invalidText @hide
15314      */
15315     /**
15316      * @cfg {String} msgFx @hide
15317      */
15318     /**
15319      * @cfg {String} validateOnBlur @hide
15320      */
15321 });
15322
15323 Roo.HtmlEditorCore.white = [
15324         'area', 'br', 'img', 'input', 'hr', 'wbr',
15325         
15326        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
15327        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
15328        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
15329        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
15330        'table',   'ul',         'xmp', 
15331        
15332        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
15333       'thead',   'tr', 
15334      
15335       'dir', 'menu', 'ol', 'ul', 'dl',
15336        
15337       'embed',  'object'
15338 ];
15339
15340
15341 Roo.HtmlEditorCore.black = [
15342     //    'embed',  'object', // enable - backend responsiblity to clean thiese
15343         'applet', // 
15344         'base',   'basefont', 'bgsound', 'blink',  'body', 
15345         'frame',  'frameset', 'head',    'html',   'ilayer', 
15346         'iframe', 'layer',  'link',     'meta',    'object',   
15347         'script', 'style' ,'title',  'xml' // clean later..
15348 ];
15349 Roo.HtmlEditorCore.clean = [
15350     'script', 'style', 'title', 'xml'
15351 ];
15352 Roo.HtmlEditorCore.remove = [
15353     'font'
15354 ];
15355 // attributes..
15356
15357 Roo.HtmlEditorCore.ablack = [
15358     'on'
15359 ];
15360     
15361 Roo.HtmlEditorCore.aclean = [ 
15362     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
15363 ];
15364
15365 // protocols..
15366 Roo.HtmlEditorCore.pwhite= [
15367         'http',  'https',  'mailto'
15368 ];
15369
15370 // white listed style attributes.
15371 Roo.HtmlEditorCore.cwhite= [
15372       //  'text-align', /// default is to allow most things..
15373       
15374          
15375 //        'font-size'//??
15376 ];
15377
15378 // black listed style attributes.
15379 Roo.HtmlEditorCore.cblack= [
15380       //  'font-size' -- this can be set by the project 
15381 ];
15382
15383
15384 Roo.HtmlEditorCore.swapCodes   =[ 
15385     [    8211, "--" ], 
15386     [    8212, "--" ], 
15387     [    8216,  "'" ],  
15388     [    8217, "'" ],  
15389     [    8220, '"' ],  
15390     [    8221, '"' ],  
15391     [    8226, "*" ],  
15392     [    8230, "..." ]
15393 ]; 
15394
15395     /*
15396  * - LGPL
15397  *
15398  * HtmlEditor
15399  * 
15400  */
15401
15402 /**
15403  * @class Roo.bootstrap.HtmlEditor
15404  * @extends Roo.bootstrap.TextArea
15405  * Bootstrap HtmlEditor class
15406
15407  * @constructor
15408  * Create a new HtmlEditor
15409  * @param {Object} config The config object
15410  */
15411
15412 Roo.bootstrap.HtmlEditor = function(config){
15413     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
15414     if (!this.toolbars) {
15415         this.toolbars = [];
15416     }
15417     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
15418     this.addEvents({
15419             /**
15420              * @event initialize
15421              * Fires when the editor is fully initialized (including the iframe)
15422              * @param {HtmlEditor} this
15423              */
15424             initialize: true,
15425             /**
15426              * @event activate
15427              * Fires when the editor is first receives the focus. Any insertion must wait
15428              * until after this event.
15429              * @param {HtmlEditor} this
15430              */
15431             activate: true,
15432              /**
15433              * @event beforesync
15434              * Fires before the textarea is updated with content from the editor iframe. Return false
15435              * to cancel the sync.
15436              * @param {HtmlEditor} this
15437              * @param {String} html
15438              */
15439             beforesync: true,
15440              /**
15441              * @event beforepush
15442              * Fires before the iframe editor is updated with content from the textarea. Return false
15443              * to cancel the push.
15444              * @param {HtmlEditor} this
15445              * @param {String} html
15446              */
15447             beforepush: true,
15448              /**
15449              * @event sync
15450              * Fires when the textarea is updated with content from the editor iframe.
15451              * @param {HtmlEditor} this
15452              * @param {String} html
15453              */
15454             sync: true,
15455              /**
15456              * @event push
15457              * Fires when the iframe editor is updated with content from the textarea.
15458              * @param {HtmlEditor} this
15459              * @param {String} html
15460              */
15461             push: true,
15462              /**
15463              * @event editmodechange
15464              * Fires when the editor switches edit modes
15465              * @param {HtmlEditor} this
15466              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
15467              */
15468             editmodechange: true,
15469             /**
15470              * @event editorevent
15471              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
15472              * @param {HtmlEditor} this
15473              */
15474             editorevent: true,
15475             /**
15476              * @event firstfocus
15477              * Fires when on first focus - needed by toolbars..
15478              * @param {HtmlEditor} this
15479              */
15480             firstfocus: true,
15481             /**
15482              * @event autosave
15483              * Auto save the htmlEditor value as a file into Events
15484              * @param {HtmlEditor} this
15485              */
15486             autosave: true,
15487             /**
15488              * @event savedpreview
15489              * preview the saved version of htmlEditor
15490              * @param {HtmlEditor} this
15491              */
15492             savedpreview: true
15493         });
15494 };
15495
15496
15497 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
15498     
15499     
15500       /**
15501      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
15502      */
15503     toolbars : false,
15504    
15505      /**
15506      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
15507      *                        Roo.resizable.
15508      */
15509     resizable : false,
15510      /**
15511      * @cfg {Number} height (in pixels)
15512      */   
15513     height: 300,
15514    /**
15515      * @cfg {Number} width (in pixels)
15516      */   
15517     width: false,
15518     
15519     /**
15520      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
15521      * 
15522      */
15523     stylesheets: false,
15524     
15525     // id of frame..
15526     frameId: false,
15527     
15528     // private properties
15529     validationEvent : false,
15530     deferHeight: true,
15531     initialized : false,
15532     activated : false,
15533     
15534     onFocus : Roo.emptyFn,
15535     iframePad:3,
15536     hideMode:'offsets',
15537     
15538     
15539     tbContainer : false,
15540     
15541     toolbarContainer :function() {
15542         return this.wrap.select('.x-html-editor-tb',true).first();
15543     },
15544
15545     /**
15546      * Protected method that will not generally be called directly. It
15547      * is called when the editor creates its toolbar. Override this method if you need to
15548      * add custom toolbar buttons.
15549      * @param {HtmlEditor} editor
15550      */
15551     createToolbar : function(){
15552         
15553         Roo.log("create toolbars");
15554         
15555         this.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard({editor: this} ) ];
15556         this.toolbars[0].render(this.toolbarContainer());
15557         
15558         return;
15559         
15560 //        if (!editor.toolbars || !editor.toolbars.length) {
15561 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
15562 //        }
15563 //        
15564 //        for (var i =0 ; i < editor.toolbars.length;i++) {
15565 //            editor.toolbars[i] = Roo.factory(
15566 //                    typeof(editor.toolbars[i]) == 'string' ?
15567 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
15568 //                Roo.bootstrap.HtmlEditor);
15569 //            editor.toolbars[i].init(editor);
15570 //        }
15571     },
15572
15573      
15574     // private
15575     onRender : function(ct, position)
15576     {
15577        // Roo.log("Call onRender: " + this.xtype);
15578         var _t = this;
15579         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
15580       
15581         this.wrap = this.inputEl().wrap({
15582             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
15583         });
15584         
15585         this.editorcore.onRender(ct, position);
15586          
15587         if (this.resizable) {
15588             this.resizeEl = new Roo.Resizable(this.wrap, {
15589                 pinned : true,
15590                 wrap: true,
15591                 dynamic : true,
15592                 minHeight : this.height,
15593                 height: this.height,
15594                 handles : this.resizable,
15595                 width: this.width,
15596                 listeners : {
15597                     resize : function(r, w, h) {
15598                         _t.onResize(w,h); // -something
15599                     }
15600                 }
15601             });
15602             
15603         }
15604         this.createToolbar(this);
15605        
15606         
15607         if(!this.width && this.resizable){
15608             this.setSize(this.wrap.getSize());
15609         }
15610         if (this.resizeEl) {
15611             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
15612             // should trigger onReize..
15613         }
15614         
15615     },
15616
15617     // private
15618     onResize : function(w, h)
15619     {
15620         Roo.log('resize: ' +w + ',' + h );
15621         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
15622         var ew = false;
15623         var eh = false;
15624         
15625         if(this.inputEl() ){
15626             if(typeof w == 'number'){
15627                 var aw = w - this.wrap.getFrameWidth('lr');
15628                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
15629                 ew = aw;
15630             }
15631             if(typeof h == 'number'){
15632                  var tbh = -11;  // fixme it needs to tool bar size!
15633                 for (var i =0; i < this.toolbars.length;i++) {
15634                     // fixme - ask toolbars for heights?
15635                     tbh += this.toolbars[i].el.getHeight();
15636                     //if (this.toolbars[i].footer) {
15637                     //    tbh += this.toolbars[i].footer.el.getHeight();
15638                     //}
15639                 }
15640               
15641                 
15642                 
15643                 
15644                 
15645                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
15646                 ah -= 5; // knock a few pixes off for look..
15647                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
15648                 var eh = ah;
15649             }
15650         }
15651         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
15652         this.editorcore.onResize(ew,eh);
15653         
15654     },
15655
15656     /**
15657      * Toggles the editor between standard and source edit mode.
15658      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
15659      */
15660     toggleSourceEdit : function(sourceEditMode)
15661     {
15662         this.editorcore.toggleSourceEdit(sourceEditMode);
15663         
15664         if(this.editorcore.sourceEditMode){
15665             Roo.log('editor - showing textarea');
15666             
15667 //            Roo.log('in');
15668 //            Roo.log(this.syncValue());
15669             this.syncValue();
15670             this.inputEl().removeClass('hide');
15671             this.inputEl().dom.removeAttribute('tabIndex');
15672             this.inputEl().focus();
15673         }else{
15674             Roo.log('editor - hiding textarea');
15675 //            Roo.log('out')
15676 //            Roo.log(this.pushValue()); 
15677             this.pushValue();
15678             
15679             this.inputEl().addClass('hide');
15680             this.inputEl().dom.setAttribute('tabIndex', -1);
15681             //this.deferFocus();
15682         }
15683          
15684         if(this.resizable){
15685             this.setSize(this.wrap.getSize());
15686         }
15687         
15688         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
15689     },
15690  
15691     // private (for BoxComponent)
15692     adjustSize : Roo.BoxComponent.prototype.adjustSize,
15693
15694     // private (for BoxComponent)
15695     getResizeEl : function(){
15696         return this.wrap;
15697     },
15698
15699     // private (for BoxComponent)
15700     getPositionEl : function(){
15701         return this.wrap;
15702     },
15703
15704     // private
15705     initEvents : function(){
15706         this.originalValue = this.getValue();
15707     },
15708
15709 //    /**
15710 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
15711 //     * @method
15712 //     */
15713 //    markInvalid : Roo.emptyFn,
15714 //    /**
15715 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
15716 //     * @method
15717 //     */
15718 //    clearInvalid : Roo.emptyFn,
15719
15720     setValue : function(v){
15721         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
15722         this.editorcore.pushValue();
15723     },
15724
15725      
15726     // private
15727     deferFocus : function(){
15728         this.focus.defer(10, this);
15729     },
15730
15731     // doc'ed in Field
15732     focus : function(){
15733         this.editorcore.focus();
15734         
15735     },
15736       
15737
15738     // private
15739     onDestroy : function(){
15740         
15741         
15742         
15743         if(this.rendered){
15744             
15745             for (var i =0; i < this.toolbars.length;i++) {
15746                 // fixme - ask toolbars for heights?
15747                 this.toolbars[i].onDestroy();
15748             }
15749             
15750             this.wrap.dom.innerHTML = '';
15751             this.wrap.remove();
15752         }
15753     },
15754
15755     // private
15756     onFirstFocus : function(){
15757         //Roo.log("onFirstFocus");
15758         this.editorcore.onFirstFocus();
15759          for (var i =0; i < this.toolbars.length;i++) {
15760             this.toolbars[i].onFirstFocus();
15761         }
15762         
15763     },
15764     
15765     // private
15766     syncValue : function()
15767     {   
15768         this.editorcore.syncValue();
15769     },
15770     
15771     pushValue : function()
15772     {   
15773         this.editorcore.pushValue();
15774     }
15775      
15776     
15777     // hide stuff that is not compatible
15778     /**
15779      * @event blur
15780      * @hide
15781      */
15782     /**
15783      * @event change
15784      * @hide
15785      */
15786     /**
15787      * @event focus
15788      * @hide
15789      */
15790     /**
15791      * @event specialkey
15792      * @hide
15793      */
15794     /**
15795      * @cfg {String} fieldClass @hide
15796      */
15797     /**
15798      * @cfg {String} focusClass @hide
15799      */
15800     /**
15801      * @cfg {String} autoCreate @hide
15802      */
15803     /**
15804      * @cfg {String} inputType @hide
15805      */
15806     /**
15807      * @cfg {String} invalidClass @hide
15808      */
15809     /**
15810      * @cfg {String} invalidText @hide
15811      */
15812     /**
15813      * @cfg {String} msgFx @hide
15814      */
15815     /**
15816      * @cfg {String} validateOnBlur @hide
15817      */
15818 });
15819  
15820     
15821    
15822    
15823    
15824       
15825
15826 /**
15827  * @class Roo.bootstrap.HtmlEditorToolbar1
15828  * Basic Toolbar
15829  * 
15830  * Usage:
15831  *
15832  new Roo.bootstrap.HtmlEditor({
15833     ....
15834     toolbars : [
15835         new Roo.bootstrap.HtmlEditorToolbar1({
15836             disable : { fonts: 1 , format: 1, ..., ... , ...],
15837             btns : [ .... ]
15838         })
15839     }
15840      
15841  * 
15842  * @cfg {Object} disable List of elements to disable..
15843  * @cfg {Array} btns List of additional buttons.
15844  * 
15845  * 
15846  * NEEDS Extra CSS? 
15847  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
15848  */
15849  
15850 Roo.bootstrap.HtmlEditor.ToolbarStandard = function(config)
15851 {
15852     
15853     Roo.apply(this, config);
15854     
15855     // default disabled, based on 'good practice'..
15856     this.disable = this.disable || {};
15857     Roo.applyIf(this.disable, {
15858         fontSize : true,
15859         colors : true,
15860         specialElements : true
15861     });
15862     Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.constructor.call(this, config);
15863     
15864     this.editor = config.editor;
15865     this.editorcore = config.editor.editorcore;
15866     
15867     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
15868     
15869     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
15870     // dont call parent... till later.
15871 }
15872 Roo.extend(Roo.bootstrap.HtmlEditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
15873     
15874     
15875     bar : true,
15876     
15877     editor : false,
15878     editorcore : false,
15879     
15880     
15881     formats : [
15882         "p" ,  
15883         "h1","h2","h3","h4","h5","h6", 
15884         "pre", "code", 
15885         "abbr", "acronym", "address", "cite", "samp", "var",
15886         'div','span'
15887     ],
15888     
15889     onRender : function(ct, position)
15890     {
15891        // Roo.log("Call onRender: " + this.xtype);
15892         
15893        Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
15894        Roo.log(this.el);
15895        this.el.dom.style.marginBottom = '0';
15896        var _this = this;
15897        var editorcore = this.editorcore;
15898        var editor= this.editor;
15899        
15900        var children = [];
15901        var btn = function(id,cmd , toggle, handler){
15902        
15903             var  event = toggle ? 'toggle' : 'click';
15904        
15905             var a = {
15906                 size : 'sm',
15907                 xtype: 'Button',
15908                 xns: Roo.bootstrap,
15909                 glyphicon : id,
15910                 cmd : id || cmd,
15911                 enableToggle:toggle !== false,
15912                 //html : 'submit'
15913                 pressed : toggle ? false : null,
15914                 listeners : {}
15915             }
15916             a.listeners[toggle ? 'toggle' : 'click'] = function() {
15917                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
15918             }
15919             children.push(a);
15920             return a;
15921        }
15922         
15923         var style = {
15924                 xtype: 'Button',
15925                 size : 'sm',
15926                 xns: Roo.bootstrap,
15927                 glyphicon : 'font',
15928                 //html : 'submit'
15929                 menu : {
15930                     xtype: 'Menu',
15931                     xns: Roo.bootstrap,
15932                     items:  []
15933                 }
15934         };
15935         Roo.each(this.formats, function(f) {
15936             style.menu.items.push({
15937                 xtype :'MenuItem',
15938                 xns: Roo.bootstrap,
15939                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
15940                 tagname : f,
15941                 listeners : {
15942                     click : function()
15943                     {
15944                         editorcore.insertTag(this.tagname);
15945                         editor.focus();
15946                     }
15947                 }
15948                 
15949             });
15950         });
15951          children.push(style);   
15952             
15953             
15954         btn('bold',false,true);
15955         btn('italic',false,true);
15956         btn('align-left', 'justifyleft',true);
15957         btn('align-center', 'justifycenter',true);
15958         btn('align-right' , 'justifyright',true);
15959         btn('link', false, false, function(btn) {
15960             //Roo.log("create link?");
15961             var url = prompt(this.createLinkText, this.defaultLinkValue);
15962             if(url && url != 'http:/'+'/'){
15963                 this.editorcore.relayCmd('createlink', url);
15964             }
15965         }),
15966         btn('list','insertunorderedlist',true);
15967         btn('pencil', false,true, function(btn){
15968                 Roo.log(this);
15969                 
15970                 this.toggleSourceEdit(btn.pressed);
15971         });
15972         /*
15973         var cog = {
15974                 xtype: 'Button',
15975                 size : 'sm',
15976                 xns: Roo.bootstrap,
15977                 glyphicon : 'cog',
15978                 //html : 'submit'
15979                 menu : {
15980                     xtype: 'Menu',
15981                     xns: Roo.bootstrap,
15982                     items:  []
15983                 }
15984         };
15985         
15986         cog.menu.items.push({
15987             xtype :'MenuItem',
15988             xns: Roo.bootstrap,
15989             html : Clean styles,
15990             tagname : f,
15991             listeners : {
15992                 click : function()
15993                 {
15994                     editorcore.insertTag(this.tagname);
15995                     editor.focus();
15996                 }
15997             }
15998             
15999         });
16000        */
16001         
16002          
16003        this.xtype = 'NavSimplebar';
16004         
16005         for(var i=0;i< children.length;i++) {
16006             
16007             this.buttons.add(this.addxtypeChild(children[i]));
16008             
16009         }
16010         
16011         editor.on('editorevent', this.updateToolbar, this);
16012     },
16013     onBtnClick : function(id)
16014     {
16015        this.editorcore.relayCmd(id);
16016        this.editorcore.focus();
16017     },
16018     
16019     /**
16020      * Protected method that will not generally be called directly. It triggers
16021      * a toolbar update by reading the markup state of the current selection in the editor.
16022      */
16023     updateToolbar: function(){
16024
16025         if(!this.editorcore.activated){
16026             this.editor.onFirstFocus(); // is this neeed?
16027             return;
16028         }
16029
16030         var btns = this.buttons; 
16031         var doc = this.editorcore.doc;
16032         btns.get('bold').setActive(doc.queryCommandState('bold'));
16033         btns.get('italic').setActive(doc.queryCommandState('italic'));
16034         //btns.get('underline').setActive(doc.queryCommandState('underline'));
16035         
16036         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
16037         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
16038         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
16039         
16040         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
16041         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
16042          /*
16043         
16044         var ans = this.editorcore.getAllAncestors();
16045         if (this.formatCombo) {
16046             
16047             
16048             var store = this.formatCombo.store;
16049             this.formatCombo.setValue("");
16050             for (var i =0; i < ans.length;i++) {
16051                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
16052                     // select it..
16053                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
16054                     break;
16055                 }
16056             }
16057         }
16058         
16059         
16060         
16061         // hides menus... - so this cant be on a menu...
16062         Roo.bootstrap.MenuMgr.hideAll();
16063         */
16064         Roo.bootstrap.MenuMgr.hideAll();
16065         //this.editorsyncValue();
16066     },
16067     onFirstFocus: function() {
16068         this.buttons.each(function(item){
16069            item.enable();
16070         });
16071     },
16072     toggleSourceEdit : function(sourceEditMode){
16073         
16074           
16075         if(sourceEditMode){
16076             Roo.log("disabling buttons");
16077            this.buttons.each( function(item){
16078                 if(item.cmd != 'pencil'){
16079                     item.disable();
16080                 }
16081             });
16082           
16083         }else{
16084             Roo.log("enabling buttons");
16085             if(this.editorcore.initialized){
16086                 this.buttons.each( function(item){
16087                     item.enable();
16088                 });
16089             }
16090             
16091         }
16092         Roo.log("calling toggole on editor");
16093         // tell the editor that it's been pressed..
16094         this.editor.toggleSourceEdit(sourceEditMode);
16095        
16096     }
16097 });
16098
16099
16100
16101
16102
16103 /**
16104  * @class Roo.bootstrap.Table.AbstractSelectionModel
16105  * @extends Roo.util.Observable
16106  * Abstract base class for grid SelectionModels.  It provides the interface that should be
16107  * implemented by descendant classes.  This class should not be directly instantiated.
16108  * @constructor
16109  */
16110 Roo.bootstrap.Table.AbstractSelectionModel = function(){
16111     this.locked = false;
16112     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
16113 };
16114
16115
16116 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
16117     /** @ignore Called by the grid automatically. Do not call directly. */
16118     init : function(grid){
16119         this.grid = grid;
16120         this.initEvents();
16121     },
16122
16123     /**
16124      * Locks the selections.
16125      */
16126     lock : function(){
16127         this.locked = true;
16128     },
16129
16130     /**
16131      * Unlocks the selections.
16132      */
16133     unlock : function(){
16134         this.locked = false;
16135     },
16136
16137     /**
16138      * Returns true if the selections are locked.
16139      * @return {Boolean}
16140      */
16141     isLocked : function(){
16142         return this.locked;
16143     }
16144 });
16145 /**
16146  * @class Roo.bootstrap.Table.ColumnModel
16147  * @extends Roo.util.Observable
16148  * This is the default implementation of a ColumnModel used by the bootstrap table. It defines
16149  * the columns in the table.
16150  
16151  * @constructor
16152  * @param {Object} config An Array of column config objects. See this class's
16153  * config objects for details.
16154 */
16155 Roo.bootstrap.Table.ColumnModel = function(config){
16156         /**
16157      * The config passed into the constructor
16158      */
16159     this.config = config;
16160     this.lookup = {};
16161
16162     // if no id, create one
16163     // if the column does not have a dataIndex mapping,
16164     // map it to the order it is in the config
16165     for(var i = 0, len = config.length; i < len; i++){
16166         var c = config[i];
16167         if(typeof c.dataIndex == "undefined"){
16168             c.dataIndex = i;
16169         }
16170         if(typeof c.renderer == "string"){
16171             c.renderer = Roo.util.Format[c.renderer];
16172         }
16173         if(typeof c.id == "undefined"){
16174             c.id = Roo.id();
16175         }
16176 //        if(c.editor && c.editor.xtype){
16177 //            c.editor  = Roo.factory(c.editor, Roo.grid);
16178 //        }
16179 //        if(c.editor && c.editor.isFormField){
16180 //            c.editor = new Roo.grid.GridEditor(c.editor);
16181 //        }
16182
16183         this.lookup[c.id] = c;
16184     }
16185
16186     /**
16187      * The width of columns which have no width specified (defaults to 100)
16188      * @type Number
16189      */
16190     this.defaultWidth = 100;
16191
16192     /**
16193      * Default sortable of columns which have no sortable specified (defaults to false)
16194      * @type Boolean
16195      */
16196     this.defaultSortable = false;
16197
16198     this.addEvents({
16199         /**
16200              * @event widthchange
16201              * Fires when the width of a column changes.
16202              * @param {ColumnModel} this
16203              * @param {Number} columnIndex The column index
16204              * @param {Number} newWidth The new width
16205              */
16206             "widthchange": true,
16207         /**
16208              * @event headerchange
16209              * Fires when the text of a header changes.
16210              * @param {ColumnModel} this
16211              * @param {Number} columnIndex The column index
16212              * @param {Number} newText The new header text
16213              */
16214             "headerchange": true,
16215         /**
16216              * @event hiddenchange
16217              * Fires when a column is hidden or "unhidden".
16218              * @param {ColumnModel} this
16219              * @param {Number} columnIndex The column index
16220              * @param {Boolean} hidden true if hidden, false otherwise
16221              */
16222             "hiddenchange": true,
16223             /**
16224          * @event columnmoved
16225          * Fires when a column is moved.
16226          * @param {ColumnModel} this
16227          * @param {Number} oldIndex
16228          * @param {Number} newIndex
16229          */
16230         "columnmoved" : true,
16231         /**
16232          * @event columlockchange
16233          * Fires when a column's locked state is changed
16234          * @param {ColumnModel} this
16235          * @param {Number} colIndex
16236          * @param {Boolean} locked true if locked
16237          */
16238         "columnlockchange" : true
16239     });
16240     Roo.bootstrap.Table.ColumnModel.superclass.constructor.call(this);
16241 };
16242 Roo.extend(Roo.bootstrap.Table.ColumnModel, Roo.util.Observable, {
16243     /**
16244      * @cfg {String} header The header text to display in the Grid view.
16245      */
16246     /**
16247      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
16248      * {@link Roo.data.Record} definition from which to draw the column's value. If not
16249      * specified, the column's index is used as an index into the Record's data Array.
16250      */
16251     /**
16252      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
16253      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
16254      */
16255     /**
16256      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
16257      * Defaults to the value of the {@link #defaultSortable} property.
16258      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
16259      */
16260     /**
16261      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
16262      */
16263     /**
16264      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
16265      */
16266     /**
16267      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
16268      */
16269     /**
16270      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
16271      */
16272     /**
16273      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
16274      * given the cell's data value. See {@link #setRenderer}. If not specified, the
16275      * default renderer uses the raw data value.
16276      */
16277     /**
16278      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
16279      */
16280
16281     /**
16282      * Returns the id of the column at the specified index.
16283      * @param {Number} index The column index
16284      * @return {String} the id
16285      */
16286     getColumnId : function(index){
16287         return this.config[index].id;
16288     },
16289
16290     /**
16291      * Returns the column for a specified id.
16292      * @param {String} id The column id
16293      * @return {Object} the column
16294      */
16295     getColumnById : function(id){
16296         return this.lookup[id];
16297     },
16298
16299     
16300     /**
16301      * Returns the column for a specified dataIndex.
16302      * @param {String} dataIndex The column dataIndex
16303      * @return {Object|Boolean} the column or false if not found
16304      */
16305     getColumnByDataIndex: function(dataIndex){
16306         var index = this.findColumnIndex(dataIndex);
16307         return index > -1 ? this.config[index] : false;
16308     },
16309     
16310     /**
16311      * Returns the index for a specified column id.
16312      * @param {String} id The column id
16313      * @return {Number} the index, or -1 if not found
16314      */
16315     getIndexById : function(id){
16316         for(var i = 0, len = this.config.length; i < len; i++){
16317             if(this.config[i].id == id){
16318                 return i;
16319             }
16320         }
16321         return -1;
16322     },
16323     
16324     /**
16325      * Returns the index for a specified column dataIndex.
16326      * @param {String} dataIndex The column dataIndex
16327      * @return {Number} the index, or -1 if not found
16328      */
16329     
16330     findColumnIndex : function(dataIndex){
16331         for(var i = 0, len = this.config.length; i < len; i++){
16332             if(this.config[i].dataIndex == dataIndex){
16333                 return i;
16334             }
16335         }
16336         return -1;
16337     },
16338     
16339     
16340     moveColumn : function(oldIndex, newIndex){
16341         var c = this.config[oldIndex];
16342         this.config.splice(oldIndex, 1);
16343         this.config.splice(newIndex, 0, c);
16344         this.dataMap = null;
16345         this.fireEvent("columnmoved", this, oldIndex, newIndex);
16346     },
16347
16348     isLocked : function(colIndex){
16349         return this.config[colIndex].locked === true;
16350     },
16351
16352     setLocked : function(colIndex, value, suppressEvent){
16353         if(this.isLocked(colIndex) == value){
16354             return;
16355         }
16356         this.config[colIndex].locked = value;
16357         if(!suppressEvent){
16358             this.fireEvent("columnlockchange", this, colIndex, value);
16359         }
16360     },
16361
16362     getTotalLockedWidth : function(){
16363         var totalWidth = 0;
16364         for(var i = 0; i < this.config.length; i++){
16365             if(this.isLocked(i) && !this.isHidden(i)){
16366                 this.totalWidth += this.getColumnWidth(i);
16367             }
16368         }
16369         return totalWidth;
16370     },
16371
16372     getLockedCount : function(){
16373         for(var i = 0, len = this.config.length; i < len; i++){
16374             if(!this.isLocked(i)){
16375                 return i;
16376             }
16377         }
16378     },
16379
16380     /**
16381      * Returns the number of columns.
16382      * @return {Number}
16383      */
16384     getColumnCount : function(visibleOnly){
16385         if(visibleOnly === true){
16386             var c = 0;
16387             for(var i = 0, len = this.config.length; i < len; i++){
16388                 if(!this.isHidden(i)){
16389                     c++;
16390                 }
16391             }
16392             return c;
16393         }
16394         return this.config.length;
16395     },
16396
16397     /**
16398      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
16399      * @param {Function} fn
16400      * @param {Object} scope (optional)
16401      * @return {Array} result
16402      */
16403     getColumnsBy : function(fn, scope){
16404         var r = [];
16405         for(var i = 0, len = this.config.length; i < len; i++){
16406             var c = this.config[i];
16407             if(fn.call(scope||this, c, i) === true){
16408                 r[r.length] = c;
16409             }
16410         }
16411         return r;
16412     },
16413
16414     /**
16415      * Returns true if the specified column is sortable.
16416      * @param {Number} col The column index
16417      * @return {Boolean}
16418      */
16419     isSortable : function(col){
16420         if(typeof this.config[col].sortable == "undefined"){
16421             return this.defaultSortable;
16422         }
16423         return this.config[col].sortable;
16424     },
16425
16426     /**
16427      * Returns the rendering (formatting) function defined for the column.
16428      * @param {Number} col The column index.
16429      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
16430      */
16431     getRenderer : function(col){
16432         if(!this.config[col].renderer){
16433             return Roo.bootstrap.Table.ColumnModel.defaultRenderer;
16434         }
16435         return this.config[col].renderer;
16436     },
16437
16438     /**
16439      * Sets the rendering (formatting) function for a column.
16440      * @param {Number} col The column index
16441      * @param {Function} fn The function to use to process the cell's raw data
16442      * to return HTML markup for the grid view. The render function is called with
16443      * the following parameters:<ul>
16444      * <li>Data value.</li>
16445      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
16446      * <li>css A CSS style string to apply to the table cell.</li>
16447      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
16448      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
16449      * <li>Row index</li>
16450      * <li>Column index</li>
16451      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
16452      */
16453     setRenderer : function(col, fn){
16454         this.config[col].renderer = fn;
16455     },
16456
16457     /**
16458      * Returns the width for the specified column.
16459      * @param {Number} col The column index
16460      * @return {Number}
16461      */
16462     getColumnWidth : function(col){
16463         return this.config[col].width * 1 || this.defaultWidth;
16464     },
16465
16466     /**
16467      * Sets the width for a column.
16468      * @param {Number} col The column index
16469      * @param {Number} width The new width
16470      */
16471     setColumnWidth : function(col, width, suppressEvent){
16472         this.config[col].width = width;
16473         this.totalWidth = null;
16474         if(!suppressEvent){
16475              this.fireEvent("widthchange", this, col, width);
16476         }
16477     },
16478
16479     /**
16480      * Returns the total width of all columns.
16481      * @param {Boolean} includeHidden True to include hidden column widths
16482      * @return {Number}
16483      */
16484     getTotalWidth : function(includeHidden){
16485         if(!this.totalWidth){
16486             this.totalWidth = 0;
16487             for(var i = 0, len = this.config.length; i < len; i++){
16488                 if(includeHidden || !this.isHidden(i)){
16489                     this.totalWidth += this.getColumnWidth(i);
16490                 }
16491             }
16492         }
16493         return this.totalWidth;
16494     },
16495
16496     /**
16497      * Returns the header for the specified column.
16498      * @param {Number} col The column index
16499      * @return {String}
16500      */
16501     getColumnHeader : function(col){
16502         return this.config[col].header;
16503     },
16504
16505     /**
16506      * Sets the header for a column.
16507      * @param {Number} col The column index
16508      * @param {String} header The new header
16509      */
16510     setColumnHeader : function(col, header){
16511         this.config[col].header = header;
16512         this.fireEvent("headerchange", this, col, header);
16513     },
16514
16515     /**
16516      * Returns the tooltip for the specified column.
16517      * @param {Number} col The column index
16518      * @return {String}
16519      */
16520     getColumnTooltip : function(col){
16521             return this.config[col].tooltip;
16522     },
16523     /**
16524      * Sets the tooltip for a column.
16525      * @param {Number} col The column index
16526      * @param {String} tooltip The new tooltip
16527      */
16528     setColumnTooltip : function(col, tooltip){
16529             this.config[col].tooltip = tooltip;
16530     },
16531
16532     /**
16533      * Returns the dataIndex for the specified column.
16534      * @param {Number} col The column index
16535      * @return {Number}
16536      */
16537     getDataIndex : function(col){
16538         return this.config[col].dataIndex;
16539     },
16540
16541     /**
16542      * Sets the dataIndex for a column.
16543      * @param {Number} col The column index
16544      * @param {Number} dataIndex The new dataIndex
16545      */
16546     setDataIndex : function(col, dataIndex){
16547         this.config[col].dataIndex = dataIndex;
16548     },
16549
16550     
16551     
16552     /**
16553      * Returns true if the cell is editable.
16554      * @param {Number} colIndex The column index
16555      * @param {Number} rowIndex The row index
16556      * @return {Boolean}
16557      */
16558     isCellEditable : function(colIndex, rowIndex){
16559         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
16560     },
16561
16562     /**
16563      * Returns the editor defined for the cell/column.
16564      * return false or null to disable editing.
16565      * @param {Number} colIndex The column index
16566      * @param {Number} rowIndex The row index
16567      * @return {Object}
16568      */
16569     getCellEditor : function(colIndex, rowIndex){
16570         return this.config[colIndex].editor;
16571     },
16572
16573     /**
16574      * Sets if a column is editable.
16575      * @param {Number} col The column index
16576      * @param {Boolean} editable True if the column is editable
16577      */
16578     setEditable : function(col, editable){
16579         this.config[col].editable = editable;
16580     },
16581
16582
16583     /**
16584      * Returns true if the column is hidden.
16585      * @param {Number} colIndex The column index
16586      * @return {Boolean}
16587      */
16588     isHidden : function(colIndex){
16589         return this.config[colIndex].hidden;
16590     },
16591
16592
16593     /**
16594      * Returns true if the column width cannot be changed
16595      */
16596     isFixed : function(colIndex){
16597         return this.config[colIndex].fixed;
16598     },
16599
16600     /**
16601      * Returns true if the column can be resized
16602      * @return {Boolean}
16603      */
16604     isResizable : function(colIndex){
16605         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
16606     },
16607     /**
16608      * Sets if a column is hidden.
16609      * @param {Number} colIndex The column index
16610      * @param {Boolean} hidden True if the column is hidden
16611      */
16612     setHidden : function(colIndex, hidden){
16613         this.config[colIndex].hidden = hidden;
16614         this.totalWidth = null;
16615         this.fireEvent("hiddenchange", this, colIndex, hidden);
16616     },
16617
16618     /**
16619      * Sets the editor for a column.
16620      * @param {Number} col The column index
16621      * @param {Object} editor The editor object
16622      */
16623     setEditor : function(col, editor){
16624         this.config[col].editor = editor;
16625     }
16626 });
16627
16628 Roo.bootstrap.Table.ColumnModel.defaultRenderer = function(value){
16629         if(typeof value == "string" && value.length < 1){
16630             return "&#160;";
16631         }
16632         return value;
16633 };
16634
16635 // Alias for backwards compatibility
16636 Roo.bootstrap.Table.DefaultColumnModel = Roo.bootstrap.Table.ColumnModel;
16637
16638 /**
16639  * @extends Roo.bootstrap.Table.AbstractSelectionModel
16640  * @class Roo.bootstrap.Table.RowSelectionModel
16641  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
16642  * It supports multiple selections and keyboard selection/navigation. 
16643  * @constructor
16644  * @param {Object} config
16645  */
16646
16647 Roo.bootstrap.Table.RowSelectionModel = function(config){
16648     Roo.apply(this, config);
16649     this.selections = new Roo.util.MixedCollection(false, function(o){
16650         return o.id;
16651     });
16652
16653     this.last = false;
16654     this.lastActive = false;
16655
16656     this.addEvents({
16657         /**
16658              * @event selectionchange
16659              * Fires when the selection changes
16660              * @param {SelectionModel} this
16661              */
16662             "selectionchange" : true,
16663         /**
16664              * @event afterselectionchange
16665              * Fires after the selection changes (eg. by key press or clicking)
16666              * @param {SelectionModel} this
16667              */
16668             "afterselectionchange" : true,
16669         /**
16670              * @event beforerowselect
16671              * Fires when a row is selected being selected, return false to cancel.
16672              * @param {SelectionModel} this
16673              * @param {Number} rowIndex The selected index
16674              * @param {Boolean} keepExisting False if other selections will be cleared
16675              */
16676             "beforerowselect" : true,
16677         /**
16678              * @event rowselect
16679              * Fires when a row is selected.
16680              * @param {SelectionModel} this
16681              * @param {Number} rowIndex The selected index
16682              * @param {Roo.data.Record} r The record
16683              */
16684             "rowselect" : true,
16685         /**
16686              * @event rowdeselect
16687              * Fires when a row is deselected.
16688              * @param {SelectionModel} this
16689              * @param {Number} rowIndex The selected index
16690              */
16691         "rowdeselect" : true
16692     });
16693     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
16694     this.locked = false;
16695 };
16696
16697 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
16698     /**
16699      * @cfg {Boolean} singleSelect
16700      * True to allow selection of only one row at a time (defaults to false)
16701      */
16702     singleSelect : false,
16703
16704     // private
16705     initEvents : function(){
16706
16707         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
16708             this.grid.on("mousedown", this.handleMouseDown, this);
16709         }else{ // allow click to work like normal
16710             this.grid.on("rowclick", this.handleDragableRowClick, this);
16711         }
16712
16713         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
16714             "up" : function(e){
16715                 if(!e.shiftKey){
16716                     this.selectPrevious(e.shiftKey);
16717                 }else if(this.last !== false && this.lastActive !== false){
16718                     var last = this.last;
16719                     this.selectRange(this.last,  this.lastActive-1);
16720                     this.grid.getView().focusRow(this.lastActive);
16721                     if(last !== false){
16722                         this.last = last;
16723                     }
16724                 }else{
16725                     this.selectFirstRow();
16726                 }
16727                 this.fireEvent("afterselectionchange", this);
16728             },
16729             "down" : function(e){
16730                 if(!e.shiftKey){
16731                     this.selectNext(e.shiftKey);
16732                 }else if(this.last !== false && this.lastActive !== false){
16733                     var last = this.last;
16734                     this.selectRange(this.last,  this.lastActive+1);
16735                     this.grid.getView().focusRow(this.lastActive);
16736                     if(last !== false){
16737                         this.last = last;
16738                     }
16739                 }else{
16740                     this.selectFirstRow();
16741                 }
16742                 this.fireEvent("afterselectionchange", this);
16743             },
16744             scope: this
16745         });
16746
16747         var view = this.grid.view;
16748         view.on("refresh", this.onRefresh, this);
16749         view.on("rowupdated", this.onRowUpdated, this);
16750         view.on("rowremoved", this.onRemove, this);
16751     },
16752
16753     // private
16754     onRefresh : function(){
16755         var ds = this.grid.dataSource, i, v = this.grid.view;
16756         var s = this.selections;
16757         s.each(function(r){
16758             if((i = ds.indexOfId(r.id)) != -1){
16759                 v.onRowSelect(i);
16760             }else{
16761                 s.remove(r);
16762             }
16763         });
16764     },
16765
16766     // private
16767     onRemove : function(v, index, r){
16768         this.selections.remove(r);
16769     },
16770
16771     // private
16772     onRowUpdated : function(v, index, r){
16773         if(this.isSelected(r)){
16774             v.onRowSelect(index);
16775         }
16776     },
16777
16778     /**
16779      * Select records.
16780      * @param {Array} records The records to select
16781      * @param {Boolean} keepExisting (optional) True to keep existing selections
16782      */
16783     selectRecords : function(records, keepExisting){
16784         if(!keepExisting){
16785             this.clearSelections();
16786         }
16787         var ds = this.grid.dataSource;
16788         for(var i = 0, len = records.length; i < len; i++){
16789             this.selectRow(ds.indexOf(records[i]), true);
16790         }
16791     },
16792
16793     /**
16794      * Gets the number of selected rows.
16795      * @return {Number}
16796      */
16797     getCount : function(){
16798         return this.selections.length;
16799     },
16800
16801     /**
16802      * Selects the first row in the grid.
16803      */
16804     selectFirstRow : function(){
16805         this.selectRow(0);
16806     },
16807
16808     /**
16809      * Select the last row.
16810      * @param {Boolean} keepExisting (optional) True to keep existing selections
16811      */
16812     selectLastRow : function(keepExisting){
16813         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
16814     },
16815
16816     /**
16817      * Selects the row immediately following the last selected row.
16818      * @param {Boolean} keepExisting (optional) True to keep existing selections
16819      */
16820     selectNext : function(keepExisting){
16821         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
16822             this.selectRow(this.last+1, keepExisting);
16823             this.grid.getView().focusRow(this.last);
16824         }
16825     },
16826
16827     /**
16828      * Selects the row that precedes the last selected row.
16829      * @param {Boolean} keepExisting (optional) True to keep existing selections
16830      */
16831     selectPrevious : function(keepExisting){
16832         if(this.last){
16833             this.selectRow(this.last-1, keepExisting);
16834             this.grid.getView().focusRow(this.last);
16835         }
16836     },
16837
16838     /**
16839      * Returns the selected records
16840      * @return {Array} Array of selected records
16841      */
16842     getSelections : function(){
16843         return [].concat(this.selections.items);
16844     },
16845
16846     /**
16847      * Returns the first selected record.
16848      * @return {Record}
16849      */
16850     getSelected : function(){
16851         return this.selections.itemAt(0);
16852     },
16853
16854
16855     /**
16856      * Clears all selections.
16857      */
16858     clearSelections : function(fast){
16859         if(this.locked) return;
16860         if(fast !== true){
16861             var ds = this.grid.dataSource;
16862             var s = this.selections;
16863             s.each(function(r){
16864                 this.deselectRow(ds.indexOfId(r.id));
16865             }, this);
16866             s.clear();
16867         }else{
16868             this.selections.clear();
16869         }
16870         this.last = false;
16871     },
16872
16873
16874     /**
16875      * Selects all rows.
16876      */
16877     selectAll : function(){
16878         if(this.locked) return;
16879         this.selections.clear();
16880         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
16881             this.selectRow(i, true);
16882         }
16883     },
16884
16885     /**
16886      * Returns True if there is a selection.
16887      * @return {Boolean}
16888      */
16889     hasSelection : function(){
16890         return this.selections.length > 0;
16891     },
16892
16893     /**
16894      * Returns True if the specified row is selected.
16895      * @param {Number/Record} record The record or index of the record to check
16896      * @return {Boolean}
16897      */
16898     isSelected : function(index){
16899         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
16900         return (r && this.selections.key(r.id) ? true : false);
16901     },
16902
16903     /**
16904      * Returns True if the specified record id is selected.
16905      * @param {String} id The id of record to check
16906      * @return {Boolean}
16907      */
16908     isIdSelected : function(id){
16909         return (this.selections.key(id) ? true : false);
16910     },
16911
16912     // private
16913     handleMouseDown : function(e, t){
16914         var view = this.grid.getView(), rowIndex;
16915         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
16916             return;
16917         };
16918         if(e.shiftKey && this.last !== false){
16919             var last = this.last;
16920             this.selectRange(last, rowIndex, e.ctrlKey);
16921             this.last = last; // reset the last
16922             view.focusRow(rowIndex);
16923         }else{
16924             var isSelected = this.isSelected(rowIndex);
16925             if(e.button !== 0 && isSelected){
16926                 view.focusRow(rowIndex);
16927             }else if(e.ctrlKey && isSelected){
16928                 this.deselectRow(rowIndex);
16929             }else if(!isSelected){
16930                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
16931                 view.focusRow(rowIndex);
16932             }
16933         }
16934         this.fireEvent("afterselectionchange", this);
16935     },
16936     // private
16937     handleDragableRowClick :  function(grid, rowIndex, e) 
16938     {
16939         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
16940             this.selectRow(rowIndex, false);
16941             grid.view.focusRow(rowIndex);
16942              this.fireEvent("afterselectionchange", this);
16943         }
16944     },
16945     
16946     /**
16947      * Selects multiple rows.
16948      * @param {Array} rows Array of the indexes of the row to select
16949      * @param {Boolean} keepExisting (optional) True to keep existing selections
16950      */
16951     selectRows : function(rows, keepExisting){
16952         if(!keepExisting){
16953             this.clearSelections();
16954         }
16955         for(var i = 0, len = rows.length; i < len; i++){
16956             this.selectRow(rows[i], true);
16957         }
16958     },
16959
16960     /**
16961      * Selects a range of rows. All rows in between startRow and endRow are also selected.
16962      * @param {Number} startRow The index of the first row in the range
16963      * @param {Number} endRow The index of the last row in the range
16964      * @param {Boolean} keepExisting (optional) True to retain existing selections
16965      */
16966     selectRange : function(startRow, endRow, keepExisting){
16967         if(this.locked) return;
16968         if(!keepExisting){
16969             this.clearSelections();
16970         }
16971         if(startRow <= endRow){
16972             for(var i = startRow; i <= endRow; i++){
16973                 this.selectRow(i, true);
16974             }
16975         }else{
16976             for(var i = startRow; i >= endRow; i--){
16977                 this.selectRow(i, true);
16978             }
16979         }
16980     },
16981
16982     /**
16983      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
16984      * @param {Number} startRow The index of the first row in the range
16985      * @param {Number} endRow The index of the last row in the range
16986      */
16987     deselectRange : function(startRow, endRow, preventViewNotify){
16988         if(this.locked) return;
16989         for(var i = startRow; i <= endRow; i++){
16990             this.deselectRow(i, preventViewNotify);
16991         }
16992     },
16993
16994     /**
16995      * Selects a row.
16996      * @param {Number} row The index of the row to select
16997      * @param {Boolean} keepExisting (optional) True to keep existing selections
16998      */
16999     selectRow : function(index, keepExisting, preventViewNotify){
17000         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
17001         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
17002             if(!keepExisting || this.singleSelect){
17003                 this.clearSelections();
17004             }
17005             var r = this.grid.dataSource.getAt(index);
17006             this.selections.add(r);
17007             this.last = this.lastActive = index;
17008             if(!preventViewNotify){
17009                 this.grid.getView().onRowSelect(index);
17010             }
17011             this.fireEvent("rowselect", this, index, r);
17012             this.fireEvent("selectionchange", this);
17013         }
17014     },
17015
17016     /**
17017      * Deselects a row.
17018      * @param {Number} row The index of the row to deselect
17019      */
17020     deselectRow : function(index, preventViewNotify){
17021         if(this.locked) return;
17022         if(this.last == index){
17023             this.last = false;
17024         }
17025         if(this.lastActive == index){
17026             this.lastActive = false;
17027         }
17028         var r = this.grid.dataSource.getAt(index);
17029         this.selections.remove(r);
17030         if(!preventViewNotify){
17031             this.grid.getView().onRowDeselect(index);
17032         }
17033         this.fireEvent("rowdeselect", this, index);
17034         this.fireEvent("selectionchange", this);
17035     },
17036
17037     // private
17038     restoreLast : function(){
17039         if(this._last){
17040             this.last = this._last;
17041         }
17042     },
17043
17044     // private
17045     acceptsNav : function(row, col, cm){
17046         return !cm.isHidden(col) && cm.isCellEditable(col, row);
17047     },
17048
17049     // private
17050     onEditorKey : function(field, e){
17051         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
17052         if(k == e.TAB){
17053             e.stopEvent();
17054             ed.completeEdit();
17055             if(e.shiftKey){
17056                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
17057             }else{
17058                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
17059             }
17060         }else if(k == e.ENTER && !e.ctrlKey){
17061             e.stopEvent();
17062             ed.completeEdit();
17063             if(e.shiftKey){
17064                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
17065             }else{
17066                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
17067             }
17068         }else if(k == e.ESC){
17069             ed.cancelEdit();
17070         }
17071         if(newCell){
17072             g.startEditing(newCell[0], newCell[1]);
17073         }
17074     }
17075 });/*
17076  * - LGPL
17077  *
17078  * element
17079  * 
17080  */
17081
17082 /**
17083  * @class Roo.bootstrap.MessageBar
17084  * @extends Roo.bootstrap.Component
17085  * Bootstrap MessageBar class
17086  * @cfg {String} html contents of the MessageBar
17087  * @cfg {String} weight (info | success | warning | danger) default info
17088  * @cfg {String} beforeClass insert the bar before the given class
17089  * @cfg {Boolean} closable (true | false) default false
17090  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
17091  * 
17092  * @constructor
17093  * Create a new Element
17094  * @param {Object} config The config object
17095  */
17096
17097 Roo.bootstrap.MessageBar = function(config){
17098     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
17099 };
17100
17101 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
17102     
17103     html: '',
17104     weight: 'info',
17105     closable: false,
17106     fixed: false,
17107     beforeClass: 'bootstrap-sticky-wrap',
17108     
17109     getAutoCreate : function(){
17110         
17111         var cfg = {
17112             tag: 'div',
17113             cls: 'alert alert-dismissable alert-' + this.weight,
17114             cn: [
17115                 {
17116                     tag: 'span',
17117                     cls: 'message',
17118                     html: this.html || ''
17119                 }
17120             ]
17121         }
17122         
17123         if(this.fixed){
17124             cfg.cls += ' alert-messages-fixed';
17125         }
17126         
17127         if(this.closable){
17128             cfg.cn.push({
17129                 tag: 'button',
17130                 cls: 'close',
17131                 html: 'x'
17132             });
17133         }
17134         
17135         return cfg;
17136     },
17137     
17138     onRender : function(ct, position)
17139     {
17140         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17141         
17142         if(!this.el){
17143             var cfg = Roo.apply({},  this.getAutoCreate());
17144             cfg.id = Roo.id();
17145             
17146             if (this.cls) {
17147                 cfg.cls += ' ' + this.cls;
17148             }
17149             if (this.style) {
17150                 cfg.style = this.style;
17151             }
17152             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
17153             
17154             this.el.setVisibilityMode(Roo.Element.DISPLAY);
17155         }
17156         
17157         this.el.select('>button.close').on('click', this.hide, this);
17158         
17159     },
17160     
17161     show : function()
17162     {
17163         if (!this.rendered) {
17164             this.render();
17165         }
17166         
17167         this.el.show();
17168         
17169         this.fireEvent('show', this);
17170         
17171     },
17172     
17173     hide : function()
17174     {
17175         if (!this.rendered) {
17176             this.render();
17177         }
17178         
17179         this.el.hide();
17180         
17181         this.fireEvent('hide', this);
17182     },
17183     
17184     update : function()
17185     {
17186 //        var e = this.el.dom.firstChild;
17187 //        
17188 //        if(this.closable){
17189 //            e = e.nextSibling;
17190 //        }
17191 //        
17192 //        e.data = this.html || '';
17193
17194         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
17195     }
17196    
17197 });
17198
17199  
17200
17201