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         
3696         // left icon..
3697         if (this.glyphicon || this.icon) {
3698             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
3699             a.cn.push({ tag : 'i', cls : c }) ;
3700         }
3701         // html..
3702         a.cn.push(span);
3703         // then badge..
3704         if (this.badge !== '') {
3705             a.cn.push({ tag: 'span',  cls : 'badge pull-right ' + (this.badgecls || ''), html: this.badge }); 
3706         }
3707         // fi
3708         if (this.menu) {
3709             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
3710             a.cls += 'dropdown-toggle treeview' ;
3711             
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.more = [];
11609         ev.rows = rows;
11610         ev.cells = cells;
11611         for (var i = 0; i < cells.length;i++) {
11612             cells[i].rows = Math.max(cells[i].rows || 0 , ev.row + 1 );
11613             
11614         }
11615         
11616         this.calevents.push(ev);
11617     },
11618     
11619     clearEvents: function() {
11620         
11621         if(!this.calevents){
11622             return;
11623         }
11624         
11625         Roo.each(this.cells.elements, function(c){
11626             c.rows = 0;
11627         });
11628         
11629         Roo.each(this.calevents, function(e) {
11630             Roo.each(e.els, function(el) {
11631                 el.un('mouseenter' ,this.onEventEnter, this);
11632                 el.un('mouseleave' ,this.onEventLeave, this);
11633                 el.remove();
11634             },this);
11635         },this);
11636         
11637     },
11638     
11639     renderEvents: function()
11640     {   
11641         // first make sure there is enough space..
11642         this.cells.each(function(c) {
11643             c.more = [];
11644             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, Math.min(5, c.rows) * 20));
11645         });
11646         
11647         for (var e = 0; e < this.calevents.length; e++) {
11648             
11649             var ev = this.calevents[e];
11650             var cells = ev.cells;
11651             var rows = ev.rows;
11652             
11653             for(var i =0; i < rows.length; i++) {
11654                 
11655                  
11656                 // how many rows should it span..
11657                 
11658                 var  cfg = {
11659                     cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
11660                     style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
11661                     
11662                     unselectable : "on",
11663                     cn : [
11664                         {
11665                             cls: 'fc-event-inner',
11666                             cn : [
11667 //                                {
11668 //                                  tag:'span',
11669 //                                  cls: 'fc-event-time',
11670 //                                  html : cells.length > 1 ? '' : ev.time
11671 //                                },
11672                                 {
11673                                   tag:'span',
11674                                   cls: 'fc-event-title',
11675                                   html : String.format('{0}', ev.title)
11676                                 }
11677                                 
11678                                 
11679                             ]
11680                         },
11681                         {
11682                             cls: 'ui-resizable-handle ui-resizable-e',
11683                             html : '&nbsp;&nbsp;&nbsp'
11684                         }
11685                         
11686                     ]
11687                 };
11688                 
11689                 var more = this.cells.item(this.cells.indexOf(cells[0])).more;
11690                 
11691                 if(more.length){
11692                     Roo.log(this.cells.item(this.cells.indexOf(cells[0])));
11693                     this.cells.item(this.cells.indexOf(cells[0])).more.push(ev);
11694                     continue;
11695                 }
11696                 
11697                 if(ev.row > 3){
11698                     Roo.log(this.cells.item(this.cells.indexOf(cells[0])));
11699                     cfg.cn[0].cn[0].html = 'More';
11700                     this.cells.item(this.cells.indexOf(cells[0])).more.push(ev);
11701                 }
11702                 
11703                 if (i == 0) {
11704                     cfg.cls += ' fc-event-start';
11705                 }
11706                 if ((i+1) == rows.length) {
11707                     cfg.cls += ' fc-event-end';
11708                 }
11709                 
11710                 var ctr = this.el.select('.fc-event-container',true).first();
11711                 var cg = ctr.createChild(cfg);
11712                 
11713                 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
11714                 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
11715                 //Roo.log(cg);
11716                 cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
11717                 cg.setWidth(ebox.right - sbox.x -2);
11718                 
11719                 if(ev.row > 3){
11720                     cg.on('click', this.onMoreEventClick, this, ev);
11721                     continue;
11722                 }
11723                 
11724                 cg.on('mouseenter' ,this.onEventEnter, this, ev);
11725                 cg.on('mouseleave' ,this.onEventLeave, this, ev);
11726                 cg.on('click', this.onEventClick, this, ev);
11727                 
11728                 ev.els.push(cg);
11729                 
11730                 
11731             }
11732             
11733             
11734         }
11735         
11736     },
11737     
11738     onEventEnter: function (e, el,event,d) {
11739         this.fireEvent('evententer', this, el, event);
11740     },
11741     
11742     onEventLeave: function (e, el,event,d) {
11743         this.fireEvent('eventleave', this, el, event);
11744     },
11745     
11746     onEventClick: function (e, el,event,d) {
11747         this.fireEvent('eventclick', this, el, event);
11748     },
11749     
11750     onMonthChange: function () {
11751         this.store.load();
11752     },
11753     
11754     onMoreEventClick: function(e, el, event)
11755     {
11756         var _this = this;
11757         
11758         Roo.log(this.calpopover);
11759         Roo.log(e);
11760         Roo.log(el);
11761         Roo.log(event);
11762         
11763         var more = this.cells.item(this.cells.indexOf(event.cells[0])).more;
11764         
11765         this.calpopover.placement = 'right';
11766         this.calpopover.setTitle('More');
11767         
11768         this.calpopover.setContent('');
11769         
11770         var ctr = this.calpopover.el.select('.popover-content', true).first();
11771         
11772         Roo.each(more, function(m){
11773             var cfg = {
11774                 cls : 'fc-event-hori fc-event-draggable',
11775                 html : m.title
11776             }
11777             var cg = ctr.createChild(cfg);
11778             
11779             cg.on('click', _this.onEventClick, _this, m);
11780         });
11781         
11782         this.calpopover.show(el);
11783         
11784         
11785     },
11786     
11787     onLoad: function () 
11788     {   
11789         this.calevents = [];
11790         var cal = this;
11791         
11792         if(this.store.getCount() > 0){
11793             this.store.data.each(function(d){
11794                cal.addItem({
11795                     id : d.data.id,
11796                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
11797                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
11798                     time : d.data.start_time,
11799                     title : d.data.title,
11800                     description : d.data.description,
11801                     venue : d.data.venue
11802                 });
11803             });
11804         }
11805         Roo.log('calevents!!!!!!!!!!!!!!!!!!!!!!');
11806         Roo.log(this.calevents);
11807         
11808         this.renderEvents();
11809         
11810         Roo.log(this.cells);
11811         
11812         if(this.loadMask){
11813             this.maskEl.hide();
11814         }
11815     },
11816     
11817     onBeforeLoad: function()
11818     {
11819         this.clearEvents();
11820         
11821         if(this.loadMask){
11822             this.maskEl.show();
11823         }
11824     }
11825 });
11826
11827  
11828  /*
11829  * - LGPL
11830  *
11831  * element
11832  * 
11833  */
11834
11835 /**
11836  * @class Roo.bootstrap.Popover
11837  * @extends Roo.bootstrap.Component
11838  * Bootstrap Popover class
11839  * @cfg {String} html contents of the popover   (or false to use children..)
11840  * @cfg {String} title of popover (or false to hide)
11841  * @cfg {String} placement how it is placed
11842  * @cfg {String} trigger click || hover (or false to trigger manually)
11843  * @cfg {String} over what (parent or false to trigger manually.)
11844  * 
11845  * @constructor
11846  * Create a new Popover
11847  * @param {Object} config The config object
11848  */
11849
11850 Roo.bootstrap.Popover = function(config){
11851     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
11852 };
11853
11854 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
11855     
11856     title: 'Fill in a title',
11857     html: false,
11858     
11859     placement : 'right',
11860     trigger : 'hover', // hover
11861     
11862     over: 'parent',
11863     
11864     can_build_overlaid : false,
11865     
11866     getChildContainer : function()
11867     {
11868         return this.el.select('.popover-content',true).first();
11869     },
11870     
11871     getAutoCreate : function(){
11872          Roo.log('make popover?');
11873         var cfg = {
11874            cls : 'popover roo-dynamic',
11875            style: 'display:block',
11876            cn : [
11877                 {
11878                     cls : 'arrow'
11879                 },
11880                 {
11881                     cls : 'popover-inner',
11882                     cn : [
11883                         {
11884                             tag: 'h3',
11885                             cls: 'popover-title',
11886                             html : this.title
11887                         },
11888                         {
11889                             cls : 'popover-content',
11890                             html : this.html
11891                         }
11892                     ]
11893                     
11894                 }
11895            ]
11896         };
11897         
11898         return cfg;
11899     },
11900     setTitle: function(str)
11901     {
11902         this.el.select('.popover-title',true).first().dom.innerHTML = str;
11903     },
11904     setContent: function(str)
11905     {
11906         this.el.select('.popover-content',true).first().dom.innerHTML = str;
11907     },
11908     // as it get's added to the bottom of the page.
11909     onRender : function(ct, position)
11910     {
11911         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
11912         if(!this.el){
11913             var cfg = Roo.apply({},  this.getAutoCreate());
11914             cfg.id = Roo.id();
11915             
11916             if (this.cls) {
11917                 cfg.cls += ' ' + this.cls;
11918             }
11919             if (this.style) {
11920                 cfg.style = this.style;
11921             }
11922             Roo.log("adding to ")
11923             this.el = Roo.get(document.body).createChild(cfg, position);
11924             Roo.log(this.el);
11925         }
11926         this.initEvents();
11927     },
11928     
11929     initEvents : function()
11930     {
11931         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
11932         this.el.enableDisplayMode('block');
11933         this.el.hide();
11934         if (this.over === false) {
11935             return; 
11936         }
11937         if (this.triggers === false) {
11938             return;
11939         }
11940         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
11941         var triggers = this.trigger ? this.trigger.split(' ') : [];
11942         Roo.each(triggers, function(trigger) {
11943         
11944             if (trigger == 'click') {
11945                 on_el.on('click', this.toggle, this);
11946             } else if (trigger != 'manual') {
11947                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
11948                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
11949       
11950                 on_el.on(eventIn  ,this.enter, this);
11951                 on_el.on(eventOut, this.leave, this);
11952             }
11953         }, this);
11954         
11955     },
11956     
11957     
11958     // private
11959     timeout : null,
11960     hoverState : null,
11961     
11962     toggle : function () {
11963         this.hoverState == 'in' ? this.leave() : this.enter();
11964     },
11965     
11966     enter : function () {
11967        
11968     
11969         clearTimeout(this.timeout);
11970     
11971         this.hoverState = 'in'
11972     
11973         if (!this.delay || !this.delay.show) {
11974             this.show();
11975             return 
11976         }
11977         var _t = this;
11978         this.timeout = setTimeout(function () {
11979             if (_t.hoverState == 'in') {
11980                 _t.show();
11981             }
11982         }, this.delay.show)
11983     },
11984     leave : function() {
11985         clearTimeout(this.timeout);
11986     
11987         this.hoverState = 'out'
11988     
11989         if (!this.delay || !this.delay.hide) {
11990             this.hide();
11991             return 
11992         }
11993         var _t = this;
11994         this.timeout = setTimeout(function () {
11995             if (_t.hoverState == 'out') {
11996                 _t.hide();
11997             }
11998         }, this.delay.hide)
11999     },
12000     
12001     show : function (on_el)
12002     {
12003         if (!on_el) {
12004             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
12005         }
12006         // set content.
12007         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
12008         if (this.html !== false) {
12009             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
12010         }
12011         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
12012         if (!this.title.length) {
12013             this.el.select('.popover-title',true).hide();
12014         }
12015         
12016         var placement = typeof this.placement == 'function' ?
12017             this.placement.call(this, this.el, on_el) :
12018             this.placement;
12019             
12020         var autoToken = /\s?auto?\s?/i;
12021         var autoPlace = autoToken.test(placement);
12022         if (autoPlace) {
12023             placement = placement.replace(autoToken, '') || 'top';
12024         }
12025         
12026         //this.el.detach()
12027         //this.el.setXY([0,0]);
12028         this.el.show();
12029         this.el.dom.style.display='block';
12030         this.el.addClass(placement);
12031         
12032         //this.el.appendTo(on_el);
12033         
12034         var p = this.getPosition();
12035         var box = this.el.getBox();
12036         
12037         if (autoPlace) {
12038             // fixme..
12039         }
12040         var align = Roo.bootstrap.Popover.alignment[placement]
12041         this.el.alignTo(on_el, align[0],align[1]);
12042         //var arrow = this.el.select('.arrow',true).first();
12043         //arrow.set(align[2], 
12044         
12045         this.el.addClass('in');
12046         this.hoverState = null;
12047         
12048         if (this.el.hasClass('fade')) {
12049             // fade it?
12050         }
12051         
12052     },
12053     hide : function()
12054     {
12055         this.el.setXY([0,0]);
12056         this.el.removeClass('in');
12057         this.el.hide();
12058         
12059     }
12060     
12061 });
12062
12063 Roo.bootstrap.Popover.alignment = {
12064     'left' : ['r-l', [-10,0], 'right'],
12065     'right' : ['l-r', [10,0], 'left'],
12066     'bottom' : ['t-b', [0,10], 'top'],
12067     'top' : [ 'b-t', [0,-10], 'bottom']
12068 };
12069
12070  /*
12071  * - LGPL
12072  *
12073  * Progress
12074  * 
12075  */
12076
12077 /**
12078  * @class Roo.bootstrap.Progress
12079  * @extends Roo.bootstrap.Component
12080  * Bootstrap Progress class
12081  * @cfg {Boolean} striped striped of the progress bar
12082  * @cfg {Boolean} active animated of the progress bar
12083  * 
12084  * 
12085  * @constructor
12086  * Create a new Progress
12087  * @param {Object} config The config object
12088  */
12089
12090 Roo.bootstrap.Progress = function(config){
12091     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
12092 };
12093
12094 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
12095     
12096     striped : false,
12097     active: false,
12098     
12099     getAutoCreate : function(){
12100         var cfg = {
12101             tag: 'div',
12102             cls: 'progress'
12103         };
12104         
12105         
12106         if(this.striped){
12107             cfg.cls += ' progress-striped';
12108         }
12109       
12110         if(this.active){
12111             cfg.cls += ' active';
12112         }
12113         
12114         
12115         return cfg;
12116     }
12117    
12118 });
12119
12120  
12121
12122  /*
12123  * - LGPL
12124  *
12125  * ProgressBar
12126  * 
12127  */
12128
12129 /**
12130  * @class Roo.bootstrap.ProgressBar
12131  * @extends Roo.bootstrap.Component
12132  * Bootstrap ProgressBar class
12133  * @cfg {Number} aria_valuenow aria-value now
12134  * @cfg {Number} aria_valuemin aria-value min
12135  * @cfg {Number} aria_valuemax aria-value max
12136  * @cfg {String} label label for the progress bar
12137  * @cfg {String} panel (success | info | warning | danger )
12138  * @cfg {String} role role of the progress bar
12139  * @cfg {String} sr_only text
12140  * 
12141  * 
12142  * @constructor
12143  * Create a new ProgressBar
12144  * @param {Object} config The config object
12145  */
12146
12147 Roo.bootstrap.ProgressBar = function(config){
12148     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
12149 };
12150
12151 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
12152     
12153     aria_valuenow : 0,
12154     aria_valuemin : 0,
12155     aria_valuemax : 100,
12156     label : false,
12157     panel : false,
12158     role : false,
12159     sr_only: false,
12160     
12161     getAutoCreate : function()
12162     {
12163         
12164         var cfg = {
12165             tag: 'div',
12166             cls: 'progress-bar',
12167             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
12168         };
12169         
12170         if(this.sr_only){
12171             cfg.cn = {
12172                 tag: 'span',
12173                 cls: 'sr-only',
12174                 html: this.sr_only
12175             }
12176         }
12177         
12178         if(this.role){
12179             cfg.role = this.role;
12180         }
12181         
12182         if(this.aria_valuenow){
12183             cfg['aria-valuenow'] = this.aria_valuenow;
12184         }
12185         
12186         if(this.aria_valuemin){
12187             cfg['aria-valuemin'] = this.aria_valuemin;
12188         }
12189         
12190         if(this.aria_valuemax){
12191             cfg['aria-valuemax'] = this.aria_valuemax;
12192         }
12193         
12194         if(this.label && !this.sr_only){
12195             cfg.html = this.label;
12196         }
12197         
12198         if(this.panel){
12199             cfg.cls += ' progress-bar-' + this.panel;
12200         }
12201         
12202         return cfg;
12203     },
12204     
12205     update : function(aria_valuenow)
12206     {
12207         this.aria_valuenow = aria_valuenow;
12208         
12209         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
12210     }
12211    
12212 });
12213
12214  
12215
12216  /*
12217  * - LGPL
12218  *
12219  * TabPanel
12220  * 
12221  */
12222
12223 /**
12224  * @class Roo.bootstrap.TabPanel
12225  * @extends Roo.bootstrap.Component
12226  * Bootstrap TabPanel class
12227  * @cfg {Boolean} active panel active
12228  * @cfg {String} html panel content
12229  * @cfg {String} tabId tab relate id
12230  * @cfg {String} navId The navbar which triggers show hide
12231  * 
12232  * 
12233  * @constructor
12234  * Create a new TabPanel
12235  * @param {Object} config The config object
12236  */
12237
12238 Roo.bootstrap.TabPanel = function(config){
12239     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
12240      this.addEvents({
12241         /**
12242              * @event changed
12243              * Fires when the active status changes
12244              * @param {Roo.bootstrap.TabPanel} this
12245              * @param {Boolean} state the new state
12246             
12247          */
12248         'changed': true
12249      });
12250 };
12251
12252 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
12253     
12254     active: false,
12255     html: false,
12256     tabId: false,
12257     navId : false,
12258     
12259     getAutoCreate : function(){
12260         var cfg = {
12261             tag: 'div',
12262             cls: 'tab-pane',
12263             html: this.html || ''
12264         };
12265         
12266         if(this.active){
12267             cfg.cls += ' active';
12268         }
12269         
12270         if(this.tabId){
12271             cfg.tabId = this.tabId;
12272         }
12273         
12274         return cfg;
12275     },
12276     onRender : function(ct, position)
12277     {
12278        // Roo.log("Call onRender: " + this.xtype);
12279         
12280         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
12281         
12282         if (this.navId && this.tabId) {
12283             var item = Roo.bootstrap.NavGroup.get(this.navId).getNavItem(this.tabId);
12284             if (!item) {
12285                 Roo.log("could not find navID:"  + this.navId + ", tabId: " + this.tabId);
12286             } else {
12287                 item.on('changed', function(item, state) {
12288                     this.setActive(state);
12289                 }, this);
12290             }
12291         }
12292         
12293     },
12294     setActive: function(state)
12295     {
12296         Roo.log("panel - set active " + this.tabId + "=" + state);
12297         
12298         this.active = state;
12299         if (!state) {
12300             this.el.removeClass('active');
12301             
12302         } else  if (!this.el.hasClass('active')) {
12303             this.el.addClass('active');
12304         }
12305         this.fireEvent('changed', this, state);
12306     }
12307     
12308     
12309 });
12310  
12311
12312  
12313
12314  /*
12315  * - LGPL
12316  *
12317  * DateField
12318  * 
12319  */
12320
12321 /**
12322  * @class Roo.bootstrap.DateField
12323  * @extends Roo.bootstrap.Input
12324  * Bootstrap DateField class
12325  * @cfg {Number} weekStart default 0
12326  * @cfg {Number} weekStart default 0
12327  * @cfg {Number} viewMode default empty, (months|years)
12328  * @cfg {Number} minViewMode default empty, (months|years)
12329  * @cfg {Number} startDate default -Infinity
12330  * @cfg {Number} endDate default Infinity
12331  * @cfg {Boolean} todayHighlight default false
12332  * @cfg {Boolean} todayBtn default false
12333  * @cfg {Boolean} calendarWeeks default false
12334  * @cfg {Object} daysOfWeekDisabled default empty
12335  * 
12336  * @cfg {Boolean} keyboardNavigation default true
12337  * @cfg {String} language default en
12338  * 
12339  * @constructor
12340  * Create a new DateField
12341  * @param {Object} config The config object
12342  */
12343
12344 Roo.bootstrap.DateField = function(config){
12345     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
12346      this.addEvents({
12347             /**
12348              * @event show
12349              * Fires when this field show.
12350              * @param {Roo.bootstrap.DateField} this
12351              * @param {Mixed} date The date value
12352              */
12353             show : true,
12354             /**
12355              * @event show
12356              * Fires when this field hide.
12357              * @param {Roo.bootstrap.DateField} this
12358              * @param {Mixed} date The date value
12359              */
12360             hide : true,
12361             /**
12362              * @event select
12363              * Fires when select a date.
12364              * @param {Roo.bootstrap.DateField} this
12365              * @param {Mixed} date The date value
12366              */
12367             select : true
12368         });
12369 };
12370
12371 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
12372     
12373     /**
12374      * @cfg {String} format
12375      * The default date format string which can be overriden for localization support.  The format must be
12376      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
12377      */
12378     format : "m/d/y",
12379     /**
12380      * @cfg {String} altFormats
12381      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
12382      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
12383      */
12384     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
12385     
12386     weekStart : 0,
12387     
12388     viewMode : '',
12389     
12390     minViewMode : '',
12391     
12392     todayHighlight : false,
12393     
12394     todayBtn: false,
12395     
12396     language: 'en',
12397     
12398     keyboardNavigation: true,
12399     
12400     calendarWeeks: false,
12401     
12402     startDate: -Infinity,
12403     
12404     endDate: Infinity,
12405     
12406     daysOfWeekDisabled: [],
12407     
12408     _events: [],
12409     
12410     UTCDate: function()
12411     {
12412         return new Date(Date.UTC.apply(Date, arguments));
12413     },
12414     
12415     UTCToday: function()
12416     {
12417         var today = new Date();
12418         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
12419     },
12420     
12421     getDate: function() {
12422             var d = this.getUTCDate();
12423             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
12424     },
12425     
12426     getUTCDate: function() {
12427             return this.date;
12428     },
12429     
12430     setDate: function(d) {
12431             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
12432     },
12433     
12434     setUTCDate: function(d) {
12435             this.date = d;
12436             this.setValue(this.formatDate(this.date));
12437     },
12438         
12439     onRender: function(ct, position)
12440     {
12441         
12442         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
12443         
12444         this.language = this.language || 'en';
12445         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
12446         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
12447         
12448         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
12449         this.format = this.format || 'm/d/y';
12450         this.isInline = false;
12451         this.isInput = true;
12452         this.component = this.el.select('.add-on', true).first() || false;
12453         this.component = (this.component && this.component.length === 0) ? false : this.component;
12454         this.hasInput = this.component && this.inputEL().length;
12455         
12456         if (typeof(this.minViewMode === 'string')) {
12457             switch (this.minViewMode) {
12458                 case 'months':
12459                     this.minViewMode = 1;
12460                     break;
12461                 case 'years':
12462                     this.minViewMode = 2;
12463                     break;
12464                 default:
12465                     this.minViewMode = 0;
12466                     break;
12467             }
12468         }
12469         
12470         if (typeof(this.viewMode === 'string')) {
12471             switch (this.viewMode) {
12472                 case 'months':
12473                     this.viewMode = 1;
12474                     break;
12475                 case 'years':
12476                     this.viewMode = 2;
12477                     break;
12478                 default:
12479                     this.viewMode = 0;
12480                     break;
12481             }
12482         }
12483                 
12484         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
12485         
12486         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
12487         
12488         this.picker().on('mousedown', this.onMousedown, this);
12489         this.picker().on('click', this.onClick, this);
12490         
12491         this.picker().addClass('datepicker-dropdown');
12492         
12493         this.startViewMode = this.viewMode;
12494         
12495         
12496         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
12497             if(!this.calendarWeeks){
12498                 v.remove();
12499                 return;
12500             };
12501             
12502             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
12503             v.attr('colspan', function(i, val){
12504                 return parseInt(val) + 1;
12505             });
12506         })
12507                         
12508         
12509         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
12510         
12511         this.setStartDate(this.startDate);
12512         this.setEndDate(this.endDate);
12513         
12514         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
12515         
12516         this.fillDow();
12517         this.fillMonths();
12518         this.update();
12519         this.showMode();
12520         
12521         if(this.isInline) {
12522             this.show();
12523         }
12524     },
12525     
12526     picker : function()
12527     {
12528         return this.el.select('.datepicker', true).first();
12529     },
12530     
12531     fillDow: function()
12532     {
12533         var dowCnt = this.weekStart;
12534         
12535         var dow = {
12536             tag: 'tr',
12537             cn: [
12538                 
12539             ]
12540         };
12541         
12542         if(this.calendarWeeks){
12543             dow.cn.push({
12544                 tag: 'th',
12545                 cls: 'cw',
12546                 html: '&nbsp;'
12547             })
12548         }
12549         
12550         while (dowCnt < this.weekStart + 7) {
12551             dow.cn.push({
12552                 tag: 'th',
12553                 cls: 'dow',
12554                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
12555             });
12556         }
12557         
12558         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
12559     },
12560     
12561     fillMonths: function()
12562     {    
12563         var i = 0
12564         var months = this.picker().select('>.datepicker-months td', true).first();
12565         
12566         months.dom.innerHTML = '';
12567         
12568         while (i < 12) {
12569             var month = {
12570                 tag: 'span',
12571                 cls: 'month',
12572                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
12573             }
12574             
12575             months.createChild(month);
12576         }
12577         
12578     },
12579     
12580     update: function(){
12581         
12582         this.date = (typeof(this.date) === 'undefined') ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
12583         
12584         if (this.date < this.startDate) {
12585             this.viewDate = new Date(this.startDate);
12586         } else if (this.date > this.endDate) {
12587             this.viewDate = new Date(this.endDate);
12588         } else {
12589             this.viewDate = new Date(this.date);
12590         }
12591         
12592         this.fill();
12593     },
12594     
12595     fill: function() {
12596         var d = new Date(this.viewDate),
12597                 year = d.getUTCFullYear(),
12598                 month = d.getUTCMonth(),
12599                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
12600                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
12601                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
12602                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
12603                 currentDate = this.date && this.date.valueOf(),
12604                 today = this.UTCToday();
12605         
12606         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
12607         
12608 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
12609         
12610 //        this.picker.select('>tfoot th.today').
12611 //                                              .text(dates[this.language].today)
12612 //                                              .toggle(this.todayBtn !== false);
12613     
12614         this.updateNavArrows();
12615         this.fillMonths();
12616                                                 
12617         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
12618         
12619         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
12620          
12621         prevMonth.setUTCDate(day);
12622         
12623         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
12624         
12625         var nextMonth = new Date(prevMonth);
12626         
12627         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
12628         
12629         nextMonth = nextMonth.valueOf();
12630         
12631         var fillMonths = false;
12632         
12633         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
12634         
12635         while(prevMonth.valueOf() < nextMonth) {
12636             var clsName = '';
12637             
12638             if (prevMonth.getUTCDay() === this.weekStart) {
12639                 if(fillMonths){
12640                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
12641                 }
12642                     
12643                 fillMonths = {
12644                     tag: 'tr',
12645                     cn: []
12646                 };
12647                 
12648                 if(this.calendarWeeks){
12649                     // ISO 8601: First week contains first thursday.
12650                     // ISO also states week starts on Monday, but we can be more abstract here.
12651                     var
12652                     // Start of current week: based on weekstart/current date
12653                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
12654                     // Thursday of this week
12655                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
12656                     // First Thursday of year, year from thursday
12657                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
12658                     // Calendar week: ms between thursdays, div ms per day, div 7 days
12659                     calWeek =  (th - yth) / 864e5 / 7 + 1;
12660                     
12661                     fillMonths.cn.push({
12662                         tag: 'td',
12663                         cls: 'cw',
12664                         html: calWeek
12665                     });
12666                 }
12667             }
12668             
12669             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
12670                 clsName += ' old';
12671             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
12672                 clsName += ' new';
12673             }
12674             if (this.todayHighlight &&
12675                 prevMonth.getUTCFullYear() == today.getFullYear() &&
12676                 prevMonth.getUTCMonth() == today.getMonth() &&
12677                 prevMonth.getUTCDate() == today.getDate()) {
12678                 clsName += ' today';
12679             }
12680             
12681             if (currentDate && prevMonth.valueOf() === currentDate) {
12682                 clsName += ' active';
12683             }
12684             
12685             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
12686                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
12687                     clsName += ' disabled';
12688             }
12689             
12690             fillMonths.cn.push({
12691                 tag: 'td',
12692                 cls: 'day ' + clsName,
12693                 html: prevMonth.getDate()
12694             })
12695             
12696             prevMonth.setDate(prevMonth.getDate()+1);
12697         }
12698           
12699         var currentYear = this.date && this.date.getUTCFullYear();
12700         var currentMonth = this.date && this.date.getUTCMonth();
12701         
12702         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
12703         
12704         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
12705             v.removeClass('active');
12706             
12707             if(currentYear === year && k === currentMonth){
12708                 v.addClass('active');
12709             }
12710             
12711             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
12712                 v.addClass('disabled');
12713             }
12714             
12715         });
12716         
12717         
12718         year = parseInt(year/10, 10) * 10;
12719         
12720         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
12721         
12722         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
12723         
12724         year -= 1;
12725         for (var i = -1; i < 11; i++) {
12726             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
12727                 tag: 'span',
12728                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
12729                 html: year
12730             })
12731             
12732             year += 1;
12733         }
12734     },
12735     
12736     showMode: function(dir) {
12737         if (dir) {
12738             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
12739         }
12740         Roo.each(this.picker().select('>div',true).elements, function(v){
12741             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
12742             v.hide();
12743         });
12744         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
12745     },
12746     
12747     place: function()
12748     {
12749         if(this.isInline) return;
12750         
12751         this.picker().removeClass(['bottom', 'top']);
12752         
12753         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
12754             /*
12755              * place to the top of element!
12756              *
12757              */
12758             
12759             this.picker().addClass('top');
12760             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12761             
12762             return;
12763         }
12764         
12765         this.picker().addClass('bottom');
12766         
12767         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12768     },
12769     
12770     parseDate : function(value){
12771         if(!value || value instanceof Date){
12772             return value;
12773         }
12774         var v = Date.parseDate(value, this.format);
12775         if (!v && this.useIso) {
12776             v = Date.parseDate(value, 'Y-m-d');
12777         }
12778         if(!v && this.altFormats){
12779             if(!this.altFormatsArray){
12780                 this.altFormatsArray = this.altFormats.split("|");
12781             }
12782             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
12783                 v = Date.parseDate(value, this.altFormatsArray[i]);
12784             }
12785         }
12786         return v;
12787     },
12788     
12789     formatDate : function(date, fmt){
12790         return (!date || !(date instanceof Date)) ?
12791         date : date.dateFormat(fmt || this.format);
12792     },
12793     
12794     onFocus : function()
12795     {
12796         Roo.bootstrap.DateField.superclass.onFocus.call(this);
12797         this.show();
12798     },
12799     
12800     onBlur : function()
12801     {
12802         Roo.bootstrap.DateField.superclass.onBlur.call(this);
12803         this.hide();
12804     },
12805     
12806     show : function()
12807     {
12808         this.picker().show();
12809         this.update();
12810         this.place();
12811         
12812         this.fireEvent('show', this, this.date);
12813     },
12814     
12815     hide : function()
12816     {
12817         if(this.isInline) return;
12818         this.picker().hide();
12819         this.viewMode = this.startViewMode;
12820         this.showMode();
12821         
12822         this.fireEvent('hide', this, this.date);
12823         
12824     },
12825     
12826     onMousedown: function(e){
12827         e.stopPropagation();
12828         e.preventDefault();
12829     },
12830     
12831     keyup: function(e){
12832         Roo.bootstrap.DateField.superclass.keyup.call(this);
12833         this.update();
12834         
12835     },
12836
12837     setValue: function(v){
12838         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
12839         
12840         this.fireEvent('select', this, this.date);
12841         
12842     },
12843     
12844     fireKey: function(e){
12845         if (!this.picker().isVisible()){
12846             if (e.keyCode == 27) // allow escape to hide and re-show picker
12847                 this.show();
12848             return;
12849         }
12850         var dateChanged = false,
12851         dir, day, month,
12852         newDate, newViewDate;
12853         switch(e.keyCode){
12854             case 27: // escape
12855                 this.hide();
12856                 e.preventDefault();
12857                 break;
12858             case 37: // left
12859             case 39: // right
12860                 if (!this.keyboardNavigation) break;
12861                 dir = e.keyCode == 37 ? -1 : 1;
12862                 
12863                 if (e.ctrlKey){
12864                     newDate = this.moveYear(this.date, dir);
12865                     newViewDate = this.moveYear(this.viewDate, dir);
12866                 } else if (e.shiftKey){
12867                     newDate = this.moveMonth(this.date, dir);
12868                     newViewDate = this.moveMonth(this.viewDate, dir);
12869                 } else {
12870                     newDate = new Date(this.date);
12871                     newDate.setUTCDate(this.date.getUTCDate() + dir);
12872                     newViewDate = new Date(this.viewDate);
12873                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
12874                 }
12875                 if (this.dateWithinRange(newDate)){
12876                     this.date = newDate;
12877                     this.viewDate = newViewDate;
12878                     this.setValue(this.formatDate(this.date));
12879                     this.update();
12880                     e.preventDefault();
12881                     dateChanged = true;
12882                 }
12883                 break;
12884             case 38: // up
12885             case 40: // down
12886                 if (!this.keyboardNavigation) break;
12887                 dir = e.keyCode == 38 ? -1 : 1;
12888                 if (e.ctrlKey){
12889                     newDate = this.moveYear(this.date, dir);
12890                     newViewDate = this.moveYear(this.viewDate, dir);
12891                 } else if (e.shiftKey){
12892                     newDate = this.moveMonth(this.date, dir);
12893                     newViewDate = this.moveMonth(this.viewDate, dir);
12894                 } else {
12895                     newDate = new Date(this.date);
12896                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
12897                     newViewDate = new Date(this.viewDate);
12898                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
12899                 }
12900                 if (this.dateWithinRange(newDate)){
12901                     this.date = newDate;
12902                     this.viewDate = newViewDate;
12903                     this.setValue(this.formatDate(this.date));
12904                     this.update();
12905                     e.preventDefault();
12906                     dateChanged = true;
12907                 }
12908                 break;
12909             case 13: // enter
12910                 this.setValue(this.formatDate(this.date));
12911                 this.hide();
12912                 e.preventDefault();
12913                 break;
12914             case 9: // tab
12915                 this.setValue(this.formatDate(this.date));
12916                 this.hide();
12917                 break;
12918         }
12919     },
12920     
12921     
12922     onClick: function(e) {
12923         e.stopPropagation();
12924         e.preventDefault();
12925         
12926         var target = e.getTarget();
12927         
12928         if(target.nodeName.toLowerCase() === 'i'){
12929             target = Roo.get(target).dom.parentNode;
12930         }
12931         
12932         var nodeName = target.nodeName;
12933         var className = target.className;
12934         var html = target.innerHTML;
12935         
12936         switch(nodeName.toLowerCase()) {
12937             case 'th':
12938                 switch(className) {
12939                     case 'switch':
12940                         this.showMode(1);
12941                         break;
12942                     case 'prev':
12943                     case 'next':
12944                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
12945                         switch(this.viewMode){
12946                                 case 0:
12947                                         this.viewDate = this.moveMonth(this.viewDate, dir);
12948                                         break;
12949                                 case 1:
12950                                 case 2:
12951                                         this.viewDate = this.moveYear(this.viewDate, dir);
12952                                         break;
12953                         }
12954                         this.fill();
12955                         break;
12956                     case 'today':
12957                         var date = new Date();
12958                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
12959                         this.fill()
12960                         this.setValue(this.formatDate(this.date));
12961                         this.hide();
12962                         break;
12963                 }
12964                 break;
12965             case 'span':
12966                 if (className.indexOf('disabled') === -1) {
12967                     this.viewDate.setUTCDate(1);
12968                     if (className.indexOf('month') !== -1) {
12969                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
12970                     } else {
12971                         var year = parseInt(html, 10) || 0;
12972                         this.viewDate.setUTCFullYear(year);
12973                         
12974                     }
12975                     this.showMode(-1);
12976                     this.fill();
12977                 }
12978                 break;
12979                 
12980             case 'td':
12981                 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
12982                     var day = parseInt(html, 10) || 1;
12983                     var year = this.viewDate.getUTCFullYear(),
12984                         month = this.viewDate.getUTCMonth();
12985
12986                     if (className.indexOf('old') !== -1) {
12987                         if(month === 0 ){
12988                             month = 11;
12989                             year -= 1;
12990                         }else{
12991                             month -= 1;
12992                         }
12993                     } else if (className.indexOf('new') !== -1) {
12994                         if (month == 11) {
12995                             month = 0;
12996                             year += 1;
12997                         } else {
12998                             month += 1;
12999                         }
13000                     }
13001                     this.date = this.UTCDate(year, month, day,0,0,0,0);
13002                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
13003                     this.fill();
13004                     this.setValue(this.formatDate(this.date));
13005                     this.hide();
13006                 }
13007                 break;
13008         }
13009     },
13010     
13011     setStartDate: function(startDate){
13012         this.startDate = startDate || -Infinity;
13013         if (this.startDate !== -Infinity) {
13014             this.startDate = this.parseDate(this.startDate);
13015         }
13016         this.update();
13017         this.updateNavArrows();
13018     },
13019
13020     setEndDate: function(endDate){
13021         this.endDate = endDate || Infinity;
13022         if (this.endDate !== Infinity) {
13023             this.endDate = this.parseDate(this.endDate);
13024         }
13025         this.update();
13026         this.updateNavArrows();
13027     },
13028     
13029     setDaysOfWeekDisabled: function(daysOfWeekDisabled){
13030         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
13031         if (typeof(this.daysOfWeekDisabled) !== 'object') {
13032             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
13033         }
13034         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
13035             return parseInt(d, 10);
13036         });
13037         this.update();
13038         this.updateNavArrows();
13039     },
13040     
13041     updateNavArrows: function() {
13042         var d = new Date(this.viewDate),
13043         year = d.getUTCFullYear(),
13044         month = d.getUTCMonth();
13045         
13046         Roo.each(this.picker().select('.prev', true).elements, function(v){
13047             v.show();
13048             switch (this.viewMode) {
13049                 case 0:
13050
13051                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
13052                         v.hide();
13053                     }
13054                     break;
13055                 case 1:
13056                 case 2:
13057                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
13058                         v.hide();
13059                     }
13060                     break;
13061             }
13062         });
13063         
13064         Roo.each(this.picker().select('.next', true).elements, function(v){
13065             v.show();
13066             switch (this.viewMode) {
13067                 case 0:
13068
13069                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
13070                         v.hide();
13071                     }
13072                     break;
13073                 case 1:
13074                 case 2:
13075                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
13076                         v.hide();
13077                     }
13078                     break;
13079             }
13080         })
13081     },
13082     
13083     moveMonth: function(date, dir){
13084         if (!dir) return date;
13085         var new_date = new Date(date.valueOf()),
13086         day = new_date.getUTCDate(),
13087         month = new_date.getUTCMonth(),
13088         mag = Math.abs(dir),
13089         new_month, test;
13090         dir = dir > 0 ? 1 : -1;
13091         if (mag == 1){
13092             test = dir == -1
13093             // If going back one month, make sure month is not current month
13094             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
13095             ? function(){
13096                 return new_date.getUTCMonth() == month;
13097             }
13098             // If going forward one month, make sure month is as expected
13099             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
13100             : function(){
13101                 return new_date.getUTCMonth() != new_month;
13102             };
13103             new_month = month + dir;
13104             new_date.setUTCMonth(new_month);
13105             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
13106             if (new_month < 0 || new_month > 11)
13107                 new_month = (new_month + 12) % 12;
13108         } else {
13109             // For magnitudes >1, move one month at a time...
13110             for (var i=0; i<mag; i++)
13111                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
13112                 new_date = this.moveMonth(new_date, dir);
13113             // ...then reset the day, keeping it in the new month
13114             new_month = new_date.getUTCMonth();
13115             new_date.setUTCDate(day);
13116             test = function(){
13117                 return new_month != new_date.getUTCMonth();
13118             };
13119         }
13120         // Common date-resetting loop -- if date is beyond end of month, make it
13121         // end of month
13122         while (test()){
13123             new_date.setUTCDate(--day);
13124             new_date.setUTCMonth(new_month);
13125         }
13126         return new_date;
13127     },
13128
13129     moveYear: function(date, dir){
13130         return this.moveMonth(date, dir*12);
13131     },
13132
13133     dateWithinRange: function(date){
13134         return date >= this.startDate && date <= this.endDate;
13135     },
13136
13137     
13138     remove: function() {
13139         this.picker().remove();
13140     }
13141    
13142 });
13143
13144 Roo.apply(Roo.bootstrap.DateField,  {
13145     
13146     head : {
13147         tag: 'thead',
13148         cn: [
13149         {
13150             tag: 'tr',
13151             cn: [
13152             {
13153                 tag: 'th',
13154                 cls: 'prev',
13155                 html: '<i class="icon-arrow-left"/>'
13156             },
13157             {
13158                 tag: 'th',
13159                 cls: 'switch',
13160                 colspan: '5'
13161             },
13162             {
13163                 tag: 'th',
13164                 cls: 'next',
13165                 html: '<i class="icon-arrow-right"/>'
13166             }
13167
13168             ]
13169         }
13170         ]
13171     },
13172     
13173     content : {
13174         tag: 'tbody',
13175         cn: [
13176         {
13177             tag: 'tr',
13178             cn: [
13179             {
13180                 tag: 'td',
13181                 colspan: '7'
13182             }
13183             ]
13184         }
13185         ]
13186     },
13187     
13188     footer : {
13189         tag: 'tfoot',
13190         cn: [
13191         {
13192             tag: 'tr',
13193             cn: [
13194             {
13195                 tag: 'th',
13196                 colspan: '7',
13197                 cls: 'today'
13198             }
13199                     
13200             ]
13201         }
13202         ]
13203     },
13204     
13205     dates:{
13206         en: {
13207             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
13208             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
13209             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
13210             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
13211             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
13212             today: "Today"
13213         }
13214     },
13215     
13216     modes: [
13217     {
13218         clsName: 'days',
13219         navFnc: 'Month',
13220         navStep: 1
13221     },
13222     {
13223         clsName: 'months',
13224         navFnc: 'FullYear',
13225         navStep: 1
13226     },
13227     {
13228         clsName: 'years',
13229         navFnc: 'FullYear',
13230         navStep: 10
13231     }]
13232 });
13233
13234 Roo.apply(Roo.bootstrap.DateField,  {
13235   
13236     template : {
13237         tag: 'div',
13238         cls: 'datepicker dropdown-menu',
13239         cn: [
13240         {
13241             tag: 'div',
13242             cls: 'datepicker-days',
13243             cn: [
13244             {
13245                 tag: 'table',
13246                 cls: 'table-condensed',
13247                 cn:[
13248                 Roo.bootstrap.DateField.head,
13249                 {
13250                     tag: 'tbody'
13251                 },
13252                 Roo.bootstrap.DateField.footer
13253                 ]
13254             }
13255             ]
13256         },
13257         {
13258             tag: 'div',
13259             cls: 'datepicker-months',
13260             cn: [
13261             {
13262                 tag: 'table',
13263                 cls: 'table-condensed',
13264                 cn:[
13265                 Roo.bootstrap.DateField.head,
13266                 Roo.bootstrap.DateField.content,
13267                 Roo.bootstrap.DateField.footer
13268                 ]
13269             }
13270             ]
13271         },
13272         {
13273             tag: 'div',
13274             cls: 'datepicker-years',
13275             cn: [
13276             {
13277                 tag: 'table',
13278                 cls: 'table-condensed',
13279                 cn:[
13280                 Roo.bootstrap.DateField.head,
13281                 Roo.bootstrap.DateField.content,
13282                 Roo.bootstrap.DateField.footer
13283                 ]
13284             }
13285             ]
13286         }
13287         ]
13288     }
13289 });
13290
13291  
13292
13293  /*
13294  * - LGPL
13295  *
13296  * TimeField
13297  * 
13298  */
13299
13300 /**
13301  * @class Roo.bootstrap.TimeField
13302  * @extends Roo.bootstrap.Input
13303  * Bootstrap DateField class
13304  * 
13305  * 
13306  * @constructor
13307  * Create a new TimeField
13308  * @param {Object} config The config object
13309  */
13310
13311 Roo.bootstrap.TimeField = function(config){
13312     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
13313     this.addEvents({
13314             /**
13315              * @event show
13316              * Fires when this field show.
13317              * @param {Roo.bootstrap.DateField} this
13318              * @param {Mixed} date The date value
13319              */
13320             show : true,
13321             /**
13322              * @event show
13323              * Fires when this field hide.
13324              * @param {Roo.bootstrap.DateField} this
13325              * @param {Mixed} date The date value
13326              */
13327             hide : true,
13328             /**
13329              * @event select
13330              * Fires when select a date.
13331              * @param {Roo.bootstrap.DateField} this
13332              * @param {Mixed} date The date value
13333              */
13334             select : true
13335         });
13336 };
13337
13338 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
13339     
13340     /**
13341      * @cfg {String} format
13342      * The default time format string which can be overriden for localization support.  The format must be
13343      * valid according to {@link Date#parseDate} (defaults to 'H:i').
13344      */
13345     format : "H:i",
13346        
13347     onRender: function(ct, position)
13348     {
13349         
13350         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
13351                 
13352         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
13353         
13354         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13355         
13356         this.pop = this.picker().select('>.datepicker-time',true).first();
13357         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block' 
13358         
13359         this.picker().on('mousedown', this.onMousedown, this);
13360         this.picker().on('click', this.onClick, this);
13361         
13362         this.picker().addClass('datepicker-dropdown');
13363     
13364         this.fillTime();
13365         this.update();
13366             
13367         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
13368         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
13369         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
13370         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
13371         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
13372         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
13373
13374     },
13375     
13376     fireKey: function(e){
13377         if (!this.picker().isVisible()){
13378             if (e.keyCode == 27) // allow escape to hide and re-show picker
13379                 this.show();
13380             return;
13381         }
13382
13383         e.preventDefault();
13384         
13385         switch(e.keyCode){
13386             case 27: // escape
13387                 this.hide();
13388                 break;
13389             case 37: // left
13390             case 39: // right
13391                 this.onTogglePeriod();
13392                 break;
13393             case 38: // up
13394                 this.onIncrementMinutes();
13395                 break;
13396             case 40: // down
13397                 this.onDecrementMinutes();
13398                 break;
13399             case 13: // enter
13400             case 9: // tab
13401                 this.setTime();
13402                 break;
13403         }
13404     },
13405     
13406     onClick: function(e) {
13407         e.stopPropagation();
13408         e.preventDefault();
13409     },
13410     
13411     picker : function()
13412     {
13413         return this.el.select('.datepicker', true).first();
13414     },
13415     
13416     fillTime: function()
13417     {    
13418         var time = this.pop.select('tbody', true).first();
13419         
13420         time.dom.innerHTML = '';
13421         
13422         time.createChild({
13423             tag: 'tr',
13424             cn: [
13425                 {
13426                     tag: 'td',
13427                     cn: [
13428                         {
13429                             tag: 'a',
13430                             href: '#',
13431                             cls: 'btn',
13432                             cn: [
13433                                 {
13434                                     tag: 'span',
13435                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
13436                                 }
13437                             ]
13438                         } 
13439                     ]
13440                 },
13441                 {
13442                     tag: 'td',
13443                     cls: 'separator'
13444                 },
13445                 {
13446                     tag: 'td',
13447                     cn: [
13448                         {
13449                             tag: 'a',
13450                             href: '#',
13451                             cls: 'btn',
13452                             cn: [
13453                                 {
13454                                     tag: 'span',
13455                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
13456                                 }
13457                             ]
13458                         }
13459                     ]
13460                 },
13461                 {
13462                     tag: 'td',
13463                     cls: 'separator'
13464                 }
13465             ]
13466         });
13467         
13468         time.createChild({
13469             tag: 'tr',
13470             cn: [
13471                 {
13472                     tag: 'td',
13473                     cn: [
13474                         {
13475                             tag: 'span',
13476                             cls: 'timepicker-hour',
13477                             html: '00'
13478                         }  
13479                     ]
13480                 },
13481                 {
13482                     tag: 'td',
13483                     cls: 'separator',
13484                     html: ':'
13485                 },
13486                 {
13487                     tag: 'td',
13488                     cn: [
13489                         {
13490                             tag: 'span',
13491                             cls: 'timepicker-minute',
13492                             html: '00'
13493                         }  
13494                     ]
13495                 },
13496                 {
13497                     tag: 'td',
13498                     cls: 'separator'
13499                 },
13500                 {
13501                     tag: 'td',
13502                     cn: [
13503                         {
13504                             tag: 'button',
13505                             type: 'button',
13506                             cls: 'btn btn-primary period',
13507                             html: 'AM'
13508                             
13509                         }
13510                     ]
13511                 }
13512             ]
13513         });
13514         
13515         time.createChild({
13516             tag: 'tr',
13517             cn: [
13518                 {
13519                     tag: 'td',
13520                     cn: [
13521                         {
13522                             tag: 'a',
13523                             href: '#',
13524                             cls: 'btn',
13525                             cn: [
13526                                 {
13527                                     tag: 'span',
13528                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
13529                                 }
13530                             ]
13531                         }
13532                     ]
13533                 },
13534                 {
13535                     tag: 'td',
13536                     cls: 'separator'
13537                 },
13538                 {
13539                     tag: 'td',
13540                     cn: [
13541                         {
13542                             tag: 'a',
13543                             href: '#',
13544                             cls: 'btn',
13545                             cn: [
13546                                 {
13547                                     tag: 'span',
13548                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
13549                                 }
13550                             ]
13551                         }
13552                     ]
13553                 },
13554                 {
13555                     tag: 'td',
13556                     cls: 'separator'
13557                 }
13558             ]
13559         });
13560         
13561     },
13562     
13563     update: function()
13564     {
13565         
13566         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
13567         
13568         this.fill();
13569     },
13570     
13571     fill: function() 
13572     {
13573         var hours = this.time.getHours();
13574         var minutes = this.time.getMinutes();
13575         var period = 'AM';
13576         
13577         if(hours > 11){
13578             period = 'PM';
13579         }
13580         
13581         if(hours == 0){
13582             hours = 12;
13583         }
13584         
13585         
13586         if(hours > 12){
13587             hours = hours - 12;
13588         }
13589         
13590         if(hours < 10){
13591             hours = '0' + hours;
13592         }
13593         
13594         if(minutes < 10){
13595             minutes = '0' + minutes;
13596         }
13597         
13598         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
13599         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
13600         this.pop.select('button', true).first().dom.innerHTML = period;
13601         
13602     },
13603     
13604     place: function()
13605     {   
13606         this.picker().removeClass(['bottom', 'top']);
13607         
13608         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
13609             /*
13610              * place to the top of element!
13611              *
13612              */
13613             
13614             this.picker().addClass('top');
13615             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
13616             
13617             return;
13618         }
13619         
13620         this.picker().addClass('bottom');
13621         
13622         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
13623     },
13624   
13625     onFocus : function()
13626     {
13627         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
13628         this.show();
13629     },
13630     
13631     onBlur : function()
13632     {
13633         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
13634         this.hide();
13635     },
13636     
13637     show : function()
13638     {
13639         this.picker().show();
13640         this.pop.show();
13641         this.update();
13642         this.place();
13643         
13644         this.fireEvent('show', this, this.date);
13645     },
13646     
13647     hide : function()
13648     {
13649         this.picker().hide();
13650         this.pop.hide();
13651         
13652         this.fireEvent('hide', this, this.date);
13653     },
13654     
13655     setTime : function()
13656     {
13657         this.hide();
13658         this.setValue(this.time.format(this.format));
13659         
13660         this.fireEvent('select', this, this.date);
13661         
13662         
13663     },
13664     
13665     onMousedown: function(e){
13666         e.stopPropagation();
13667         e.preventDefault();
13668     },
13669     
13670     onIncrementHours: function()
13671     {
13672         Roo.log('onIncrementHours');
13673         this.time = this.time.add(Date.HOUR, 1);
13674         this.update();
13675         
13676     },
13677     
13678     onDecrementHours: function()
13679     {
13680         Roo.log('onDecrementHours');
13681         this.time = this.time.add(Date.HOUR, -1);
13682         this.update();
13683     },
13684     
13685     onIncrementMinutes: function()
13686     {
13687         Roo.log('onIncrementMinutes');
13688         this.time = this.time.add(Date.MINUTE, 1);
13689         this.update();
13690     },
13691     
13692     onDecrementMinutes: function()
13693     {
13694         Roo.log('onDecrementMinutes');
13695         this.time = this.time.add(Date.MINUTE, -1);
13696         this.update();
13697     },
13698     
13699     onTogglePeriod: function()
13700     {
13701         Roo.log('onTogglePeriod');
13702         this.time = this.time.add(Date.HOUR, 12);
13703         this.update();
13704     }
13705     
13706    
13707 });
13708
13709 Roo.apply(Roo.bootstrap.TimeField,  {
13710     
13711     content : {
13712         tag: 'tbody',
13713         cn: [
13714             {
13715                 tag: 'tr',
13716                 cn: [
13717                 {
13718                     tag: 'td',
13719                     colspan: '7'
13720                 }
13721                 ]
13722             }
13723         ]
13724     },
13725     
13726     footer : {
13727         tag: 'tfoot',
13728         cn: [
13729             {
13730                 tag: 'tr',
13731                 cn: [
13732                 {
13733                     tag: 'th',
13734                     colspan: '7',
13735                     cls: '',
13736                     cn: [
13737                         {
13738                             tag: 'button',
13739                             cls: 'btn btn-info ok',
13740                             html: 'OK'
13741                         }
13742                     ]
13743                 }
13744
13745                 ]
13746             }
13747         ]
13748     }
13749 });
13750
13751 Roo.apply(Roo.bootstrap.TimeField,  {
13752   
13753     template : {
13754         tag: 'div',
13755         cls: 'datepicker dropdown-menu',
13756         cn: [
13757             {
13758                 tag: 'div',
13759                 cls: 'datepicker-time',
13760                 cn: [
13761                 {
13762                     tag: 'table',
13763                     cls: 'table-condensed',
13764                     cn:[
13765                     Roo.bootstrap.TimeField.content,
13766                     Roo.bootstrap.TimeField.footer
13767                     ]
13768                 }
13769                 ]
13770             }
13771         ]
13772     }
13773 });
13774
13775  
13776
13777  /*
13778  * - LGPL
13779  *
13780  * CheckBox
13781  * 
13782  */
13783
13784 /**
13785  * @class Roo.bootstrap.CheckBox
13786  * @extends Roo.bootstrap.Input
13787  * Bootstrap CheckBox class
13788  * 
13789  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
13790  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
13791  * @cfg {String} boxLabel The text that appears beside the checkbox
13792  * @cfg {Boolean} checked initnal the element
13793  * 
13794  * @constructor
13795  * Create a new CheckBox
13796  * @param {Object} config The config object
13797  */
13798
13799 Roo.bootstrap.CheckBox = function(config){
13800     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
13801    
13802         this.addEvents({
13803             /**
13804             * @event check
13805             * Fires when the element is checked or unchecked.
13806             * @param {Roo.bootstrap.CheckBox} this This input
13807             * @param {Boolean} checked The new checked value
13808             */
13809            check : true
13810         });
13811 };
13812
13813 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
13814     
13815     inputType: 'checkbox',
13816     inputValue: 1,
13817     valueOff: 0,
13818     boxLabel: false,
13819     checked: false,
13820     
13821     getAutoCreate : function()
13822     {
13823         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
13824         
13825         var id = Roo.id();
13826         
13827         var cfg = {};
13828         
13829         cfg.cls = 'form-group' //input-group
13830         
13831         var input =  {
13832             tag: 'input',
13833             id : id,
13834             type : this.inputType,
13835             value : (!this.checked) ? this.valueOff : this.inputValue,
13836             cls : 'form-box',
13837             placeholder : this.placeholder || ''
13838             
13839         };
13840         
13841         if (this.disabled) {
13842             input.disabled=true;
13843         }
13844         
13845         if(this.checked){
13846             input.checked = this.checked;
13847         }
13848         
13849         if (this.name) {
13850             input.name = this.name;
13851         }
13852         
13853         if (this.size) {
13854             input.cls += ' input-' + this.size;
13855         }
13856         
13857         var settings=this;
13858         ['xs','sm','md','lg'].map(function(size){
13859             if (settings[size]) {
13860                 cfg.cls += ' col-' + size + '-' + settings[size];
13861             }
13862         });
13863         
13864         var inputblock = input;
13865         
13866         if (this.before || this.after) {
13867             
13868             inputblock = {
13869                 cls : 'input-group',
13870                 cn :  [] 
13871             };
13872             if (this.before) {
13873                 inputblock.cn.push({
13874                     tag :'span',
13875                     cls : 'input-group-addon',
13876                     html : this.before
13877                 });
13878             }
13879             inputblock.cn.push(input);
13880             if (this.after) {
13881                 inputblock.cn.push({
13882                     tag :'span',
13883                     cls : 'input-group-addon',
13884                     html : this.after
13885                 });
13886             }
13887             
13888         };
13889         
13890         if (align ==='left' && this.fieldLabel.length) {
13891                 Roo.log("left and has label");
13892                 cfg.cn = [
13893                     
13894                     {
13895                         tag: 'label',
13896                         'for' :  id,
13897                         cls : 'control-label col-md-' + this.labelWidth,
13898                         html : this.fieldLabel
13899                         
13900                     },
13901                     {
13902                         cls : "col-md-" + (12 - this.labelWidth), 
13903                         cn: [
13904                             inputblock
13905                         ]
13906                     }
13907                     
13908                 ];
13909         } else if ( this.fieldLabel.length) {
13910                 Roo.log(" label");
13911                 cfg.cn = [
13912                    
13913                     {
13914                         tag: this.boxLabel ? 'span' : 'label',
13915                         'for': id,
13916                         cls: 'control-label box-input-label',
13917                         //cls : 'input-group-addon',
13918                         html : this.fieldLabel
13919                         
13920                     },
13921                     
13922                     inputblock
13923                     
13924                 ];
13925
13926         } else {
13927             
13928                    Roo.log(" no label && no align");
13929                 cfg.cn = [
13930                     
13931                         inputblock
13932                     
13933                 ];
13934                 
13935                 
13936         };
13937         
13938         if(this.boxLabel){
13939             cfg.cn.push({
13940                 tag: 'label',
13941                 'for': id,
13942                 cls: 'box-label',
13943                 html: this.boxLabel
13944             })
13945         }
13946         
13947         return cfg;
13948         
13949     },
13950     
13951     /**
13952      * return the real input element.
13953      */
13954     inputEl: function ()
13955     {
13956         return this.el.select('input.form-box',true).first();
13957     },
13958     
13959     label: function()
13960     {
13961         return this.el.select('label.control-label',true).first();
13962     },
13963     
13964     initEvents : function()
13965     {
13966 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
13967         
13968         this.inputEl().on('click', this.onClick,  this);
13969         
13970     },
13971     
13972     onClick : function()
13973     {   
13974         this.setChecked(!this.checked);
13975     },
13976     
13977     setChecked : function(state,suppressEvent)
13978     {
13979         this.checked = state;
13980         
13981         this.inputEl().dom.checked = state;
13982         
13983         if(suppressEvent !== true){
13984             this.fireEvent('check', this, state);
13985         }
13986         
13987         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
13988         
13989     },
13990     
13991     setValue : function(v,suppressEvent)
13992     {
13993         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
13994     }
13995     
13996 });
13997
13998  
13999 /*
14000  * - LGPL
14001  *
14002  * Radio
14003  * 
14004  */
14005
14006 /**
14007  * @class Roo.bootstrap.Radio
14008  * @extends Roo.bootstrap.CheckBox
14009  * Bootstrap Radio class
14010
14011  * @constructor
14012  * Create a new Radio
14013  * @param {Object} config The config object
14014  */
14015
14016 Roo.bootstrap.Radio = function(config){
14017     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
14018    
14019 };
14020
14021 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
14022     
14023     inputType: 'radio',
14024     inputValue: '',
14025     valueOff: '',
14026     
14027     getAutoCreate : function()
14028     {
14029         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
14030         
14031         var id = Roo.id();
14032         
14033         var cfg = {};
14034         
14035         cfg.cls = 'form-group' //input-group
14036         
14037         var input =  {
14038             tag: 'input',
14039             id : id,
14040             type : this.inputType,
14041             value : (!this.checked) ? this.valueOff : this.inputValue,
14042             cls : 'form-box',
14043             placeholder : this.placeholder || ''
14044             
14045         };
14046         
14047         if (this.disabled) {
14048             input.disabled=true;
14049         }
14050         
14051         if(this.checked){
14052             input.checked = this.checked;
14053         }
14054         
14055         if (this.name) {
14056             input.name = this.name;
14057         }
14058         
14059         if (this.size) {
14060             input.cls += ' input-' + this.size;
14061         }
14062         
14063         var settings=this;
14064         ['xs','sm','md','lg'].map(function(size){
14065             if (settings[size]) {
14066                 cfg.cls += ' col-' + size + '-' + settings[size];
14067             }
14068         });
14069         
14070         var inputblock = input;
14071         
14072         if (this.before || this.after) {
14073             
14074             inputblock = {
14075                 cls : 'input-group',
14076                 cn :  [] 
14077             };
14078             if (this.before) {
14079                 inputblock.cn.push({
14080                     tag :'span',
14081                     cls : 'input-group-addon',
14082                     html : this.before
14083                 });
14084             }
14085             inputblock.cn.push(input);
14086             if (this.after) {
14087                 inputblock.cn.push({
14088                     tag :'span',
14089                     cls : 'input-group-addon',
14090                     html : this.after
14091                 });
14092             }
14093             
14094         };
14095         
14096         if (align ==='left' && this.fieldLabel.length) {
14097                 Roo.log("left and has label");
14098                 cfg.cn = [
14099                     
14100                     {
14101                         tag: 'label',
14102                         'for' :  id,
14103                         cls : 'control-label col-md-' + this.labelWidth,
14104                         html : this.fieldLabel
14105                         
14106                     },
14107                     {
14108                         cls : "col-md-" + (12 - this.labelWidth), 
14109                         cn: [
14110                             inputblock
14111                         ]
14112                     }
14113                     
14114                 ];
14115         } else if ( this.fieldLabel.length) {
14116                 Roo.log(" label");
14117                  cfg.cn = [
14118                    
14119                     {
14120                         tag: 'label',
14121                         'for': id,
14122                         cls: 'control-label box-input-label',
14123                         //cls : 'input-group-addon',
14124                         html : this.fieldLabel
14125                         
14126                     },
14127                     
14128                     inputblock
14129                     
14130                 ];
14131
14132         } else {
14133             
14134                    Roo.log(" no label && no align");
14135                 cfg.cn = [
14136                     
14137                         inputblock
14138                     
14139                 ];
14140                 
14141                 
14142         };
14143         
14144         if(this.boxLabel){
14145             cfg.cn.push({
14146                 tag: 'label',
14147                 'for': id,
14148                 cls: 'box-label',
14149                 html: this.boxLabel
14150             })
14151         }
14152         
14153         return cfg;
14154         
14155     },
14156    
14157     onClick : function()
14158     {   
14159         this.setChecked(true);
14160     },
14161     
14162     setChecked : function(state,suppressEvent)
14163     {
14164         if(state){
14165             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
14166                 v.dom.checked = false;
14167             });
14168         }
14169         
14170         this.checked = state;
14171         this.inputEl().dom.checked = state;
14172         
14173         if(suppressEvent !== true){
14174             this.fireEvent('check', this, state);
14175         }
14176         
14177         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
14178         
14179     },
14180     
14181     getGroupValue : function()
14182     {
14183         var value = ''
14184         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
14185             if(v.dom.checked == true){
14186                 value = v.dom.value;
14187             }
14188         });
14189         
14190         return value;
14191     },
14192     
14193     /**
14194      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
14195      * @return {Mixed} value The field value
14196      */
14197     getValue : function(){
14198         return this.getGroupValue();
14199     }
14200     
14201 });
14202
14203  
14204 //<script type="text/javascript">
14205
14206 /*
14207  * Based  Ext JS Library 1.1.1
14208  * Copyright(c) 2006-2007, Ext JS, LLC.
14209  * LGPL
14210  *
14211  */
14212  
14213 /**
14214  * @class Roo.HtmlEditorCore
14215  * @extends Roo.Component
14216  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
14217  *
14218  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
14219  */
14220
14221 Roo.HtmlEditorCore = function(config){
14222     
14223     
14224     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
14225     this.addEvents({
14226         /**
14227          * @event initialize
14228          * Fires when the editor is fully initialized (including the iframe)
14229          * @param {Roo.HtmlEditorCore} this
14230          */
14231         initialize: true,
14232         /**
14233          * @event activate
14234          * Fires when the editor is first receives the focus. Any insertion must wait
14235          * until after this event.
14236          * @param {Roo.HtmlEditorCore} this
14237          */
14238         activate: true,
14239          /**
14240          * @event beforesync
14241          * Fires before the textarea is updated with content from the editor iframe. Return false
14242          * to cancel the sync.
14243          * @param {Roo.HtmlEditorCore} this
14244          * @param {String} html
14245          */
14246         beforesync: true,
14247          /**
14248          * @event beforepush
14249          * Fires before the iframe editor is updated with content from the textarea. Return false
14250          * to cancel the push.
14251          * @param {Roo.HtmlEditorCore} this
14252          * @param {String} html
14253          */
14254         beforepush: true,
14255          /**
14256          * @event sync
14257          * Fires when the textarea is updated with content from the editor iframe.
14258          * @param {Roo.HtmlEditorCore} this
14259          * @param {String} html
14260          */
14261         sync: true,
14262          /**
14263          * @event push
14264          * Fires when the iframe editor is updated with content from the textarea.
14265          * @param {Roo.HtmlEditorCore} this
14266          * @param {String} html
14267          */
14268         push: true,
14269         
14270         /**
14271          * @event editorevent
14272          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
14273          * @param {Roo.HtmlEditorCore} this
14274          */
14275         editorevent: true
14276     });
14277      
14278 };
14279
14280
14281 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
14282
14283
14284      /**
14285      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
14286      */
14287     
14288     owner : false,
14289     
14290      /**
14291      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
14292      *                        Roo.resizable.
14293      */
14294     resizable : false,
14295      /**
14296      * @cfg {Number} height (in pixels)
14297      */   
14298     height: 300,
14299    /**
14300      * @cfg {Number} width (in pixels)
14301      */   
14302     width: 500,
14303     
14304     /**
14305      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
14306      * 
14307      */
14308     stylesheets: false,
14309     
14310     // id of frame..
14311     frameId: false,
14312     
14313     // private properties
14314     validationEvent : false,
14315     deferHeight: true,
14316     initialized : false,
14317     activated : false,
14318     sourceEditMode : false,
14319     onFocus : Roo.emptyFn,
14320     iframePad:3,
14321     hideMode:'offsets',
14322     
14323     clearUp: true,
14324     
14325      
14326     
14327
14328     /**
14329      * Protected method that will not generally be called directly. It
14330      * is called when the editor initializes the iframe with HTML contents. Override this method if you
14331      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
14332      */
14333     getDocMarkup : function(){
14334         // body styles..
14335         var st = '';
14336         Roo.log(this.stylesheets);
14337         
14338         // inherit styels from page...?? 
14339         if (this.stylesheets === false) {
14340             
14341             Roo.get(document.head).select('style').each(function(node) {
14342                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
14343             });
14344             
14345             Roo.get(document.head).select('link').each(function(node) { 
14346                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
14347             });
14348             
14349         } else if (!this.stylesheets.length) {
14350                 // simple..
14351                 st = '<style type="text/css">' +
14352                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
14353                    '</style>';
14354         } else {
14355             Roo.each(this.stylesheets, function(s) {
14356                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
14357             });
14358             
14359         }
14360         
14361         st +=  '<style type="text/css">' +
14362             'IMG { cursor: pointer } ' +
14363         '</style>';
14364
14365         
14366         return '<html><head>' + st  +
14367             //<style type="text/css">' +
14368             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
14369             //'</style>' +
14370             ' </head><body class="roo-htmleditor-body"></body></html>';
14371     },
14372
14373     // private
14374     onRender : function(ct, position)
14375     {
14376         var _t = this;
14377         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
14378         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
14379         
14380         
14381         this.el.dom.style.border = '0 none';
14382         this.el.dom.setAttribute('tabIndex', -1);
14383         this.el.addClass('x-hidden hide');
14384         
14385         
14386         
14387         if(Roo.isIE){ // fix IE 1px bogus margin
14388             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
14389         }
14390        
14391         
14392         this.frameId = Roo.id();
14393         
14394          
14395         
14396         var iframe = this.owner.wrap.createChild({
14397             tag: 'iframe',
14398             cls: 'form-control', // bootstrap..
14399             id: this.frameId,
14400             name: this.frameId,
14401             frameBorder : 'no',
14402             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
14403         }, this.el
14404         );
14405         
14406         
14407         this.iframe = iframe.dom;
14408
14409          this.assignDocWin();
14410         
14411         this.doc.designMode = 'on';
14412        
14413         this.doc.open();
14414         this.doc.write(this.getDocMarkup());
14415         this.doc.close();
14416
14417         
14418         var task = { // must defer to wait for browser to be ready
14419             run : function(){
14420                 //console.log("run task?" + this.doc.readyState);
14421                 this.assignDocWin();
14422                 if(this.doc.body || this.doc.readyState == 'complete'){
14423                     try {
14424                         this.doc.designMode="on";
14425                     } catch (e) {
14426                         return;
14427                     }
14428                     Roo.TaskMgr.stop(task);
14429                     this.initEditor.defer(10, this);
14430                 }
14431             },
14432             interval : 10,
14433             duration: 10000,
14434             scope: this
14435         };
14436         Roo.TaskMgr.start(task);
14437
14438         
14439          
14440     },
14441
14442     // private
14443     onResize : function(w, h)
14444     {
14445          Roo.log('resize: ' +w + ',' + h );
14446         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
14447         if(!this.iframe){
14448             return;
14449         }
14450         if(typeof w == 'number'){
14451             
14452             this.iframe.style.width = w + 'px';
14453         }
14454         if(typeof h == 'number'){
14455             
14456             this.iframe.style.height = h + 'px';
14457             if(this.doc){
14458                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
14459             }
14460         }
14461         
14462     },
14463
14464     /**
14465      * Toggles the editor between standard and source edit mode.
14466      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
14467      */
14468     toggleSourceEdit : function(sourceEditMode){
14469         
14470         this.sourceEditMode = sourceEditMode === true;
14471         
14472         if(this.sourceEditMode){
14473  
14474             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
14475             
14476         }else{
14477             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
14478             //this.iframe.className = '';
14479             this.deferFocus();
14480         }
14481         //this.setSize(this.owner.wrap.getSize());
14482         //this.fireEvent('editmodechange', this, this.sourceEditMode);
14483     },
14484
14485     
14486   
14487
14488     /**
14489      * Protected method that will not generally be called directly. If you need/want
14490      * custom HTML cleanup, this is the method you should override.
14491      * @param {String} html The HTML to be cleaned
14492      * return {String} The cleaned HTML
14493      */
14494     cleanHtml : function(html){
14495         html = String(html);
14496         if(html.length > 5){
14497             if(Roo.isSafari){ // strip safari nonsense
14498                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
14499             }
14500         }
14501         if(html == '&nbsp;'){
14502             html = '';
14503         }
14504         return html;
14505     },
14506
14507     /**
14508      * HTML Editor -> Textarea
14509      * Protected method that will not generally be called directly. Syncs the contents
14510      * of the editor iframe with the textarea.
14511      */
14512     syncValue : function(){
14513         if(this.initialized){
14514             var bd = (this.doc.body || this.doc.documentElement);
14515             //this.cleanUpPaste(); -- this is done else where and causes havoc..
14516             var html = bd.innerHTML;
14517             if(Roo.isSafari){
14518                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
14519                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
14520                 if(m && m[1]){
14521                     html = '<div style="'+m[0]+'">' + html + '</div>';
14522                 }
14523             }
14524             html = this.cleanHtml(html);
14525             // fix up the special chars.. normaly like back quotes in word...
14526             // however we do not want to do this with chinese..
14527             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
14528                 var cc = b.charCodeAt();
14529                 if (
14530                     (cc >= 0x4E00 && cc < 0xA000 ) ||
14531                     (cc >= 0x3400 && cc < 0x4E00 ) ||
14532                     (cc >= 0xf900 && cc < 0xfb00 )
14533                 ) {
14534                         return b;
14535                 }
14536                 return "&#"+cc+";" 
14537             });
14538             if(this.owner.fireEvent('beforesync', this, html) !== false){
14539                 this.el.dom.value = html;
14540                 this.owner.fireEvent('sync', this, html);
14541             }
14542         }
14543     },
14544
14545     /**
14546      * Protected method that will not generally be called directly. Pushes the value of the textarea
14547      * into the iframe editor.
14548      */
14549     pushValue : function(){
14550         if(this.initialized){
14551             var v = this.el.dom.value.trim();
14552             
14553 //            if(v.length < 1){
14554 //                v = '&#160;';
14555 //            }
14556             
14557             if(this.owner.fireEvent('beforepush', this, v) !== false){
14558                 var d = (this.doc.body || this.doc.documentElement);
14559                 d.innerHTML = v;
14560                 this.cleanUpPaste();
14561                 this.el.dom.value = d.innerHTML;
14562                 this.owner.fireEvent('push', this, v);
14563             }
14564         }
14565     },
14566
14567     // private
14568     deferFocus : function(){
14569         this.focus.defer(10, this);
14570     },
14571
14572     // doc'ed in Field
14573     focus : function(){
14574         if(this.win && !this.sourceEditMode){
14575             this.win.focus();
14576         }else{
14577             this.el.focus();
14578         }
14579     },
14580     
14581     assignDocWin: function()
14582     {
14583         var iframe = this.iframe;
14584         
14585          if(Roo.isIE){
14586             this.doc = iframe.contentWindow.document;
14587             this.win = iframe.contentWindow;
14588         } else {
14589             if (!Roo.get(this.frameId)) {
14590                 return;
14591             }
14592             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
14593             this.win = Roo.get(this.frameId).dom.contentWindow;
14594         }
14595     },
14596     
14597     // private
14598     initEditor : function(){
14599         //console.log("INIT EDITOR");
14600         this.assignDocWin();
14601         
14602         
14603         
14604         this.doc.designMode="on";
14605         this.doc.open();
14606         this.doc.write(this.getDocMarkup());
14607         this.doc.close();
14608         
14609         var dbody = (this.doc.body || this.doc.documentElement);
14610         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
14611         // this copies styles from the containing element into thsi one..
14612         // not sure why we need all of this..
14613         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
14614         ss['background-attachment'] = 'fixed'; // w3c
14615         dbody.bgProperties = 'fixed'; // ie
14616         Roo.DomHelper.applyStyles(dbody, ss);
14617         Roo.EventManager.on(this.doc, {
14618             //'mousedown': this.onEditorEvent,
14619             'mouseup': this.onEditorEvent,
14620             'dblclick': this.onEditorEvent,
14621             'click': this.onEditorEvent,
14622             'keyup': this.onEditorEvent,
14623             buffer:100,
14624             scope: this
14625         });
14626         if(Roo.isGecko){
14627             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
14628         }
14629         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
14630             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
14631         }
14632         this.initialized = true;
14633
14634         this.owner.fireEvent('initialize', this);
14635         this.pushValue();
14636     },
14637
14638     // private
14639     onDestroy : function(){
14640         
14641         
14642         
14643         if(this.rendered){
14644             
14645             //for (var i =0; i < this.toolbars.length;i++) {
14646             //    // fixme - ask toolbars for heights?
14647             //    this.toolbars[i].onDestroy();
14648            // }
14649             
14650             //this.wrap.dom.innerHTML = '';
14651             //this.wrap.remove();
14652         }
14653     },
14654
14655     // private
14656     onFirstFocus : function(){
14657         
14658         this.assignDocWin();
14659         
14660         
14661         this.activated = true;
14662          
14663     
14664         if(Roo.isGecko){ // prevent silly gecko errors
14665             this.win.focus();
14666             var s = this.win.getSelection();
14667             if(!s.focusNode || s.focusNode.nodeType != 3){
14668                 var r = s.getRangeAt(0);
14669                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
14670                 r.collapse(true);
14671                 this.deferFocus();
14672             }
14673             try{
14674                 this.execCmd('useCSS', true);
14675                 this.execCmd('styleWithCSS', false);
14676             }catch(e){}
14677         }
14678         this.owner.fireEvent('activate', this);
14679     },
14680
14681     // private
14682     adjustFont: function(btn){
14683         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
14684         //if(Roo.isSafari){ // safari
14685         //    adjust *= 2;
14686        // }
14687         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
14688         if(Roo.isSafari){ // safari
14689             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
14690             v =  (v < 10) ? 10 : v;
14691             v =  (v > 48) ? 48 : v;
14692             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
14693             
14694         }
14695         
14696         
14697         v = Math.max(1, v+adjust);
14698         
14699         this.execCmd('FontSize', v  );
14700     },
14701
14702     onEditorEvent : function(e){
14703         this.owner.fireEvent('editorevent', this, e);
14704       //  this.updateToolbar();
14705         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
14706     },
14707
14708     insertTag : function(tg)
14709     {
14710         // could be a bit smarter... -> wrap the current selected tRoo..
14711         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
14712             
14713             range = this.createRange(this.getSelection());
14714             var wrappingNode = this.doc.createElement(tg.toLowerCase());
14715             wrappingNode.appendChild(range.extractContents());
14716             range.insertNode(wrappingNode);
14717
14718             return;
14719             
14720             
14721             
14722         }
14723         this.execCmd("formatblock",   tg);
14724         
14725     },
14726     
14727     insertText : function(txt)
14728     {
14729         
14730         
14731         var range = this.createRange();
14732         range.deleteContents();
14733                //alert(Sender.getAttribute('label'));
14734                
14735         range.insertNode(this.doc.createTextNode(txt));
14736     } ,
14737     
14738      
14739
14740     /**
14741      * Executes a Midas editor command on the editor document and performs necessary focus and
14742      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
14743      * @param {String} cmd The Midas command
14744      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
14745      */
14746     relayCmd : function(cmd, value){
14747         this.win.focus();
14748         this.execCmd(cmd, value);
14749         this.owner.fireEvent('editorevent', this);
14750         //this.updateToolbar();
14751         this.owner.deferFocus();
14752     },
14753
14754     /**
14755      * Executes a Midas editor command directly on the editor document.
14756      * For visual commands, you should use {@link #relayCmd} instead.
14757      * <b>This should only be called after the editor is initialized.</b>
14758      * @param {String} cmd The Midas command
14759      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
14760      */
14761     execCmd : function(cmd, value){
14762         this.doc.execCommand(cmd, false, value === undefined ? null : value);
14763         this.syncValue();
14764     },
14765  
14766  
14767    
14768     /**
14769      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
14770      * to insert tRoo.
14771      * @param {String} text | dom node.. 
14772      */
14773     insertAtCursor : function(text)
14774     {
14775         
14776         
14777         
14778         if(!this.activated){
14779             return;
14780         }
14781         /*
14782         if(Roo.isIE){
14783             this.win.focus();
14784             var r = this.doc.selection.createRange();
14785             if(r){
14786                 r.collapse(true);
14787                 r.pasteHTML(text);
14788                 this.syncValue();
14789                 this.deferFocus();
14790             
14791             }
14792             return;
14793         }
14794         */
14795         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
14796             this.win.focus();
14797             
14798             
14799             // from jquery ui (MIT licenced)
14800             var range, node;
14801             var win = this.win;
14802             
14803             if (win.getSelection && win.getSelection().getRangeAt) {
14804                 range = win.getSelection().getRangeAt(0);
14805                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
14806                 range.insertNode(node);
14807             } else if (win.document.selection && win.document.selection.createRange) {
14808                 // no firefox support
14809                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
14810                 win.document.selection.createRange().pasteHTML(txt);
14811             } else {
14812                 // no firefox support
14813                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
14814                 this.execCmd('InsertHTML', txt);
14815             } 
14816             
14817             this.syncValue();
14818             
14819             this.deferFocus();
14820         }
14821     },
14822  // private
14823     mozKeyPress : function(e){
14824         if(e.ctrlKey){
14825             var c = e.getCharCode(), cmd;
14826           
14827             if(c > 0){
14828                 c = String.fromCharCode(c).toLowerCase();
14829                 switch(c){
14830                     case 'b':
14831                         cmd = 'bold';
14832                         break;
14833                     case 'i':
14834                         cmd = 'italic';
14835                         break;
14836                     
14837                     case 'u':
14838                         cmd = 'underline';
14839                         break;
14840                     
14841                     case 'v':
14842                         this.cleanUpPaste.defer(100, this);
14843                         return;
14844                         
14845                 }
14846                 if(cmd){
14847                     this.win.focus();
14848                     this.execCmd(cmd);
14849                     this.deferFocus();
14850                     e.preventDefault();
14851                 }
14852                 
14853             }
14854         }
14855     },
14856
14857     // private
14858     fixKeys : function(){ // load time branching for fastest keydown performance
14859         if(Roo.isIE){
14860             return function(e){
14861                 var k = e.getKey(), r;
14862                 if(k == e.TAB){
14863                     e.stopEvent();
14864                     r = this.doc.selection.createRange();
14865                     if(r){
14866                         r.collapse(true);
14867                         r.pasteHTML('&#160;&#160;&#160;&#160;');
14868                         this.deferFocus();
14869                     }
14870                     return;
14871                 }
14872                 
14873                 if(k == e.ENTER){
14874                     r = this.doc.selection.createRange();
14875                     if(r){
14876                         var target = r.parentElement();
14877                         if(!target || target.tagName.toLowerCase() != 'li'){
14878                             e.stopEvent();
14879                             r.pasteHTML('<br />');
14880                             r.collapse(false);
14881                             r.select();
14882                         }
14883                     }
14884                 }
14885                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
14886                     this.cleanUpPaste.defer(100, this);
14887                     return;
14888                 }
14889                 
14890                 
14891             };
14892         }else if(Roo.isOpera){
14893             return function(e){
14894                 var k = e.getKey();
14895                 if(k == e.TAB){
14896                     e.stopEvent();
14897                     this.win.focus();
14898                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
14899                     this.deferFocus();
14900                 }
14901                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
14902                     this.cleanUpPaste.defer(100, this);
14903                     return;
14904                 }
14905                 
14906             };
14907         }else if(Roo.isSafari){
14908             return function(e){
14909                 var k = e.getKey();
14910                 
14911                 if(k == e.TAB){
14912                     e.stopEvent();
14913                     this.execCmd('InsertText','\t');
14914                     this.deferFocus();
14915                     return;
14916                 }
14917                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
14918                     this.cleanUpPaste.defer(100, this);
14919                     return;
14920                 }
14921                 
14922              };
14923         }
14924     }(),
14925     
14926     getAllAncestors: function()
14927     {
14928         var p = this.getSelectedNode();
14929         var a = [];
14930         if (!p) {
14931             a.push(p); // push blank onto stack..
14932             p = this.getParentElement();
14933         }
14934         
14935         
14936         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
14937             a.push(p);
14938             p = p.parentNode;
14939         }
14940         a.push(this.doc.body);
14941         return a;
14942     },
14943     lastSel : false,
14944     lastSelNode : false,
14945     
14946     
14947     getSelection : function() 
14948     {
14949         this.assignDocWin();
14950         return Roo.isIE ? this.doc.selection : this.win.getSelection();
14951     },
14952     
14953     getSelectedNode: function() 
14954     {
14955         // this may only work on Gecko!!!
14956         
14957         // should we cache this!!!!
14958         
14959         
14960         
14961          
14962         var range = this.createRange(this.getSelection()).cloneRange();
14963         
14964         if (Roo.isIE) {
14965             var parent = range.parentElement();
14966             while (true) {
14967                 var testRange = range.duplicate();
14968                 testRange.moveToElementText(parent);
14969                 if (testRange.inRange(range)) {
14970                     break;
14971                 }
14972                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
14973                     break;
14974                 }
14975                 parent = parent.parentElement;
14976             }
14977             return parent;
14978         }
14979         
14980         // is ancestor a text element.
14981         var ac =  range.commonAncestorContainer;
14982         if (ac.nodeType == 3) {
14983             ac = ac.parentNode;
14984         }
14985         
14986         var ar = ac.childNodes;
14987          
14988         var nodes = [];
14989         var other_nodes = [];
14990         var has_other_nodes = false;
14991         for (var i=0;i<ar.length;i++) {
14992             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
14993                 continue;
14994             }
14995             // fullly contained node.
14996             
14997             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
14998                 nodes.push(ar[i]);
14999                 continue;
15000             }
15001             
15002             // probably selected..
15003             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
15004                 other_nodes.push(ar[i]);
15005                 continue;
15006             }
15007             // outer..
15008             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
15009                 continue;
15010             }
15011             
15012             
15013             has_other_nodes = true;
15014         }
15015         if (!nodes.length && other_nodes.length) {
15016             nodes= other_nodes;
15017         }
15018         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
15019             return false;
15020         }
15021         
15022         return nodes[0];
15023     },
15024     createRange: function(sel)
15025     {
15026         // this has strange effects when using with 
15027         // top toolbar - not sure if it's a great idea.
15028         //this.editor.contentWindow.focus();
15029         if (typeof sel != "undefined") {
15030             try {
15031                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
15032             } catch(e) {
15033                 return this.doc.createRange();
15034             }
15035         } else {
15036             return this.doc.createRange();
15037         }
15038     },
15039     getParentElement: function()
15040     {
15041         
15042         this.assignDocWin();
15043         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
15044         
15045         var range = this.createRange(sel);
15046          
15047         try {
15048             var p = range.commonAncestorContainer;
15049             while (p.nodeType == 3) { // text node
15050                 p = p.parentNode;
15051             }
15052             return p;
15053         } catch (e) {
15054             return null;
15055         }
15056     
15057     },
15058     /***
15059      *
15060      * Range intersection.. the hard stuff...
15061      *  '-1' = before
15062      *  '0' = hits..
15063      *  '1' = after.
15064      *         [ -- selected range --- ]
15065      *   [fail]                        [fail]
15066      *
15067      *    basically..
15068      *      if end is before start or  hits it. fail.
15069      *      if start is after end or hits it fail.
15070      *
15071      *   if either hits (but other is outside. - then it's not 
15072      *   
15073      *    
15074      **/
15075     
15076     
15077     // @see http://www.thismuchiknow.co.uk/?p=64.
15078     rangeIntersectsNode : function(range, node)
15079     {
15080         var nodeRange = node.ownerDocument.createRange();
15081         try {
15082             nodeRange.selectNode(node);
15083         } catch (e) {
15084             nodeRange.selectNodeContents(node);
15085         }
15086     
15087         var rangeStartRange = range.cloneRange();
15088         rangeStartRange.collapse(true);
15089     
15090         var rangeEndRange = range.cloneRange();
15091         rangeEndRange.collapse(false);
15092     
15093         var nodeStartRange = nodeRange.cloneRange();
15094         nodeStartRange.collapse(true);
15095     
15096         var nodeEndRange = nodeRange.cloneRange();
15097         nodeEndRange.collapse(false);
15098     
15099         return rangeStartRange.compareBoundaryPoints(
15100                  Range.START_TO_START, nodeEndRange) == -1 &&
15101                rangeEndRange.compareBoundaryPoints(
15102                  Range.START_TO_START, nodeStartRange) == 1;
15103         
15104          
15105     },
15106     rangeCompareNode : function(range, node)
15107     {
15108         var nodeRange = node.ownerDocument.createRange();
15109         try {
15110             nodeRange.selectNode(node);
15111         } catch (e) {
15112             nodeRange.selectNodeContents(node);
15113         }
15114         
15115         
15116         range.collapse(true);
15117     
15118         nodeRange.collapse(true);
15119      
15120         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
15121         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
15122          
15123         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
15124         
15125         var nodeIsBefore   =  ss == 1;
15126         var nodeIsAfter    = ee == -1;
15127         
15128         if (nodeIsBefore && nodeIsAfter)
15129             return 0; // outer
15130         if (!nodeIsBefore && nodeIsAfter)
15131             return 1; //right trailed.
15132         
15133         if (nodeIsBefore && !nodeIsAfter)
15134             return 2;  // left trailed.
15135         // fully contined.
15136         return 3;
15137     },
15138
15139     // private? - in a new class?
15140     cleanUpPaste :  function()
15141     {
15142         // cleans up the whole document..
15143         Roo.log('cleanuppaste');
15144         
15145         this.cleanUpChildren(this.doc.body);
15146         var clean = this.cleanWordChars(this.doc.body.innerHTML);
15147         if (clean != this.doc.body.innerHTML) {
15148             this.doc.body.innerHTML = clean;
15149         }
15150         
15151     },
15152     
15153     cleanWordChars : function(input) {// change the chars to hex code
15154         var he = Roo.HtmlEditorCore;
15155         
15156         var output = input;
15157         Roo.each(he.swapCodes, function(sw) { 
15158             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
15159             
15160             output = output.replace(swapper, sw[1]);
15161         });
15162         
15163         return output;
15164     },
15165     
15166     
15167     cleanUpChildren : function (n)
15168     {
15169         if (!n.childNodes.length) {
15170             return;
15171         }
15172         for (var i = n.childNodes.length-1; i > -1 ; i--) {
15173            this.cleanUpChild(n.childNodes[i]);
15174         }
15175     },
15176     
15177     
15178         
15179     
15180     cleanUpChild : function (node)
15181     {
15182         var ed = this;
15183         //console.log(node);
15184         if (node.nodeName == "#text") {
15185             // clean up silly Windows -- stuff?
15186             return; 
15187         }
15188         if (node.nodeName == "#comment") {
15189             node.parentNode.removeChild(node);
15190             // clean up silly Windows -- stuff?
15191             return; 
15192         }
15193         
15194         if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1 && this.clearUp) {
15195             // remove node.
15196             node.parentNode.removeChild(node);
15197             return;
15198             
15199         }
15200         
15201         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
15202         
15203         // remove <a name=....> as rendering on yahoo mailer is borked with this.
15204         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
15205         
15206         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
15207         //    remove_keep_children = true;
15208         //}
15209         
15210         if (remove_keep_children) {
15211             this.cleanUpChildren(node);
15212             // inserts everything just before this node...
15213             while (node.childNodes.length) {
15214                 var cn = node.childNodes[0];
15215                 node.removeChild(cn);
15216                 node.parentNode.insertBefore(cn, node);
15217             }
15218             node.parentNode.removeChild(node);
15219             return;
15220         }
15221         
15222         if (!node.attributes || !node.attributes.length) {
15223             this.cleanUpChildren(node);
15224             return;
15225         }
15226         
15227         function cleanAttr(n,v)
15228         {
15229             
15230             if (v.match(/^\./) || v.match(/^\//)) {
15231                 return;
15232             }
15233             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
15234                 return;
15235             }
15236             if (v.match(/^#/)) {
15237                 return;
15238             }
15239 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
15240             node.removeAttribute(n);
15241             
15242         }
15243         
15244         function cleanStyle(n,v)
15245         {
15246             if (v.match(/expression/)) { //XSS?? should we even bother..
15247                 node.removeAttribute(n);
15248                 return;
15249             }
15250             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
15251             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
15252             
15253             
15254             var parts = v.split(/;/);
15255             var clean = [];
15256             
15257             Roo.each(parts, function(p) {
15258                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
15259                 if (!p.length) {
15260                     return true;
15261                 }
15262                 var l = p.split(':').shift().replace(/\s+/g,'');
15263                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
15264                 
15265                 if ( cblack.indexOf(l) > -1) {
15266 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
15267                     //node.removeAttribute(n);
15268                     return true;
15269                 }
15270                 //Roo.log()
15271                 // only allow 'c whitelisted system attributes'
15272                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
15273 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
15274                     //node.removeAttribute(n);
15275                     return true;
15276                 }
15277                 
15278                 
15279                  
15280                 
15281                 clean.push(p);
15282                 return true;
15283             });
15284             if (clean.length) { 
15285                 node.setAttribute(n, clean.join(';'));
15286             } else {
15287                 node.removeAttribute(n);
15288             }
15289             
15290         }
15291         
15292         
15293         for (var i = node.attributes.length-1; i > -1 ; i--) {
15294             var a = node.attributes[i];
15295             //console.log(a);
15296             
15297             if (a.name.toLowerCase().substr(0,2)=='on')  {
15298                 node.removeAttribute(a.name);
15299                 continue;
15300             }
15301             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
15302                 node.removeAttribute(a.name);
15303                 continue;
15304             }
15305             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
15306                 cleanAttr(a.name,a.value); // fixme..
15307                 continue;
15308             }
15309             if (a.name == 'style') {
15310                 cleanStyle(a.name,a.value);
15311                 continue;
15312             }
15313             /// clean up MS crap..
15314             // tecnically this should be a list of valid class'es..
15315             
15316             
15317             if (a.name == 'class') {
15318                 if (a.value.match(/^Mso/)) {
15319                     node.className = '';
15320                 }
15321                 
15322                 if (a.value.match(/body/)) {
15323                     node.className = '';
15324                 }
15325                 continue;
15326             }
15327             
15328             // style cleanup!?
15329             // class cleanup?
15330             
15331         }
15332         
15333         
15334         this.cleanUpChildren(node);
15335         
15336         
15337     },
15338     /**
15339      * Clean up MS wordisms...
15340      */
15341     cleanWord : function(node)
15342     {
15343         var _t = this;
15344         var cleanWordChildren = function()
15345         {
15346             if (!node.childNodes.length) {
15347                 return;
15348             }
15349             for (var i = node.childNodes.length-1; i > -1 ; i--) {
15350                _t.cleanWord(node.childNodes[i]);
15351             }
15352         }
15353         
15354         
15355         if (!node) {
15356             this.cleanWord(this.doc.body);
15357             return;
15358         }
15359         if (node.nodeName == "#text") {
15360             // clean up silly Windows -- stuff?
15361             return; 
15362         }
15363         if (node.nodeName == "#comment") {
15364             node.parentNode.removeChild(node);
15365             // clean up silly Windows -- stuff?
15366             return; 
15367         }
15368         
15369         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
15370             node.parentNode.removeChild(node);
15371             return;
15372         }
15373         
15374         // remove - but keep children..
15375         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
15376             while (node.childNodes.length) {
15377                 var cn = node.childNodes[0];
15378                 node.removeChild(cn);
15379                 node.parentNode.insertBefore(cn, node);
15380             }
15381             node.parentNode.removeChild(node);
15382             cleanWordChildren();
15383             return;
15384         }
15385         // clean styles
15386         if (node.className.length) {
15387             
15388             var cn = node.className.split(/\W+/);
15389             var cna = [];
15390             Roo.each(cn, function(cls) {
15391                 if (cls.match(/Mso[a-zA-Z]+/)) {
15392                     return;
15393                 }
15394                 cna.push(cls);
15395             });
15396             node.className = cna.length ? cna.join(' ') : '';
15397             if (!cna.length) {
15398                 node.removeAttribute("class");
15399             }
15400         }
15401         
15402         if (node.hasAttribute("lang")) {
15403             node.removeAttribute("lang");
15404         }
15405         
15406         if (node.hasAttribute("style")) {
15407             
15408             var styles = node.getAttribute("style").split(";");
15409             var nstyle = [];
15410             Roo.each(styles, function(s) {
15411                 if (!s.match(/:/)) {
15412                     return;
15413                 }
15414                 var kv = s.split(":");
15415                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
15416                     return;
15417                 }
15418                 // what ever is left... we allow.
15419                 nstyle.push(s);
15420             });
15421             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
15422             if (!nstyle.length) {
15423                 node.removeAttribute('style');
15424             }
15425         }
15426         
15427         cleanWordChildren();
15428         
15429         
15430     },
15431     domToHTML : function(currentElement, depth, nopadtext) {
15432         
15433             depth = depth || 0;
15434             nopadtext = nopadtext || false;
15435         
15436             if (!currentElement) {
15437                 return this.domToHTML(this.doc.body);
15438             }
15439             
15440             //Roo.log(currentElement);
15441             var j;
15442             var allText = false;
15443             var nodeName = currentElement.nodeName;
15444             var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
15445             
15446             if  (nodeName == '#text') {
15447                 return currentElement.nodeValue;
15448             }
15449             
15450             
15451             var ret = '';
15452             if (nodeName != 'BODY') {
15453                  
15454                 var i = 0;
15455                 // Prints the node tagName, such as <A>, <IMG>, etc
15456                 if (tagName) {
15457                     var attr = [];
15458                     for(i = 0; i < currentElement.attributes.length;i++) {
15459                         // quoting?
15460                         var aname = currentElement.attributes.item(i).name;
15461                         if (!currentElement.attributes.item(i).value.length) {
15462                             continue;
15463                         }
15464                         attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
15465                     }
15466                     
15467                     ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
15468                 } 
15469                 else {
15470                     
15471                     // eack
15472                 }
15473             } else {
15474                 tagName = false;
15475             }
15476             if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
15477                 return ret;
15478             }
15479             if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
15480                 nopadtext = true;
15481             }
15482             
15483             
15484             // Traverse the tree
15485             i = 0;
15486             var currentElementChild = currentElement.childNodes.item(i);
15487             var allText = true;
15488             var innerHTML  = '';
15489             lastnode = '';
15490             while (currentElementChild) {
15491                 // Formatting code (indent the tree so it looks nice on the screen)
15492                 var nopad = nopadtext;
15493                 if (lastnode == 'SPAN') {
15494                     nopad  = true;
15495                 }
15496                 // text
15497                 if  (currentElementChild.nodeName == '#text') {
15498                     var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
15499                     if (!nopad && toadd.length > 80) {
15500                         innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
15501                     }
15502                     innerHTML  += toadd;
15503                     
15504                     i++;
15505                     currentElementChild = currentElement.childNodes.item(i);
15506                     lastNode = '';
15507                     continue;
15508                 }
15509                 allText = false;
15510                 
15511                 innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
15512                     
15513                 // Recursively traverse the tree structure of the child node
15514                 innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
15515                 lastnode = currentElementChild.nodeName;
15516                 i++;
15517                 currentElementChild=currentElement.childNodes.item(i);
15518             }
15519             
15520             ret += innerHTML;
15521             
15522             if (!allText) {
15523                     // The remaining code is mostly for formatting the tree
15524                 ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
15525             }
15526             
15527             
15528             if (tagName) {
15529                 ret+= "</"+tagName+">";
15530             }
15531             return ret;
15532             
15533         }
15534     
15535     // hide stuff that is not compatible
15536     /**
15537      * @event blur
15538      * @hide
15539      */
15540     /**
15541      * @event change
15542      * @hide
15543      */
15544     /**
15545      * @event focus
15546      * @hide
15547      */
15548     /**
15549      * @event specialkey
15550      * @hide
15551      */
15552     /**
15553      * @cfg {String} fieldClass @hide
15554      */
15555     /**
15556      * @cfg {String} focusClass @hide
15557      */
15558     /**
15559      * @cfg {String} autoCreate @hide
15560      */
15561     /**
15562      * @cfg {String} inputType @hide
15563      */
15564     /**
15565      * @cfg {String} invalidClass @hide
15566      */
15567     /**
15568      * @cfg {String} invalidText @hide
15569      */
15570     /**
15571      * @cfg {String} msgFx @hide
15572      */
15573     /**
15574      * @cfg {String} validateOnBlur @hide
15575      */
15576 });
15577
15578 Roo.HtmlEditorCore.white = [
15579         'area', 'br', 'img', 'input', 'hr', 'wbr',
15580         
15581        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
15582        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
15583        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
15584        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
15585        'table',   'ul',         'xmp', 
15586        
15587        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
15588       'thead',   'tr', 
15589      
15590       'dir', 'menu', 'ol', 'ul', 'dl',
15591        
15592       'embed',  'object'
15593 ];
15594
15595
15596 Roo.HtmlEditorCore.black = [
15597     //    'embed',  'object', // enable - backend responsiblity to clean thiese
15598         'applet', // 
15599         'base',   'basefont', 'bgsound', 'blink',  'body', 
15600         'frame',  'frameset', 'head',    'html',   'ilayer', 
15601         'iframe', 'layer',  'link',     'meta',    'object',   
15602         'script', 'style' ,'title',  'xml' // clean later..
15603 ];
15604 Roo.HtmlEditorCore.clean = [
15605     'script', 'style', 'title', 'xml'
15606 ];
15607 Roo.HtmlEditorCore.remove = [
15608     'font'
15609 ];
15610 // attributes..
15611
15612 Roo.HtmlEditorCore.ablack = [
15613     'on'
15614 ];
15615     
15616 Roo.HtmlEditorCore.aclean = [ 
15617     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
15618 ];
15619
15620 // protocols..
15621 Roo.HtmlEditorCore.pwhite= [
15622         'http',  'https',  'mailto'
15623 ];
15624
15625 // white listed style attributes.
15626 Roo.HtmlEditorCore.cwhite= [
15627       //  'text-align', /// default is to allow most things..
15628       
15629          
15630 //        'font-size'//??
15631 ];
15632
15633 // black listed style attributes.
15634 Roo.HtmlEditorCore.cblack= [
15635       //  'font-size' -- this can be set by the project 
15636 ];
15637
15638
15639 Roo.HtmlEditorCore.swapCodes   =[ 
15640     [    8211, "--" ], 
15641     [    8212, "--" ], 
15642     [    8216,  "'" ],  
15643     [    8217, "'" ],  
15644     [    8220, '"' ],  
15645     [    8221, '"' ],  
15646     [    8226, "*" ],  
15647     [    8230, "..." ]
15648 ]; 
15649
15650     /*
15651  * - LGPL
15652  *
15653  * HtmlEditor
15654  * 
15655  */
15656
15657 /**
15658  * @class Roo.bootstrap.HtmlEditor
15659  * @extends Roo.bootstrap.TextArea
15660  * Bootstrap HtmlEditor class
15661
15662  * @constructor
15663  * Create a new HtmlEditor
15664  * @param {Object} config The config object
15665  */
15666
15667 Roo.bootstrap.HtmlEditor = function(config){
15668     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
15669     if (!this.toolbars) {
15670         this.toolbars = [];
15671     }
15672     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
15673     this.addEvents({
15674             /**
15675              * @event initialize
15676              * Fires when the editor is fully initialized (including the iframe)
15677              * @param {HtmlEditor} this
15678              */
15679             initialize: true,
15680             /**
15681              * @event activate
15682              * Fires when the editor is first receives the focus. Any insertion must wait
15683              * until after this event.
15684              * @param {HtmlEditor} this
15685              */
15686             activate: true,
15687              /**
15688              * @event beforesync
15689              * Fires before the textarea is updated with content from the editor iframe. Return false
15690              * to cancel the sync.
15691              * @param {HtmlEditor} this
15692              * @param {String} html
15693              */
15694             beforesync: true,
15695              /**
15696              * @event beforepush
15697              * Fires before the iframe editor is updated with content from the textarea. Return false
15698              * to cancel the push.
15699              * @param {HtmlEditor} this
15700              * @param {String} html
15701              */
15702             beforepush: true,
15703              /**
15704              * @event sync
15705              * Fires when the textarea is updated with content from the editor iframe.
15706              * @param {HtmlEditor} this
15707              * @param {String} html
15708              */
15709             sync: true,
15710              /**
15711              * @event push
15712              * Fires when the iframe editor is updated with content from the textarea.
15713              * @param {HtmlEditor} this
15714              * @param {String} html
15715              */
15716             push: true,
15717              /**
15718              * @event editmodechange
15719              * Fires when the editor switches edit modes
15720              * @param {HtmlEditor} this
15721              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
15722              */
15723             editmodechange: true,
15724             /**
15725              * @event editorevent
15726              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
15727              * @param {HtmlEditor} this
15728              */
15729             editorevent: true,
15730             /**
15731              * @event firstfocus
15732              * Fires when on first focus - needed by toolbars..
15733              * @param {HtmlEditor} this
15734              */
15735             firstfocus: true,
15736             /**
15737              * @event autosave
15738              * Auto save the htmlEditor value as a file into Events
15739              * @param {HtmlEditor} this
15740              */
15741             autosave: true,
15742             /**
15743              * @event savedpreview
15744              * preview the saved version of htmlEditor
15745              * @param {HtmlEditor} this
15746              */
15747             savedpreview: true
15748         });
15749 };
15750
15751
15752 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
15753     
15754     
15755       /**
15756      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
15757      */
15758     toolbars : false,
15759    
15760      /**
15761      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
15762      *                        Roo.resizable.
15763      */
15764     resizable : false,
15765      /**
15766      * @cfg {Number} height (in pixels)
15767      */   
15768     height: 300,
15769    /**
15770      * @cfg {Number} width (in pixels)
15771      */   
15772     width: false,
15773     
15774     /**
15775      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
15776      * 
15777      */
15778     stylesheets: false,
15779     
15780     // id of frame..
15781     frameId: false,
15782     
15783     // private properties
15784     validationEvent : false,
15785     deferHeight: true,
15786     initialized : false,
15787     activated : false,
15788     
15789     onFocus : Roo.emptyFn,
15790     iframePad:3,
15791     hideMode:'offsets',
15792     
15793     
15794     tbContainer : false,
15795     
15796     toolbarContainer :function() {
15797         return this.wrap.select('.x-html-editor-tb',true).first();
15798     },
15799
15800     /**
15801      * Protected method that will not generally be called directly. It
15802      * is called when the editor creates its toolbar. Override this method if you need to
15803      * add custom toolbar buttons.
15804      * @param {HtmlEditor} editor
15805      */
15806     createToolbar : function(){
15807         
15808         Roo.log("create toolbars");
15809         
15810         this.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard({editor: this} ) ];
15811         this.toolbars[0].render(this.toolbarContainer());
15812         
15813         return;
15814         
15815 //        if (!editor.toolbars || !editor.toolbars.length) {
15816 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
15817 //        }
15818 //        
15819 //        for (var i =0 ; i < editor.toolbars.length;i++) {
15820 //            editor.toolbars[i] = Roo.factory(
15821 //                    typeof(editor.toolbars[i]) == 'string' ?
15822 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
15823 //                Roo.bootstrap.HtmlEditor);
15824 //            editor.toolbars[i].init(editor);
15825 //        }
15826     },
15827
15828      
15829     // private
15830     onRender : function(ct, position)
15831     {
15832        // Roo.log("Call onRender: " + this.xtype);
15833         var _t = this;
15834         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
15835       
15836         this.wrap = this.inputEl().wrap({
15837             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
15838         });
15839         
15840         this.editorcore.onRender(ct, position);
15841          
15842         if (this.resizable) {
15843             this.resizeEl = new Roo.Resizable(this.wrap, {
15844                 pinned : true,
15845                 wrap: true,
15846                 dynamic : true,
15847                 minHeight : this.height,
15848                 height: this.height,
15849                 handles : this.resizable,
15850                 width: this.width,
15851                 listeners : {
15852                     resize : function(r, w, h) {
15853                         _t.onResize(w,h); // -something
15854                     }
15855                 }
15856             });
15857             
15858         }
15859         this.createToolbar(this);
15860        
15861         
15862         if(!this.width && this.resizable){
15863             this.setSize(this.wrap.getSize());
15864         }
15865         if (this.resizeEl) {
15866             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
15867             // should trigger onReize..
15868         }
15869         
15870     },
15871
15872     // private
15873     onResize : function(w, h)
15874     {
15875         Roo.log('resize: ' +w + ',' + h );
15876         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
15877         var ew = false;
15878         var eh = false;
15879         
15880         if(this.inputEl() ){
15881             if(typeof w == 'number'){
15882                 var aw = w - this.wrap.getFrameWidth('lr');
15883                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
15884                 ew = aw;
15885             }
15886             if(typeof h == 'number'){
15887                  var tbh = -11;  // fixme it needs to tool bar size!
15888                 for (var i =0; i < this.toolbars.length;i++) {
15889                     // fixme - ask toolbars for heights?
15890                     tbh += this.toolbars[i].el.getHeight();
15891                     //if (this.toolbars[i].footer) {
15892                     //    tbh += this.toolbars[i].footer.el.getHeight();
15893                     //}
15894                 }
15895               
15896                 
15897                 
15898                 
15899                 
15900                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
15901                 ah -= 5; // knock a few pixes off for look..
15902                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
15903                 var eh = ah;
15904             }
15905         }
15906         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
15907         this.editorcore.onResize(ew,eh);
15908         
15909     },
15910
15911     /**
15912      * Toggles the editor between standard and source edit mode.
15913      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
15914      */
15915     toggleSourceEdit : function(sourceEditMode)
15916     {
15917         this.editorcore.toggleSourceEdit(sourceEditMode);
15918         
15919         if(this.editorcore.sourceEditMode){
15920             Roo.log('editor - showing textarea');
15921             
15922 //            Roo.log('in');
15923 //            Roo.log(this.syncValue());
15924             this.syncValue();
15925             this.inputEl().removeClass('hide');
15926             this.inputEl().dom.removeAttribute('tabIndex');
15927             this.inputEl().focus();
15928         }else{
15929             Roo.log('editor - hiding textarea');
15930 //            Roo.log('out')
15931 //            Roo.log(this.pushValue()); 
15932             this.pushValue();
15933             
15934             this.inputEl().addClass('hide');
15935             this.inputEl().dom.setAttribute('tabIndex', -1);
15936             //this.deferFocus();
15937         }
15938          
15939         if(this.resizable){
15940             this.setSize(this.wrap.getSize());
15941         }
15942         
15943         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
15944     },
15945  
15946     // private (for BoxComponent)
15947     adjustSize : Roo.BoxComponent.prototype.adjustSize,
15948
15949     // private (for BoxComponent)
15950     getResizeEl : function(){
15951         return this.wrap;
15952     },
15953
15954     // private (for BoxComponent)
15955     getPositionEl : function(){
15956         return this.wrap;
15957     },
15958
15959     // private
15960     initEvents : function(){
15961         this.originalValue = this.getValue();
15962     },
15963
15964 //    /**
15965 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
15966 //     * @method
15967 //     */
15968 //    markInvalid : Roo.emptyFn,
15969 //    /**
15970 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
15971 //     * @method
15972 //     */
15973 //    clearInvalid : Roo.emptyFn,
15974
15975     setValue : function(v){
15976         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
15977         this.editorcore.pushValue();
15978     },
15979
15980      
15981     // private
15982     deferFocus : function(){
15983         this.focus.defer(10, this);
15984     },
15985
15986     // doc'ed in Field
15987     focus : function(){
15988         this.editorcore.focus();
15989         
15990     },
15991       
15992
15993     // private
15994     onDestroy : function(){
15995         
15996         
15997         
15998         if(this.rendered){
15999             
16000             for (var i =0; i < this.toolbars.length;i++) {
16001                 // fixme - ask toolbars for heights?
16002                 this.toolbars[i].onDestroy();
16003             }
16004             
16005             this.wrap.dom.innerHTML = '';
16006             this.wrap.remove();
16007         }
16008     },
16009
16010     // private
16011     onFirstFocus : function(){
16012         //Roo.log("onFirstFocus");
16013         this.editorcore.onFirstFocus();
16014          for (var i =0; i < this.toolbars.length;i++) {
16015             this.toolbars[i].onFirstFocus();
16016         }
16017         
16018     },
16019     
16020     // private
16021     syncValue : function()
16022     {   
16023         this.editorcore.syncValue();
16024     },
16025     
16026     pushValue : function()
16027     {   
16028         this.editorcore.pushValue();
16029     }
16030      
16031     
16032     // hide stuff that is not compatible
16033     /**
16034      * @event blur
16035      * @hide
16036      */
16037     /**
16038      * @event change
16039      * @hide
16040      */
16041     /**
16042      * @event focus
16043      * @hide
16044      */
16045     /**
16046      * @event specialkey
16047      * @hide
16048      */
16049     /**
16050      * @cfg {String} fieldClass @hide
16051      */
16052     /**
16053      * @cfg {String} focusClass @hide
16054      */
16055     /**
16056      * @cfg {String} autoCreate @hide
16057      */
16058     /**
16059      * @cfg {String} inputType @hide
16060      */
16061     /**
16062      * @cfg {String} invalidClass @hide
16063      */
16064     /**
16065      * @cfg {String} invalidText @hide
16066      */
16067     /**
16068      * @cfg {String} msgFx @hide
16069      */
16070     /**
16071      * @cfg {String} validateOnBlur @hide
16072      */
16073 });
16074  
16075     
16076    
16077    
16078    
16079       
16080
16081 /**
16082  * @class Roo.bootstrap.HtmlEditorToolbar1
16083  * Basic Toolbar
16084  * 
16085  * Usage:
16086  *
16087  new Roo.bootstrap.HtmlEditor({
16088     ....
16089     toolbars : [
16090         new Roo.bootstrap.HtmlEditorToolbar1({
16091             disable : { fonts: 1 , format: 1, ..., ... , ...],
16092             btns : [ .... ]
16093         })
16094     }
16095      
16096  * 
16097  * @cfg {Object} disable List of elements to disable..
16098  * @cfg {Array} btns List of additional buttons.
16099  * 
16100  * 
16101  * NEEDS Extra CSS? 
16102  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
16103  */
16104  
16105 Roo.bootstrap.HtmlEditor.ToolbarStandard = function(config)
16106 {
16107     
16108     Roo.apply(this, config);
16109     
16110     // default disabled, based on 'good practice'..
16111     this.disable = this.disable || {};
16112     Roo.applyIf(this.disable, {
16113         fontSize : true,
16114         colors : true,
16115         specialElements : true
16116     });
16117     Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.constructor.call(this, config);
16118     
16119     this.editor = config.editor;
16120     this.editorcore = config.editor.editorcore;
16121     
16122     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
16123     
16124     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
16125     // dont call parent... till later.
16126 }
16127 Roo.extend(Roo.bootstrap.HtmlEditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
16128     
16129     
16130     bar : true,
16131     
16132     editor : false,
16133     editorcore : false,
16134     
16135     
16136     formats : [
16137         "p" ,  
16138         "h1","h2","h3","h4","h5","h6", 
16139         "pre", "code", 
16140         "abbr", "acronym", "address", "cite", "samp", "var",
16141         'div','span'
16142     ],
16143     
16144     onRender : function(ct, position)
16145     {
16146        // Roo.log("Call onRender: " + this.xtype);
16147         
16148        Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
16149        Roo.log(this.el);
16150        this.el.dom.style.marginBottom = '0';
16151        var _this = this;
16152        var editorcore = this.editorcore;
16153        var editor= this.editor;
16154        
16155        var children = [];
16156        var btn = function(id,cmd , toggle, handler){
16157        
16158             var  event = toggle ? 'toggle' : 'click';
16159        
16160             var a = {
16161                 size : 'sm',
16162                 xtype: 'Button',
16163                 xns: Roo.bootstrap,
16164                 glyphicon : id,
16165                 cmd : id || cmd,
16166                 enableToggle:toggle !== false,
16167                 //html : 'submit'
16168                 pressed : toggle ? false : null,
16169                 listeners : {}
16170             }
16171             a.listeners[toggle ? 'toggle' : 'click'] = function() {
16172                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
16173             }
16174             children.push(a);
16175             return a;
16176        }
16177         
16178         var style = {
16179                 xtype: 'Button',
16180                 size : 'sm',
16181                 xns: Roo.bootstrap,
16182                 glyphicon : 'font',
16183                 //html : 'submit'
16184                 menu : {
16185                     xtype: 'Menu',
16186                     xns: Roo.bootstrap,
16187                     items:  []
16188                 }
16189         };
16190         Roo.each(this.formats, function(f) {
16191             style.menu.items.push({
16192                 xtype :'MenuItem',
16193                 xns: Roo.bootstrap,
16194                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
16195                 tagname : f,
16196                 listeners : {
16197                     click : function()
16198                     {
16199                         editorcore.insertTag(this.tagname);
16200                         editor.focus();
16201                     }
16202                 }
16203                 
16204             });
16205         });
16206          children.push(style);   
16207             
16208             
16209         btn('bold',false,true);
16210         btn('italic',false,true);
16211         btn('align-left', 'justifyleft',true);
16212         btn('align-center', 'justifycenter',true);
16213         btn('align-right' , 'justifyright',true);
16214         btn('link', false, false, function(btn) {
16215             //Roo.log("create link?");
16216             var url = prompt(this.createLinkText, this.defaultLinkValue);
16217             if(url && url != 'http:/'+'/'){
16218                 this.editorcore.relayCmd('createlink', url);
16219             }
16220         }),
16221         btn('list','insertunorderedlist',true);
16222         btn('pencil', false,true, function(btn){
16223                 Roo.log(this);
16224                 
16225                 this.toggleSourceEdit(btn.pressed);
16226         });
16227         /*
16228         var cog = {
16229                 xtype: 'Button',
16230                 size : 'sm',
16231                 xns: Roo.bootstrap,
16232                 glyphicon : 'cog',
16233                 //html : 'submit'
16234                 menu : {
16235                     xtype: 'Menu',
16236                     xns: Roo.bootstrap,
16237                     items:  []
16238                 }
16239         };
16240         
16241         cog.menu.items.push({
16242             xtype :'MenuItem',
16243             xns: Roo.bootstrap,
16244             html : Clean styles,
16245             tagname : f,
16246             listeners : {
16247                 click : function()
16248                 {
16249                     editorcore.insertTag(this.tagname);
16250                     editor.focus();
16251                 }
16252             }
16253             
16254         });
16255        */
16256         
16257          
16258        this.xtype = 'NavSimplebar';
16259         
16260         for(var i=0;i< children.length;i++) {
16261             
16262             this.buttons.add(this.addxtypeChild(children[i]));
16263             
16264         }
16265         
16266         editor.on('editorevent', this.updateToolbar, this);
16267     },
16268     onBtnClick : function(id)
16269     {
16270        this.editorcore.relayCmd(id);
16271        this.editorcore.focus();
16272     },
16273     
16274     /**
16275      * Protected method that will not generally be called directly. It triggers
16276      * a toolbar update by reading the markup state of the current selection in the editor.
16277      */
16278     updateToolbar: function(){
16279
16280         if(!this.editorcore.activated){
16281             this.editor.onFirstFocus(); // is this neeed?
16282             return;
16283         }
16284
16285         var btns = this.buttons; 
16286         var doc = this.editorcore.doc;
16287         btns.get('bold').setActive(doc.queryCommandState('bold'));
16288         btns.get('italic').setActive(doc.queryCommandState('italic'));
16289         //btns.get('underline').setActive(doc.queryCommandState('underline'));
16290         
16291         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
16292         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
16293         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
16294         
16295         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
16296         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
16297          /*
16298         
16299         var ans = this.editorcore.getAllAncestors();
16300         if (this.formatCombo) {
16301             
16302             
16303             var store = this.formatCombo.store;
16304             this.formatCombo.setValue("");
16305             for (var i =0; i < ans.length;i++) {
16306                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
16307                     // select it..
16308                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
16309                     break;
16310                 }
16311             }
16312         }
16313         
16314         
16315         
16316         // hides menus... - so this cant be on a menu...
16317         Roo.bootstrap.MenuMgr.hideAll();
16318         */
16319         Roo.bootstrap.MenuMgr.hideAll();
16320         //this.editorsyncValue();
16321     },
16322     onFirstFocus: function() {
16323         this.buttons.each(function(item){
16324            item.enable();
16325         });
16326     },
16327     toggleSourceEdit : function(sourceEditMode){
16328         
16329           
16330         if(sourceEditMode){
16331             Roo.log("disabling buttons");
16332            this.buttons.each( function(item){
16333                 if(item.cmd != 'pencil'){
16334                     item.disable();
16335                 }
16336             });
16337           
16338         }else{
16339             Roo.log("enabling buttons");
16340             if(this.editorcore.initialized){
16341                 this.buttons.each( function(item){
16342                     item.enable();
16343                 });
16344             }
16345             
16346         }
16347         Roo.log("calling toggole on editor");
16348         // tell the editor that it's been pressed..
16349         this.editor.toggleSourceEdit(sourceEditMode);
16350        
16351     }
16352 });
16353
16354
16355
16356
16357
16358 /**
16359  * @class Roo.bootstrap.Table.AbstractSelectionModel
16360  * @extends Roo.util.Observable
16361  * Abstract base class for grid SelectionModels.  It provides the interface that should be
16362  * implemented by descendant classes.  This class should not be directly instantiated.
16363  * @constructor
16364  */
16365 Roo.bootstrap.Table.AbstractSelectionModel = function(){
16366     this.locked = false;
16367     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
16368 };
16369
16370
16371 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
16372     /** @ignore Called by the grid automatically. Do not call directly. */
16373     init : function(grid){
16374         this.grid = grid;
16375         this.initEvents();
16376     },
16377
16378     /**
16379      * Locks the selections.
16380      */
16381     lock : function(){
16382         this.locked = true;
16383     },
16384
16385     /**
16386      * Unlocks the selections.
16387      */
16388     unlock : function(){
16389         this.locked = false;
16390     },
16391
16392     /**
16393      * Returns true if the selections are locked.
16394      * @return {Boolean}
16395      */
16396     isLocked : function(){
16397         return this.locked;
16398     }
16399 });
16400 /**
16401  * @class Roo.bootstrap.Table.ColumnModel
16402  * @extends Roo.util.Observable
16403  * This is the default implementation of a ColumnModel used by the bootstrap table. It defines
16404  * the columns in the table.
16405  
16406  * @constructor
16407  * @param {Object} config An Array of column config objects. See this class's
16408  * config objects for details.
16409 */
16410 Roo.bootstrap.Table.ColumnModel = function(config){
16411         /**
16412      * The config passed into the constructor
16413      */
16414     this.config = config;
16415     this.lookup = {};
16416
16417     // if no id, create one
16418     // if the column does not have a dataIndex mapping,
16419     // map it to the order it is in the config
16420     for(var i = 0, len = config.length; i < len; i++){
16421         var c = config[i];
16422         if(typeof c.dataIndex == "undefined"){
16423             c.dataIndex = i;
16424         }
16425         if(typeof c.renderer == "string"){
16426             c.renderer = Roo.util.Format[c.renderer];
16427         }
16428         if(typeof c.id == "undefined"){
16429             c.id = Roo.id();
16430         }
16431 //        if(c.editor && c.editor.xtype){
16432 //            c.editor  = Roo.factory(c.editor, Roo.grid);
16433 //        }
16434 //        if(c.editor && c.editor.isFormField){
16435 //            c.editor = new Roo.grid.GridEditor(c.editor);
16436 //        }
16437
16438         this.lookup[c.id] = c;
16439     }
16440
16441     /**
16442      * The width of columns which have no width specified (defaults to 100)
16443      * @type Number
16444      */
16445     this.defaultWidth = 100;
16446
16447     /**
16448      * Default sortable of columns which have no sortable specified (defaults to false)
16449      * @type Boolean
16450      */
16451     this.defaultSortable = false;
16452
16453     this.addEvents({
16454         /**
16455              * @event widthchange
16456              * Fires when the width of a column changes.
16457              * @param {ColumnModel} this
16458              * @param {Number} columnIndex The column index
16459              * @param {Number} newWidth The new width
16460              */
16461             "widthchange": true,
16462         /**
16463              * @event headerchange
16464              * Fires when the text of a header changes.
16465              * @param {ColumnModel} this
16466              * @param {Number} columnIndex The column index
16467              * @param {Number} newText The new header text
16468              */
16469             "headerchange": true,
16470         /**
16471              * @event hiddenchange
16472              * Fires when a column is hidden or "unhidden".
16473              * @param {ColumnModel} this
16474              * @param {Number} columnIndex The column index
16475              * @param {Boolean} hidden true if hidden, false otherwise
16476              */
16477             "hiddenchange": true,
16478             /**
16479          * @event columnmoved
16480          * Fires when a column is moved.
16481          * @param {ColumnModel} this
16482          * @param {Number} oldIndex
16483          * @param {Number} newIndex
16484          */
16485         "columnmoved" : true,
16486         /**
16487          * @event columlockchange
16488          * Fires when a column's locked state is changed
16489          * @param {ColumnModel} this
16490          * @param {Number} colIndex
16491          * @param {Boolean} locked true if locked
16492          */
16493         "columnlockchange" : true
16494     });
16495     Roo.bootstrap.Table.ColumnModel.superclass.constructor.call(this);
16496 };
16497 Roo.extend(Roo.bootstrap.Table.ColumnModel, Roo.util.Observable, {
16498     /**
16499      * @cfg {String} header The header text to display in the Grid view.
16500      */
16501     /**
16502      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
16503      * {@link Roo.data.Record} definition from which to draw the column's value. If not
16504      * specified, the column's index is used as an index into the Record's data Array.
16505      */
16506     /**
16507      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
16508      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
16509      */
16510     /**
16511      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
16512      * Defaults to the value of the {@link #defaultSortable} property.
16513      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
16514      */
16515     /**
16516      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
16517      */
16518     /**
16519      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
16520      */
16521     /**
16522      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
16523      */
16524     /**
16525      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
16526      */
16527     /**
16528      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
16529      * given the cell's data value. See {@link #setRenderer}. If not specified, the
16530      * default renderer uses the raw data value.
16531      */
16532     /**
16533      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
16534      */
16535
16536     /**
16537      * Returns the id of the column at the specified index.
16538      * @param {Number} index The column index
16539      * @return {String} the id
16540      */
16541     getColumnId : function(index){
16542         return this.config[index].id;
16543     },
16544
16545     /**
16546      * Returns the column for a specified id.
16547      * @param {String} id The column id
16548      * @return {Object} the column
16549      */
16550     getColumnById : function(id){
16551         return this.lookup[id];
16552     },
16553
16554     
16555     /**
16556      * Returns the column for a specified dataIndex.
16557      * @param {String} dataIndex The column dataIndex
16558      * @return {Object|Boolean} the column or false if not found
16559      */
16560     getColumnByDataIndex: function(dataIndex){
16561         var index = this.findColumnIndex(dataIndex);
16562         return index > -1 ? this.config[index] : false;
16563     },
16564     
16565     /**
16566      * Returns the index for a specified column id.
16567      * @param {String} id The column id
16568      * @return {Number} the index, or -1 if not found
16569      */
16570     getIndexById : function(id){
16571         for(var i = 0, len = this.config.length; i < len; i++){
16572             if(this.config[i].id == id){
16573                 return i;
16574             }
16575         }
16576         return -1;
16577     },
16578     
16579     /**
16580      * Returns the index for a specified column dataIndex.
16581      * @param {String} dataIndex The column dataIndex
16582      * @return {Number} the index, or -1 if not found
16583      */
16584     
16585     findColumnIndex : function(dataIndex){
16586         for(var i = 0, len = this.config.length; i < len; i++){
16587             if(this.config[i].dataIndex == dataIndex){
16588                 return i;
16589             }
16590         }
16591         return -1;
16592     },
16593     
16594     
16595     moveColumn : function(oldIndex, newIndex){
16596         var c = this.config[oldIndex];
16597         this.config.splice(oldIndex, 1);
16598         this.config.splice(newIndex, 0, c);
16599         this.dataMap = null;
16600         this.fireEvent("columnmoved", this, oldIndex, newIndex);
16601     },
16602
16603     isLocked : function(colIndex){
16604         return this.config[colIndex].locked === true;
16605     },
16606
16607     setLocked : function(colIndex, value, suppressEvent){
16608         if(this.isLocked(colIndex) == value){
16609             return;
16610         }
16611         this.config[colIndex].locked = value;
16612         if(!suppressEvent){
16613             this.fireEvent("columnlockchange", this, colIndex, value);
16614         }
16615     },
16616
16617     getTotalLockedWidth : function(){
16618         var totalWidth = 0;
16619         for(var i = 0; i < this.config.length; i++){
16620             if(this.isLocked(i) && !this.isHidden(i)){
16621                 this.totalWidth += this.getColumnWidth(i);
16622             }
16623         }
16624         return totalWidth;
16625     },
16626
16627     getLockedCount : function(){
16628         for(var i = 0, len = this.config.length; i < len; i++){
16629             if(!this.isLocked(i)){
16630                 return i;
16631             }
16632         }
16633     },
16634
16635     /**
16636      * Returns the number of columns.
16637      * @return {Number}
16638      */
16639     getColumnCount : function(visibleOnly){
16640         if(visibleOnly === true){
16641             var c = 0;
16642             for(var i = 0, len = this.config.length; i < len; i++){
16643                 if(!this.isHidden(i)){
16644                     c++;
16645                 }
16646             }
16647             return c;
16648         }
16649         return this.config.length;
16650     },
16651
16652     /**
16653      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
16654      * @param {Function} fn
16655      * @param {Object} scope (optional)
16656      * @return {Array} result
16657      */
16658     getColumnsBy : function(fn, scope){
16659         var r = [];
16660         for(var i = 0, len = this.config.length; i < len; i++){
16661             var c = this.config[i];
16662             if(fn.call(scope||this, c, i) === true){
16663                 r[r.length] = c;
16664             }
16665         }
16666         return r;
16667     },
16668
16669     /**
16670      * Returns true if the specified column is sortable.
16671      * @param {Number} col The column index
16672      * @return {Boolean}
16673      */
16674     isSortable : function(col){
16675         if(typeof this.config[col].sortable == "undefined"){
16676             return this.defaultSortable;
16677         }
16678         return this.config[col].sortable;
16679     },
16680
16681     /**
16682      * Returns the rendering (formatting) function defined for the column.
16683      * @param {Number} col The column index.
16684      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
16685      */
16686     getRenderer : function(col){
16687         if(!this.config[col].renderer){
16688             return Roo.bootstrap.Table.ColumnModel.defaultRenderer;
16689         }
16690         return this.config[col].renderer;
16691     },
16692
16693     /**
16694      * Sets the rendering (formatting) function for a column.
16695      * @param {Number} col The column index
16696      * @param {Function} fn The function to use to process the cell's raw data
16697      * to return HTML markup for the grid view. The render function is called with
16698      * the following parameters:<ul>
16699      * <li>Data value.</li>
16700      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
16701      * <li>css A CSS style string to apply to the table cell.</li>
16702      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
16703      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
16704      * <li>Row index</li>
16705      * <li>Column index</li>
16706      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
16707      */
16708     setRenderer : function(col, fn){
16709         this.config[col].renderer = fn;
16710     },
16711
16712     /**
16713      * Returns the width for the specified column.
16714      * @param {Number} col The column index
16715      * @return {Number}
16716      */
16717     getColumnWidth : function(col){
16718         return this.config[col].width * 1 || this.defaultWidth;
16719     },
16720
16721     /**
16722      * Sets the width for a column.
16723      * @param {Number} col The column index
16724      * @param {Number} width The new width
16725      */
16726     setColumnWidth : function(col, width, suppressEvent){
16727         this.config[col].width = width;
16728         this.totalWidth = null;
16729         if(!suppressEvent){
16730              this.fireEvent("widthchange", this, col, width);
16731         }
16732     },
16733
16734     /**
16735      * Returns the total width of all columns.
16736      * @param {Boolean} includeHidden True to include hidden column widths
16737      * @return {Number}
16738      */
16739     getTotalWidth : function(includeHidden){
16740         if(!this.totalWidth){
16741             this.totalWidth = 0;
16742             for(var i = 0, len = this.config.length; i < len; i++){
16743                 if(includeHidden || !this.isHidden(i)){
16744                     this.totalWidth += this.getColumnWidth(i);
16745                 }
16746             }
16747         }
16748         return this.totalWidth;
16749     },
16750
16751     /**
16752      * Returns the header for the specified column.
16753      * @param {Number} col The column index
16754      * @return {String}
16755      */
16756     getColumnHeader : function(col){
16757         return this.config[col].header;
16758     },
16759
16760     /**
16761      * Sets the header for a column.
16762      * @param {Number} col The column index
16763      * @param {String} header The new header
16764      */
16765     setColumnHeader : function(col, header){
16766         this.config[col].header = header;
16767         this.fireEvent("headerchange", this, col, header);
16768     },
16769
16770     /**
16771      * Returns the tooltip for the specified column.
16772      * @param {Number} col The column index
16773      * @return {String}
16774      */
16775     getColumnTooltip : function(col){
16776             return this.config[col].tooltip;
16777     },
16778     /**
16779      * Sets the tooltip for a column.
16780      * @param {Number} col The column index
16781      * @param {String} tooltip The new tooltip
16782      */
16783     setColumnTooltip : function(col, tooltip){
16784             this.config[col].tooltip = tooltip;
16785     },
16786
16787     /**
16788      * Returns the dataIndex for the specified column.
16789      * @param {Number} col The column index
16790      * @return {Number}
16791      */
16792     getDataIndex : function(col){
16793         return this.config[col].dataIndex;
16794     },
16795
16796     /**
16797      * Sets the dataIndex for a column.
16798      * @param {Number} col The column index
16799      * @param {Number} dataIndex The new dataIndex
16800      */
16801     setDataIndex : function(col, dataIndex){
16802         this.config[col].dataIndex = dataIndex;
16803     },
16804
16805     
16806     
16807     /**
16808      * Returns true if the cell is editable.
16809      * @param {Number} colIndex The column index
16810      * @param {Number} rowIndex The row index
16811      * @return {Boolean}
16812      */
16813     isCellEditable : function(colIndex, rowIndex){
16814         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
16815     },
16816
16817     /**
16818      * Returns the editor defined for the cell/column.
16819      * return false or null to disable editing.
16820      * @param {Number} colIndex The column index
16821      * @param {Number} rowIndex The row index
16822      * @return {Object}
16823      */
16824     getCellEditor : function(colIndex, rowIndex){
16825         return this.config[colIndex].editor;
16826     },
16827
16828     /**
16829      * Sets if a column is editable.
16830      * @param {Number} col The column index
16831      * @param {Boolean} editable True if the column is editable
16832      */
16833     setEditable : function(col, editable){
16834         this.config[col].editable = editable;
16835     },
16836
16837
16838     /**
16839      * Returns true if the column is hidden.
16840      * @param {Number} colIndex The column index
16841      * @return {Boolean}
16842      */
16843     isHidden : function(colIndex){
16844         return this.config[colIndex].hidden;
16845     },
16846
16847
16848     /**
16849      * Returns true if the column width cannot be changed
16850      */
16851     isFixed : function(colIndex){
16852         return this.config[colIndex].fixed;
16853     },
16854
16855     /**
16856      * Returns true if the column can be resized
16857      * @return {Boolean}
16858      */
16859     isResizable : function(colIndex){
16860         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
16861     },
16862     /**
16863      * Sets if a column is hidden.
16864      * @param {Number} colIndex The column index
16865      * @param {Boolean} hidden True if the column is hidden
16866      */
16867     setHidden : function(colIndex, hidden){
16868         this.config[colIndex].hidden = hidden;
16869         this.totalWidth = null;
16870         this.fireEvent("hiddenchange", this, colIndex, hidden);
16871     },
16872
16873     /**
16874      * Sets the editor for a column.
16875      * @param {Number} col The column index
16876      * @param {Object} editor The editor object
16877      */
16878     setEditor : function(col, editor){
16879         this.config[col].editor = editor;
16880     }
16881 });
16882
16883 Roo.bootstrap.Table.ColumnModel.defaultRenderer = function(value){
16884         if(typeof value == "string" && value.length < 1){
16885             return "&#160;";
16886         }
16887         return value;
16888 };
16889
16890 // Alias for backwards compatibility
16891 Roo.bootstrap.Table.DefaultColumnModel = Roo.bootstrap.Table.ColumnModel;
16892
16893 /**
16894  * @extends Roo.bootstrap.Table.AbstractSelectionModel
16895  * @class Roo.bootstrap.Table.RowSelectionModel
16896  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
16897  * It supports multiple selections and keyboard selection/navigation. 
16898  * @constructor
16899  * @param {Object} config
16900  */
16901
16902 Roo.bootstrap.Table.RowSelectionModel = function(config){
16903     Roo.apply(this, config);
16904     this.selections = new Roo.util.MixedCollection(false, function(o){
16905         return o.id;
16906     });
16907
16908     this.last = false;
16909     this.lastActive = false;
16910
16911     this.addEvents({
16912         /**
16913              * @event selectionchange
16914              * Fires when the selection changes
16915              * @param {SelectionModel} this
16916              */
16917             "selectionchange" : true,
16918         /**
16919              * @event afterselectionchange
16920              * Fires after the selection changes (eg. by key press or clicking)
16921              * @param {SelectionModel} this
16922              */
16923             "afterselectionchange" : true,
16924         /**
16925              * @event beforerowselect
16926              * Fires when a row is selected being selected, return false to cancel.
16927              * @param {SelectionModel} this
16928              * @param {Number} rowIndex The selected index
16929              * @param {Boolean} keepExisting False if other selections will be cleared
16930              */
16931             "beforerowselect" : true,
16932         /**
16933              * @event rowselect
16934              * Fires when a row is selected.
16935              * @param {SelectionModel} this
16936              * @param {Number} rowIndex The selected index
16937              * @param {Roo.data.Record} r The record
16938              */
16939             "rowselect" : true,
16940         /**
16941              * @event rowdeselect
16942              * Fires when a row is deselected.
16943              * @param {SelectionModel} this
16944              * @param {Number} rowIndex The selected index
16945              */
16946         "rowdeselect" : true
16947     });
16948     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
16949     this.locked = false;
16950 };
16951
16952 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
16953     /**
16954      * @cfg {Boolean} singleSelect
16955      * True to allow selection of only one row at a time (defaults to false)
16956      */
16957     singleSelect : false,
16958
16959     // private
16960     initEvents : function(){
16961
16962         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
16963             this.grid.on("mousedown", this.handleMouseDown, this);
16964         }else{ // allow click to work like normal
16965             this.grid.on("rowclick", this.handleDragableRowClick, this);
16966         }
16967
16968         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
16969             "up" : function(e){
16970                 if(!e.shiftKey){
16971                     this.selectPrevious(e.shiftKey);
16972                 }else if(this.last !== false && this.lastActive !== false){
16973                     var last = this.last;
16974                     this.selectRange(this.last,  this.lastActive-1);
16975                     this.grid.getView().focusRow(this.lastActive);
16976                     if(last !== false){
16977                         this.last = last;
16978                     }
16979                 }else{
16980                     this.selectFirstRow();
16981                 }
16982                 this.fireEvent("afterselectionchange", this);
16983             },
16984             "down" : function(e){
16985                 if(!e.shiftKey){
16986                     this.selectNext(e.shiftKey);
16987                 }else if(this.last !== false && this.lastActive !== false){
16988                     var last = this.last;
16989                     this.selectRange(this.last,  this.lastActive+1);
16990                     this.grid.getView().focusRow(this.lastActive);
16991                     if(last !== false){
16992                         this.last = last;
16993                     }
16994                 }else{
16995                     this.selectFirstRow();
16996                 }
16997                 this.fireEvent("afterselectionchange", this);
16998             },
16999             scope: this
17000         });
17001
17002         var view = this.grid.view;
17003         view.on("refresh", this.onRefresh, this);
17004         view.on("rowupdated", this.onRowUpdated, this);
17005         view.on("rowremoved", this.onRemove, this);
17006     },
17007
17008     // private
17009     onRefresh : function(){
17010         var ds = this.grid.dataSource, i, v = this.grid.view;
17011         var s = this.selections;
17012         s.each(function(r){
17013             if((i = ds.indexOfId(r.id)) != -1){
17014                 v.onRowSelect(i);
17015             }else{
17016                 s.remove(r);
17017             }
17018         });
17019     },
17020
17021     // private
17022     onRemove : function(v, index, r){
17023         this.selections.remove(r);
17024     },
17025
17026     // private
17027     onRowUpdated : function(v, index, r){
17028         if(this.isSelected(r)){
17029             v.onRowSelect(index);
17030         }
17031     },
17032
17033     /**
17034      * Select records.
17035      * @param {Array} records The records to select
17036      * @param {Boolean} keepExisting (optional) True to keep existing selections
17037      */
17038     selectRecords : function(records, keepExisting){
17039         if(!keepExisting){
17040             this.clearSelections();
17041         }
17042         var ds = this.grid.dataSource;
17043         for(var i = 0, len = records.length; i < len; i++){
17044             this.selectRow(ds.indexOf(records[i]), true);
17045         }
17046     },
17047
17048     /**
17049      * Gets the number of selected rows.
17050      * @return {Number}
17051      */
17052     getCount : function(){
17053         return this.selections.length;
17054     },
17055
17056     /**
17057      * Selects the first row in the grid.
17058      */
17059     selectFirstRow : function(){
17060         this.selectRow(0);
17061     },
17062
17063     /**
17064      * Select the last row.
17065      * @param {Boolean} keepExisting (optional) True to keep existing selections
17066      */
17067     selectLastRow : function(keepExisting){
17068         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
17069     },
17070
17071     /**
17072      * Selects the row immediately following the last selected row.
17073      * @param {Boolean} keepExisting (optional) True to keep existing selections
17074      */
17075     selectNext : function(keepExisting){
17076         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
17077             this.selectRow(this.last+1, keepExisting);
17078             this.grid.getView().focusRow(this.last);
17079         }
17080     },
17081
17082     /**
17083      * Selects the row that precedes the last selected row.
17084      * @param {Boolean} keepExisting (optional) True to keep existing selections
17085      */
17086     selectPrevious : function(keepExisting){
17087         if(this.last){
17088             this.selectRow(this.last-1, keepExisting);
17089             this.grid.getView().focusRow(this.last);
17090         }
17091     },
17092
17093     /**
17094      * Returns the selected records
17095      * @return {Array} Array of selected records
17096      */
17097     getSelections : function(){
17098         return [].concat(this.selections.items);
17099     },
17100
17101     /**
17102      * Returns the first selected record.
17103      * @return {Record}
17104      */
17105     getSelected : function(){
17106         return this.selections.itemAt(0);
17107     },
17108
17109
17110     /**
17111      * Clears all selections.
17112      */
17113     clearSelections : function(fast){
17114         if(this.locked) return;
17115         if(fast !== true){
17116             var ds = this.grid.dataSource;
17117             var s = this.selections;
17118             s.each(function(r){
17119                 this.deselectRow(ds.indexOfId(r.id));
17120             }, this);
17121             s.clear();
17122         }else{
17123             this.selections.clear();
17124         }
17125         this.last = false;
17126     },
17127
17128
17129     /**
17130      * Selects all rows.
17131      */
17132     selectAll : function(){
17133         if(this.locked) return;
17134         this.selections.clear();
17135         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
17136             this.selectRow(i, true);
17137         }
17138     },
17139
17140     /**
17141      * Returns True if there is a selection.
17142      * @return {Boolean}
17143      */
17144     hasSelection : function(){
17145         return this.selections.length > 0;
17146     },
17147
17148     /**
17149      * Returns True if the specified row is selected.
17150      * @param {Number/Record} record The record or index of the record to check
17151      * @return {Boolean}
17152      */
17153     isSelected : function(index){
17154         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
17155         return (r && this.selections.key(r.id) ? true : false);
17156     },
17157
17158     /**
17159      * Returns True if the specified record id is selected.
17160      * @param {String} id The id of record to check
17161      * @return {Boolean}
17162      */
17163     isIdSelected : function(id){
17164         return (this.selections.key(id) ? true : false);
17165     },
17166
17167     // private
17168     handleMouseDown : function(e, t){
17169         var view = this.grid.getView(), rowIndex;
17170         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
17171             return;
17172         };
17173         if(e.shiftKey && this.last !== false){
17174             var last = this.last;
17175             this.selectRange(last, rowIndex, e.ctrlKey);
17176             this.last = last; // reset the last
17177             view.focusRow(rowIndex);
17178         }else{
17179             var isSelected = this.isSelected(rowIndex);
17180             if(e.button !== 0 && isSelected){
17181                 view.focusRow(rowIndex);
17182             }else if(e.ctrlKey && isSelected){
17183                 this.deselectRow(rowIndex);
17184             }else if(!isSelected){
17185                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
17186                 view.focusRow(rowIndex);
17187             }
17188         }
17189         this.fireEvent("afterselectionchange", this);
17190     },
17191     // private
17192     handleDragableRowClick :  function(grid, rowIndex, e) 
17193     {
17194         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
17195             this.selectRow(rowIndex, false);
17196             grid.view.focusRow(rowIndex);
17197              this.fireEvent("afterselectionchange", this);
17198         }
17199     },
17200     
17201     /**
17202      * Selects multiple rows.
17203      * @param {Array} rows Array of the indexes of the row to select
17204      * @param {Boolean} keepExisting (optional) True to keep existing selections
17205      */
17206     selectRows : function(rows, keepExisting){
17207         if(!keepExisting){
17208             this.clearSelections();
17209         }
17210         for(var i = 0, len = rows.length; i < len; i++){
17211             this.selectRow(rows[i], true);
17212         }
17213     },
17214
17215     /**
17216      * Selects a range of rows. All rows in between startRow and endRow are also selected.
17217      * @param {Number} startRow The index of the first row in the range
17218      * @param {Number} endRow The index of the last row in the range
17219      * @param {Boolean} keepExisting (optional) True to retain existing selections
17220      */
17221     selectRange : function(startRow, endRow, keepExisting){
17222         if(this.locked) return;
17223         if(!keepExisting){
17224             this.clearSelections();
17225         }
17226         if(startRow <= endRow){
17227             for(var i = startRow; i <= endRow; i++){
17228                 this.selectRow(i, true);
17229             }
17230         }else{
17231             for(var i = startRow; i >= endRow; i--){
17232                 this.selectRow(i, true);
17233             }
17234         }
17235     },
17236
17237     /**
17238      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
17239      * @param {Number} startRow The index of the first row in the range
17240      * @param {Number} endRow The index of the last row in the range
17241      */
17242     deselectRange : function(startRow, endRow, preventViewNotify){
17243         if(this.locked) return;
17244         for(var i = startRow; i <= endRow; i++){
17245             this.deselectRow(i, preventViewNotify);
17246         }
17247     },
17248
17249     /**
17250      * Selects a row.
17251      * @param {Number} row The index of the row to select
17252      * @param {Boolean} keepExisting (optional) True to keep existing selections
17253      */
17254     selectRow : function(index, keepExisting, preventViewNotify){
17255         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
17256         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
17257             if(!keepExisting || this.singleSelect){
17258                 this.clearSelections();
17259             }
17260             var r = this.grid.dataSource.getAt(index);
17261             this.selections.add(r);
17262             this.last = this.lastActive = index;
17263             if(!preventViewNotify){
17264                 this.grid.getView().onRowSelect(index);
17265             }
17266             this.fireEvent("rowselect", this, index, r);
17267             this.fireEvent("selectionchange", this);
17268         }
17269     },
17270
17271     /**
17272      * Deselects a row.
17273      * @param {Number} row The index of the row to deselect
17274      */
17275     deselectRow : function(index, preventViewNotify){
17276         if(this.locked) return;
17277         if(this.last == index){
17278             this.last = false;
17279         }
17280         if(this.lastActive == index){
17281             this.lastActive = false;
17282         }
17283         var r = this.grid.dataSource.getAt(index);
17284         this.selections.remove(r);
17285         if(!preventViewNotify){
17286             this.grid.getView().onRowDeselect(index);
17287         }
17288         this.fireEvent("rowdeselect", this, index);
17289         this.fireEvent("selectionchange", this);
17290     },
17291
17292     // private
17293     restoreLast : function(){
17294         if(this._last){
17295             this.last = this._last;
17296         }
17297     },
17298
17299     // private
17300     acceptsNav : function(row, col, cm){
17301         return !cm.isHidden(col) && cm.isCellEditable(col, row);
17302     },
17303
17304     // private
17305     onEditorKey : function(field, e){
17306         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
17307         if(k == e.TAB){
17308             e.stopEvent();
17309             ed.completeEdit();
17310             if(e.shiftKey){
17311                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
17312             }else{
17313                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
17314             }
17315         }else if(k == e.ENTER && !e.ctrlKey){
17316             e.stopEvent();
17317             ed.completeEdit();
17318             if(e.shiftKey){
17319                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
17320             }else{
17321                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
17322             }
17323         }else if(k == e.ESC){
17324             ed.cancelEdit();
17325         }
17326         if(newCell){
17327             g.startEditing(newCell[0], newCell[1]);
17328         }
17329     }
17330 });/*
17331  * - LGPL
17332  *
17333  * element
17334  * 
17335  */
17336
17337 /**
17338  * @class Roo.bootstrap.MessageBar
17339  * @extends Roo.bootstrap.Component
17340  * Bootstrap MessageBar class
17341  * @cfg {String} html contents of the MessageBar
17342  * @cfg {String} weight (info | success | warning | danger) default info
17343  * @cfg {String} beforeClass insert the bar before the given class
17344  * @cfg {Boolean} closable (true | false) default false
17345  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
17346  * 
17347  * @constructor
17348  * Create a new Element
17349  * @param {Object} config The config object
17350  */
17351
17352 Roo.bootstrap.MessageBar = function(config){
17353     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
17354 };
17355
17356 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
17357     
17358     html: '',
17359     weight: 'info',
17360     closable: false,
17361     fixed: false,
17362     beforeClass: 'bootstrap-sticky-wrap',
17363     
17364     getAutoCreate : function(){
17365         
17366         var cfg = {
17367             tag: 'div',
17368             cls: 'alert alert-dismissable alert-' + this.weight,
17369             cn: [
17370                 {
17371                     tag: 'span',
17372                     cls: 'message',
17373                     html: this.html || ''
17374                 }
17375             ]
17376         }
17377         
17378         if(this.fixed){
17379             cfg.cls += ' alert-messages-fixed';
17380         }
17381         
17382         if(this.closable){
17383             cfg.cn.push({
17384                 tag: 'button',
17385                 cls: 'close',
17386                 html: 'x'
17387             });
17388         }
17389         
17390         return cfg;
17391     },
17392     
17393     onRender : function(ct, position)
17394     {
17395         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17396         
17397         if(!this.el){
17398             var cfg = Roo.apply({},  this.getAutoCreate());
17399             cfg.id = Roo.id();
17400             
17401             if (this.cls) {
17402                 cfg.cls += ' ' + this.cls;
17403             }
17404             if (this.style) {
17405                 cfg.style = this.style;
17406             }
17407             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
17408             
17409             this.el.setVisibilityMode(Roo.Element.DISPLAY);
17410         }
17411         
17412         this.el.select('>button.close').on('click', this.hide, this);
17413         
17414     },
17415     
17416     show : function()
17417     {
17418         if (!this.rendered) {
17419             this.render();
17420         }
17421         
17422         this.el.show();
17423         
17424         this.fireEvent('show', this);
17425         
17426     },
17427     
17428     hide : function()
17429     {
17430         if (!this.rendered) {
17431             this.render();
17432         }
17433         
17434         this.el.hide();
17435         
17436         this.fireEvent('hide', this);
17437     },
17438     
17439     update : function()
17440     {
17441 //        var e = this.el.dom.firstChild;
17442 //        
17443 //        if(this.closable){
17444 //            e = e.nextSibling;
17445 //        }
17446 //        
17447 //        e.data = this.html || '';
17448
17449         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
17450     }
17451    
17452 });
17453
17454  
17455
17456